devtronic 1.2.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +3 -3
  2. package/dist/{chunk-V4QEAL7Y.js → chunk-YBT2XOOQ.js} +38 -38
  3. package/dist/index.js +352 -120
  4. package/dist/{plugin-SGSFVXPA.js → plugin-XSNKI5VI.js} +5 -1
  5. package/package.json +1 -1
  6. package/templates/addons/auto-devtronic/agents/afk-task-validator.md +78 -0
  7. package/templates/addons/auto-devtronic/agents/{quality-runner.md → quality-executor.md} +1 -1
  8. package/templates/addons/auto-devtronic/manifest.json +2 -1
  9. package/templates/addons/auto-devtronic/skills/auto-devtronic/SKILL.md +14 -14
  10. package/templates/addons/design-best-practices/manifest.json +2 -2
  11. package/templates/addons/design-best-practices/skills/{design-review → design-critique}/SKILL.md +2 -1
  12. package/templates/addons/design-best-practices/skills/design-harden/SKILL.md +1 -0
  13. package/templates/addons/design-best-practices/skills/design-init/SKILL.md +1 -0
  14. package/templates/addons/design-best-practices/skills/design-refine/SKILL.md +2 -0
  15. package/templates/addons/design-best-practices/skills/{design-system → design-tokens}/SKILL.md +3 -1
  16. package/templates/claude-code/.claude/rules/quality.md +4 -0
  17. package/templates/claude-code/.claude/skills/audit/SKILL.md +1 -1
  18. package/templates/claude-code/.claude/skills/backlog/SKILL.md +1 -1
  19. package/templates/claude-code/.claude/skills/brief/SKILL.md +1 -1
  20. package/templates/claude-code/.claude/skills/briefing/SKILL.md +1 -1
  21. package/templates/claude-code/.claude/skills/checkpoint/SKILL.md +1 -1
  22. package/templates/claude-code/.claude/skills/create-plan/SKILL.md +1 -1
  23. package/templates/claude-code/.claude/skills/create-skill/SKILL.md +1 -1
  24. package/templates/claude-code/.claude/skills/design/SKILL.md +1 -1
  25. package/templates/claude-code/.claude/skills/design-audit/SKILL.md +1 -1
  26. package/templates/claude-code/.claude/skills/design-define/SKILL.md +1 -1
  27. package/templates/claude-code/.claude/skills/design-ia/SKILL.md +1 -1
  28. package/templates/claude-code/.claude/skills/design-research/SKILL.md +1 -1
  29. package/templates/claude-code/.claude/skills/design-review/SKILL.md +1 -1
  30. package/templates/claude-code/.claude/skills/design-spec/SKILL.md +1 -1
  31. package/templates/claude-code/.claude/skills/design-system/SKILL.md +1 -1
  32. package/templates/claude-code/.claude/skills/design-system-audit/SKILL.md +1 -1
  33. package/templates/claude-code/.claude/skills/design-system-define/SKILL.md +1 -1
  34. package/templates/claude-code/.claude/skills/design-system-sync/SKILL.md +1 -1
  35. package/templates/claude-code/.claude/skills/design-wireframe/SKILL.md +1 -1
  36. package/templates/claude-code/.claude/skills/devtronic-help/SKILL.md +338 -0
  37. package/templates/claude-code/.claude/skills/execute-plan/SKILL.md +1 -1
  38. package/templates/claude-code/.claude/skills/generate-tests/SKILL.md +1 -1
  39. package/templates/claude-code/.claude/skills/handoff/SKILL.md +1 -1
  40. package/templates/claude-code/.claude/skills/investigate/SKILL.md +1 -1
  41. package/templates/claude-code/.claude/skills/learn/SKILL.md +1 -1
  42. package/templates/claude-code/.claude/skills/opensrc/SKILL.md +1 -1
  43. package/templates/claude-code/.claude/skills/post-review/SKILL.md +2 -2
  44. package/templates/claude-code/.claude/skills/quick/SKILL.md +1 -1
  45. package/templates/claude-code/.claude/skills/recap/SKILL.md +1 -1
  46. package/templates/claude-code/.claude/skills/research/SKILL.md +1 -1
  47. package/templates/claude-code/.claude/skills/scaffold/SKILL.md +1 -1
  48. package/templates/claude-code/.claude/skills/setup/SKILL.md +1 -1
  49. package/templates/claude-code/.claude/skills/spec/SKILL.md +1 -1
  50. package/templates/claude-code/.claude/skills/summary/SKILL.md +1 -1
  51. package/templates/claude-code/.claude/skills/worktree/SKILL.md +1 -1
  52. package/templates/marketplace/auto-lint.sh +26 -0
  53. package/templates/marketplace/checkpoint.sh +39 -0
  54. package/templates/marketplace/hooks.json +75 -0
  55. package/templates/marketplace/stop-guard.sh +54 -0
package/dist/index.js CHANGED
@@ -4,6 +4,8 @@ import {
4
4
  BASE_AGENT_COUNT,
5
5
  BASE_SKILL_COUNT,
6
6
  DESIGN_SKILL_COUNT,
7
+ GITHUB_MARKETPLACE_NAME,
8
+ GITHUB_MARKETPLACE_REPO,
7
9
  MANIFEST_DIR,
8
10
  MARKETPLACE_NAME,
9
11
  PLUGIN_DIR,
@@ -24,7 +26,7 @@ import {
24
26
  readManifest,
25
27
  writeFile,
26
28
  writeManifest
27
- } from "./chunk-V4QEAL7Y.js";
29
+ } from "./chunk-YBT2XOOQ.js";
28
30
 
29
31
  // src/index.ts
30
32
  import { Command } from "commander";
@@ -32,7 +34,7 @@ import * as p16 from "@clack/prompts";
32
34
  import chalk17 from "chalk";
33
35
 
34
36
  // src/commands/init.ts
35
- import { existsSync as existsSync5, chmodSync } from "fs";
37
+ import { existsSync as existsSync5 } from "fs";
36
38
  import { resolve as resolve2, join as join7, dirname as dirname2 } from "path";
37
39
  import { fileURLToPath as fileURLToPath2 } from "url";
38
40
  import * as p3 from "@clack/prompts";
@@ -1127,8 +1129,22 @@ function writeClaudeSettings(targetDir, settings) {
1127
1129
  ensureDir(join6(targetDir, ".claude"));
1128
1130
  writeFile(settingsPath, JSON.stringify(settings, null, 2));
1129
1131
  }
1132
+ var LEGACY_PLUGIN_NAMES = ["dev-ai", "ai-agentic"];
1133
+ var LEGACY_MARKETPLACE_NAMES = ["dev-ai-local", "ai-agentic-local", "devtronic-local"];
1130
1134
  function registerPlugin(targetDir, pluginName, marketplaceName, marketplacePath) {
1131
1135
  const settings = readClaudeSettings(targetDir);
1136
+ if (settings.extraKnownMarketplaces) {
1137
+ for (const legacy of LEGACY_MARKETPLACE_NAMES) {
1138
+ delete settings.extraKnownMarketplaces[legacy];
1139
+ }
1140
+ }
1141
+ if (settings.enabledPlugins) {
1142
+ for (const key of Object.keys(settings.enabledPlugins)) {
1143
+ if (LEGACY_PLUGIN_NAMES.some((lp) => key.startsWith(`${lp}@`))) {
1144
+ delete settings.enabledPlugins[key];
1145
+ }
1146
+ }
1147
+ }
1132
1148
  if (!settings.extraKnownMarketplaces) {
1133
1149
  settings.extraKnownMarketplaces = {};
1134
1150
  }
@@ -1146,6 +1162,38 @@ function registerPlugin(targetDir, pluginName, marketplaceName, marketplacePath)
1146
1162
  }
1147
1163
  writeClaudeSettings(targetDir, settings);
1148
1164
  }
1165
+ function registerGitHubPlugin(targetDir, pluginName, marketplaceName, githubRepo) {
1166
+ const settings = readClaudeSettings(targetDir);
1167
+ if (settings.extraKnownMarketplaces) {
1168
+ for (const legacy of LEGACY_MARKETPLACE_NAMES) {
1169
+ delete settings.extraKnownMarketplaces[legacy];
1170
+ }
1171
+ }
1172
+ if (settings.enabledPlugins) {
1173
+ for (const key of Object.keys(settings.enabledPlugins)) {
1174
+ if (LEGACY_PLUGIN_NAMES.some((lp) => key.startsWith(`${lp}@`))) {
1175
+ delete settings.enabledPlugins[key];
1176
+ }
1177
+ if (key === `${pluginName}@devtronic-local`) {
1178
+ delete settings.enabledPlugins[key];
1179
+ }
1180
+ }
1181
+ }
1182
+ if (!settings.extraKnownMarketplaces) {
1183
+ settings.extraKnownMarketplaces = {};
1184
+ }
1185
+ settings.extraKnownMarketplaces[marketplaceName] = {
1186
+ source: { source: "github", repo: githubRepo }
1187
+ };
1188
+ if (!settings.enabledPlugins) {
1189
+ settings.enabledPlugins = {};
1190
+ }
1191
+ const pluginKey = `${pluginName}@${marketplaceName}`;
1192
+ if (settings.enabledPlugins[pluginKey] === void 0) {
1193
+ settings.enabledPlugins[pluginKey] = true;
1194
+ }
1195
+ writeClaudeSettings(targetDir, settings);
1196
+ }
1149
1197
  function unregisterPlugin(targetDir, pluginName, marketplaceName) {
1150
1198
  const settings = readClaudeSettings(targetDir);
1151
1199
  const pluginKey = `${pluginName}@${marketplaceName}`;
@@ -1440,27 +1488,17 @@ Valid addons: ${validAddons.join(", ")}`);
1440
1488
  const generatedFiles = [];
1441
1489
  const usePluginMode = selectedIDEs.includes("claude-code");
1442
1490
  if (usePluginMode) {
1443
- const pluginResult = generatePlugin(
1491
+ registerGitHubPlugin(
1444
1492
  targetDir,
1445
- TEMPLATES_DIR,
1446
- getCliVersion(),
1447
- projectConfig,
1448
- analysis.packageManager
1493
+ PLUGIN_NAME,
1494
+ GITHUB_MARKETPLACE_NAME,
1495
+ GITHUB_MARKETPLACE_REPO
1449
1496
  );
1450
- for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
1451
- const scriptPath = join7(targetDir, pluginResult.pluginPath, "scripts", script);
1452
- if (existsSync5(scriptPath)) {
1453
- chmodSync(scriptPath, 493);
1454
- }
1455
- }
1456
- registerPlugin(targetDir, PLUGIN_NAME, MARKETPLACE_NAME, PLUGIN_DIR);
1457
- Object.assign(manifest.files, pluginResult.files);
1458
- manifest.installMode = "plugin";
1459
- manifest.pluginPath = pluginResult.pluginPath;
1497
+ manifest.installMode = "marketplace";
1460
1498
  const addonSkillCount = (projectConfig.enabledAddons || []).reduce((sum, a) => sum + (ADDONS[a]?.skills.length ?? 0), 0);
1461
1499
  const baseTotal = BASE_SKILL_COUNT + DESIGN_SKILL_COUNT;
1462
1500
  const skillLabel = addonSkillCount > 0 ? `${baseTotal} + ${addonSkillCount} addon skills` : `${baseTotal} skills`;
1463
- generatedFiles.push(`Plugin ${PLUGIN_NAME} (${skillLabel}, ${BASE_AGENT_COUNT} agents, 5 hooks)`);
1501
+ generatedFiles.push(`Plugin ${PLUGIN_NAME} (${skillLabel}, ${BASE_AGENT_COUNT} agents) via GitHub marketplace`);
1464
1502
  }
1465
1503
  for (const ide of selectedIDEs) {
1466
1504
  const templateName = IDE_TEMPLATE_MAP[ide];
@@ -1587,11 +1625,14 @@ Valid addons: ${validAddons.join(", ")}`);
1587
1625
  if (usePluginMode) {
1588
1626
  p3.note(
1589
1627
  [
1590
- ` Name: ${chalk4.cyan(PLUGIN_NAME)} at ${PLUGIN_DIR}/${PLUGIN_NAME}/`,
1591
- ` Skills: /devtronic:brief, /devtronic:spec, /devtronic:research, ...`,
1592
- ` Hooks: SessionStart, PostToolUse, Stop, SubagentStop, PreCompact`
1628
+ ` Marketplace: ${chalk4.cyan(GITHUB_MARKETPLACE_REPO)}`,
1629
+ ` Plugin: ${chalk4.cyan(PLUGIN_NAME)}`,
1630
+ ` Skills: /devtronic:brief, /devtronic:spec, ... (auto-namespaced)`,
1631
+ ` Hooks: SessionStart, PostToolUse, Stop, SubagentStop, PreCompact`,
1632
+ ``,
1633
+ ` ${chalk4.dim("Restart Claude Code or run /reload-plugins to activate.")}`
1593
1634
  ].join("\n"),
1594
- "Plugin Installed"
1635
+ "GitHub Marketplace Registered"
1595
1636
  );
1596
1637
  }
1597
1638
  p3.note(
@@ -1615,6 +1656,13 @@ Valid addons: ${validAddons.join(", ")}`);
1615
1656
  ].join("\n"),
1616
1657
  "Autonomous Mode"
1617
1658
  );
1659
+ p3.note(
1660
+ [
1661
+ `${chalk4.dim("In Claude Code:")} ${chalk4.cyan("/devtronic-help")} ${chalk4.dim("\u2014 discover skills, agents, and workflows")}`,
1662
+ `${chalk4.dim("From terminal:")} ${chalk4.cyan("npx devtronic list")} ${chalk4.dim("\u2014 list installed skills and agents")}`
1663
+ ].join("\n"),
1664
+ "Need Help?"
1665
+ );
1618
1666
  p3.outro(chalk4.green("Setup complete!"));
1619
1667
  } catch (err) {
1620
1668
  spinner8.stop("Configuration failed");
@@ -1718,7 +1766,7 @@ function buildProjectConfigFromPreset(presetConfig, analysis) {
1718
1766
 
1719
1767
  // src/commands/update.ts
1720
1768
  import { resolve as resolve4, join as join11, dirname as dirname6 } from "path";
1721
- import { existsSync as existsSync9, unlinkSync as unlinkSync2, lstatSync, readdirSync as readdirSync2, rmdirSync as rmdirSync2, chmodSync as chmodSync2 } from "fs";
1769
+ import { existsSync as existsSync9, unlinkSync as unlinkSync2, lstatSync as lstatSync2, readdirSync as readdirSync2, rmdirSync as rmdirSync2, rmSync as rmSync2, chmodSync } from "fs";
1722
1770
  import * as p4 from "@clack/prompts";
1723
1771
  import chalk5 from "chalk";
1724
1772
 
@@ -1861,6 +1909,7 @@ function registerAddonInConfig(targetDir, addonName) {
1861
1909
  // src/generators/addonFiles.ts
1862
1910
  import {
1863
1911
  existsSync as existsSync8,
1912
+ lstatSync,
1864
1913
  readFileSync as readFileSync4,
1865
1914
  writeFileSync as writeFileSync2,
1866
1915
  mkdirSync as mkdirSync2,
@@ -1870,7 +1919,6 @@ import {
1870
1919
  rmdirSync
1871
1920
  } from "fs";
1872
1921
  import { join as join10, dirname as dirname5 } from "path";
1873
- import { createHash } from "crypto";
1874
1922
  var AGENT_PATHS = {
1875
1923
  claude: ".claude",
1876
1924
  cursor: ".cursor",
@@ -1922,11 +1970,14 @@ var RUNTIME_SPECS = {
1922
1970
  })
1923
1971
  }
1924
1972
  };
1925
- function checksum(content) {
1926
- return createHash("sha256").update(content).digest("hex").slice(0, 16);
1927
- }
1928
1973
  function ensureDir2(dir) {
1929
- if (!existsSync8(dir)) mkdirSync2(dir, { recursive: true });
1974
+ if (!existsSync8(dir)) {
1975
+ try {
1976
+ if (lstatSync(dir).isSymbolicLink()) unlinkSync(dir);
1977
+ } catch {
1978
+ }
1979
+ mkdirSync2(dir, { recursive: true });
1980
+ }
1930
1981
  }
1931
1982
  function readManifest2(addonSourceDir) {
1932
1983
  const manifestPath = join10(addonSourceDir, "manifest.json");
@@ -1980,7 +2031,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
1980
2031
  ensureDir2(dirname5(destPath));
1981
2032
  writeFileSync2(destPath, content);
1982
2033
  result.written++;
1983
- result.checksums[relPath] = checksum(content);
2034
+ result.checksums[relPath] = calculateChecksum(content);
1984
2035
  }
1985
2036
  continue;
1986
2037
  }
@@ -2002,7 +2053,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
2002
2053
  ensureDir2(dirname5(destPath));
2003
2054
  writeFileSync2(destPath, content);
2004
2055
  result.written++;
2005
- result.checksums[relPath] = checksum(content);
2056
+ result.checksums[relPath] = calculateChecksum(content);
2006
2057
  }
2007
2058
  for (const agentName of manifest.files.agents ?? []) {
2008
2059
  const agentFile = join10(addonSourceDir, "agents", `${agentName}.md`);
@@ -2013,7 +2064,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
2013
2064
  ensureDir2(dirname5(destPath));
2014
2065
  writeFileSync2(destPath, content);
2015
2066
  result.written++;
2016
- result.checksums[`agents/${agentName}.md`] = checksum(content);
2067
+ result.checksums[`agents/${agentName}.md`] = calculateChecksum(content);
2017
2068
  } else {
2018
2069
  result.skipped++;
2019
2070
  }
@@ -2028,7 +2079,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
2028
2079
  ensureDir2(dirname5(destPath));
2029
2080
  writeFileSync2(destPath, content);
2030
2081
  result.written++;
2031
- result.checksums[`rules/${rule}`] = checksum(content);
2082
+ result.checksums[`rules/${rule}`] = calculateChecksum(content);
2032
2083
  } else {
2033
2084
  result.skipped++;
2034
2085
  }
@@ -2044,7 +2095,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
2044
2095
  ensureDir2(dirname5(destPath));
2045
2096
  writeFileSync2(destPath, content);
2046
2097
  result.written++;
2047
- result.checksums[relPath] = checksum(content);
2098
+ result.checksums[relPath] = calculateChecksum(content);
2048
2099
  } else {
2049
2100
  result.skipped++;
2050
2101
  }
@@ -2156,7 +2207,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
2156
2207
  continue;
2157
2208
  }
2158
2209
  const existing = readFileSync4(destPath, "utf-8");
2159
- const existingChecksum = checksum(existing);
2210
+ const existingChecksum = calculateChecksum(existing);
2160
2211
  const originalChecksum = installedChecksums[relPath];
2161
2212
  if (existing === newContent) {
2162
2213
  result.skipped++;
@@ -2184,7 +2235,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
2184
2235
  continue;
2185
2236
  }
2186
2237
  const existing = readFileSync4(destPath, "utf-8");
2187
- const existingChecksum = checksum(existing);
2238
+ const existingChecksum = calculateChecksum(existing);
2188
2239
  const originalChecksum = installedChecksums[relPath];
2189
2240
  if (existing === newContent) {
2190
2241
  result.skipped++;
@@ -2210,7 +2261,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
2210
2261
  continue;
2211
2262
  }
2212
2263
  const existing = readFileSync4(destPath, "utf-8");
2213
- const existingChecksum = checksum(existing);
2264
+ const existingChecksum = calculateChecksum(existing);
2214
2265
  const originalChecksum = installedChecksums[relPath];
2215
2266
  if (existing === newContent) {
2216
2267
  result.skipped++;
@@ -2237,7 +2288,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
2237
2288
  continue;
2238
2289
  }
2239
2290
  const existing = readFileSync4(destPath, "utf-8");
2240
- const existingChecksum = checksum(existing);
2291
+ const existingChecksum = calculateChecksum(existing);
2241
2292
  const originalChecksum = installedChecksums[relPath];
2242
2293
  if (existing === newContent) {
2243
2294
  result.skipped++;
@@ -2264,7 +2315,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
2264
2315
  continue;
2265
2316
  }
2266
2317
  const existing = readFileSync4(destPath, "utf-8");
2267
- const existingChecksum = checksum(existing);
2318
+ const existingChecksum = calculateChecksum(existing);
2268
2319
  const originalChecksum = installedChecksums[relPath];
2269
2320
  if (existing === newContent) {
2270
2321
  result.skipped++;
@@ -2299,7 +2350,7 @@ function detectModifiedAddonFiles(projectDir, addonName) {
2299
2350
  for (const [relPath, originalHash] of Object.entries(installedChecksums)) {
2300
2351
  const absPath = join10(projectDir, baseDir, relPath);
2301
2352
  if (!existsSync8(absPath)) continue;
2302
- const current = checksum(readFileSync4(absPath, "utf-8"));
2353
+ const current = calculateChecksum(readFileSync4(absPath, "utf-8"));
2303
2354
  if (current !== originalHash) {
2304
2355
  modified.push(relPath);
2305
2356
  }
@@ -2345,23 +2396,42 @@ async function updateCommand(options) {
2345
2396
  const shouldMigrate = manifest.selectedIDEs.includes("claude-code") && !manifest.installMode && hasStandaloneSkills(manifest);
2346
2397
  if (shouldMigrate) {
2347
2398
  p4.note(
2348
- "Claude Code skills/agents detected as standalone.\nThe new version uses plugin mode (namespace devtronic:).",
2399
+ "Claude Code skills/agents detected as standalone.\nThe new version uses plugin mode. Skills are auto-namespaced in plugin mode.",
2349
2400
  "Migration Available"
2350
2401
  );
2351
2402
  if (!options.check) {
2352
2403
  const migrate = await p4.confirm({
2353
- message: "Migrate to plugin mode? (standalone \u2192 devtronic plugin)",
2404
+ message: "Migrate to GitHub marketplace? (standalone \u2192 devtronic marketplace plugin)",
2405
+ initialValue: true
2406
+ });
2407
+ if (!p4.isCancel(migrate) && migrate) {
2408
+ await migrateStandaloneToMarketplace(targetDir, manifest, analysis, options.dryRun);
2409
+ return;
2410
+ }
2411
+ }
2412
+ }
2413
+ const shouldMigrateToMarketplace = manifest.installMode === "plugin" && manifest.pluginPath;
2414
+ if (shouldMigrateToMarketplace) {
2415
+ p4.note(
2416
+ "Claude Code plugin detected as local directory.\nThe new version uses a GitHub marketplace for reliable plugin distribution.\nThis fixes cache issues and enables proper skill namespacing.",
2417
+ "Marketplace Migration Available"
2418
+ );
2419
+ if (!options.check) {
2420
+ const migrate = await p4.confirm({
2421
+ message: "Migrate to GitHub marketplace? (recommended)",
2354
2422
  initialValue: true
2355
2423
  });
2356
2424
  if (!p4.isCancel(migrate) && migrate) {
2357
- await migrateToPlugin(targetDir, manifest, analysis, options.dryRun);
2425
+ await migrateToMarketplace(targetDir, manifest, options.dryRun);
2358
2426
  return;
2359
2427
  }
2360
2428
  }
2361
2429
  }
2362
2430
  if (options.check) {
2363
- if (installedVersion === currentVersion && stackChanges.length === 0 && !shouldMigrate) {
2431
+ if (installedVersion === currentVersion && stackChanges.length === 0 && !shouldMigrate && !shouldMigrateToMarketplace) {
2364
2432
  p4.log.success("Already up to date!");
2433
+ } else if (shouldMigrateToMarketplace) {
2434
+ p4.log.info("Marketplace migration available: local plugin \u2192 GitHub marketplace");
2365
2435
  } else if (shouldMigrate) {
2366
2436
  p4.log.info("Plugin migration available: standalone \u2192 devtronic plugin");
2367
2437
  } else if (installedVersion !== currentVersion) {
@@ -2545,7 +2615,7 @@ async function updateCommand(options) {
2545
2615
  for (const ide of manifest.selectedIDEs) {
2546
2616
  const templateDir = join11(TEMPLATES_DIR, IDE_TEMPLATE_MAP[ide]);
2547
2617
  if (!existsSync9(templateDir)) continue;
2548
- const isPluginMode = ide === "claude-code" && manifest.installMode === "plugin";
2618
+ const isPluginMode = ide === "claude-code" && (manifest.installMode === "plugin" || manifest.installMode === "marketplace");
2549
2619
  const files = getAllFilesRecursive(templateDir);
2550
2620
  for (const file of files) {
2551
2621
  if (modifiedFiles.includes(file)) {
@@ -2562,6 +2632,9 @@ async function updateCommand(options) {
2562
2632
  updatedManifest.files[file] = createManifestEntry(templateContent);
2563
2633
  }
2564
2634
  }
2635
+ if (manifest.installMode === "marketplace") {
2636
+ registerGitHubPlugin(targetDir, PLUGIN_NAME, GITHUB_MARKETPLACE_NAME, GITHUB_MARKETPLACE_REPO);
2637
+ }
2565
2638
  if (manifest.installMode === "plugin" && manifest.pluginPath) {
2566
2639
  const userModifiedPluginFiles = /* @__PURE__ */ new Map();
2567
2640
  for (const [relPath, fileInfo] of Object.entries(updatedManifest.files)) {
@@ -2584,7 +2657,7 @@ async function updateCommand(options) {
2584
2657
  for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
2585
2658
  const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
2586
2659
  if (existsSync9(scriptPath)) {
2587
- chmodSync2(scriptPath, 493);
2660
+ chmodSync(scriptPath, 493);
2588
2661
  }
2589
2662
  }
2590
2663
  for (const [relPath, content] of userModifiedPluginFiles) {
@@ -2612,7 +2685,7 @@ async function updateCommand(options) {
2612
2685
  }
2613
2686
  const claudeMdPath = join11(targetDir, "CLAUDE.md");
2614
2687
  if (fileExists(claudeMdPath)) {
2615
- const stat = lstatSync(claudeMdPath);
2688
+ const stat = lstatSync2(claudeMdPath);
2616
2689
  if (stat.isSymbolicLink()) {
2617
2690
  const content = readFile(claudeMdPath);
2618
2691
  unlinkSync2(claudeMdPath);
@@ -2765,7 +2838,7 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
2765
2838
  }
2766
2839
  const claudeMdPath = join11(targetDir, "CLAUDE.md");
2767
2840
  if (fileExists(claudeMdPath)) {
2768
- const stat = lstatSync(claudeMdPath);
2841
+ const stat = lstatSync2(claudeMdPath);
2769
2842
  if (stat.isSymbolicLink()) {
2770
2843
  const content = readFile(claudeMdPath);
2771
2844
  unlinkSync2(claudeMdPath);
@@ -2788,6 +2861,10 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
2788
2861
  }
2789
2862
  }
2790
2863
  }
2864
+ if (manifest.installMode === "marketplace") {
2865
+ registerGitHubPlugin(targetDir, PLUGIN_NAME, GITHUB_MARKETPLACE_NAME, GITHUB_MARKETPLACE_REPO);
2866
+ regeneratedFiles.push("GitHub marketplace registration");
2867
+ }
2791
2868
  if (manifest.installMode === "plugin" && manifest.pluginPath) {
2792
2869
  const userModifiedPluginFiles = /* @__PURE__ */ new Map();
2793
2870
  for (const [relPath, fileInfo] of Object.entries(manifest.files)) {
@@ -2809,7 +2886,7 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
2809
2886
  for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
2810
2887
  const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
2811
2888
  if (existsSync9(scriptPath)) {
2812
- chmodSync2(scriptPath, 493);
2889
+ chmodSync(scriptPath, 493);
2813
2890
  }
2814
2891
  }
2815
2892
  for (const [relPath, content] of userModifiedPluginFiles) {
@@ -2839,29 +2916,15 @@ function hasStandaloneSkills(manifest) {
2839
2916
  (f) => f.startsWith(".claude/skills/") || f.startsWith(".claude/agents/")
2840
2917
  );
2841
2918
  }
2842
- async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
2919
+ async function migrateStandaloneToMarketplace(targetDir, manifest, analysis, dryRun) {
2843
2920
  if (dryRun) {
2844
2921
  p4.log.info("Would migrate standalone skills/agents to devtronic plugin.");
2845
2922
  p4.outro("Dry run complete \u2014 no changes made");
2846
2923
  return;
2847
2924
  }
2848
2925
  const spinner8 = p4.spinner();
2849
- spinner8.start("Migrating to plugin mode...");
2850
- const config = manifest.projectConfig || buildDefaultConfig(analysis);
2851
- const pluginResult = generatePlugin(
2852
- targetDir,
2853
- TEMPLATES_DIR,
2854
- getCliVersion(),
2855
- config,
2856
- analysis.packageManager
2857
- );
2858
- for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
2859
- const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
2860
- if (existsSync9(scriptPath)) {
2861
- chmodSync2(scriptPath, 493);
2862
- }
2863
- }
2864
- registerPlugin(targetDir, PLUGIN_NAME, MARKETPLACE_NAME, PLUGIN_DIR);
2926
+ spinner8.start("Migrating to GitHub marketplace...");
2927
+ registerGitHubPlugin(targetDir, PLUGIN_NAME, GITHUB_MARKETPLACE_NAME, GITHUB_MARKETPLACE_REPO);
2865
2928
  const removed = [];
2866
2929
  const preserved = [];
2867
2930
  for (const [path, fileInfo] of Object.entries(manifest.files)) {
@@ -2879,9 +2942,7 @@ async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
2879
2942
  }
2880
2943
  cleanEmptyDirs(join11(targetDir, ".claude", "skills"));
2881
2944
  cleanEmptyDirs(join11(targetDir, ".claude", "agents"));
2882
- Object.assign(manifest.files, pluginResult.files);
2883
- manifest.installMode = "plugin";
2884
- manifest.pluginPath = pluginResult.pluginPath;
2945
+ manifest.installMode = "marketplace";
2885
2946
  manifest.version = getCliVersion();
2886
2947
  manifest.implantedAt = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2887
2948
  writeManifest(targetDir, manifest);
@@ -2899,12 +2960,59 @@ async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
2899
2960
  );
2900
2961
  }
2901
2962
  p4.note(
2902
- ` Plugin: ${chalk5.cyan("devtronic")} at .claude-plugins/devtronic/
2903
- Skills: /devtronic:brief, /devtronic:spec, ...
2904
- Hooks: 5 workflow hooks enabled`,
2905
- "Plugin Generated"
2963
+ ` Marketplace: ${chalk5.cyan(GITHUB_MARKETPLACE_REPO)}
2964
+ Plugin: ${chalk5.cyan(PLUGIN_NAME)}
2965
+ Skills: /devtronic:brief, /devtronic:spec, ... (auto-namespaced)
2966
+
2967
+ ${chalk5.dim("Restart Claude Code or run /reload-plugins to activate.")}`,
2968
+ "GitHub Marketplace Registered"
2969
+ );
2970
+ p4.outro(chalk5.green("Migrated to GitHub marketplace!"));
2971
+ }
2972
+ async function migrateToMarketplace(targetDir, manifest, dryRun) {
2973
+ if (dryRun) {
2974
+ p4.log.info("Would migrate local plugin to GitHub marketplace.");
2975
+ p4.outro("Dry run complete \u2014 no changes made");
2976
+ return;
2977
+ }
2978
+ const spinner8 = p4.spinner();
2979
+ spinner8.start("Migrating to GitHub marketplace...");
2980
+ registerGitHubPlugin(targetDir, PLUGIN_NAME, GITHUB_MARKETPLACE_NAME, GITHUB_MARKETPLACE_REPO);
2981
+ const pluginDir = join11(targetDir, PLUGIN_DIR, PLUGIN_NAME);
2982
+ if (existsSync9(pluginDir)) {
2983
+ rmSync2(pluginDir, { recursive: true, force: true });
2984
+ }
2985
+ const marketplaceDescDir = join11(targetDir, PLUGIN_DIR, ".claude-plugin");
2986
+ if (existsSync9(marketplaceDescDir)) {
2987
+ rmSync2(marketplaceDescDir, { recursive: true, force: true });
2988
+ }
2989
+ const pluginsDir = join11(targetDir, PLUGIN_DIR);
2990
+ if (existsSync9(pluginsDir)) {
2991
+ const remaining = readdirSync2(pluginsDir);
2992
+ if (remaining.length === 0) {
2993
+ rmSync2(pluginsDir, { recursive: true, force: true });
2994
+ }
2995
+ }
2996
+ for (const key of Object.keys(manifest.files)) {
2997
+ if (key.startsWith(PLUGIN_DIR + "/")) {
2998
+ delete manifest.files[key];
2999
+ }
3000
+ }
3001
+ manifest.installMode = "marketplace";
3002
+ delete manifest.pluginPath;
3003
+ manifest.version = getCliVersion();
3004
+ manifest.implantedAt = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3005
+ writeManifest(targetDir, manifest);
3006
+ spinner8.stop("Migration complete");
3007
+ p4.note(
3008
+ ` Marketplace: ${chalk5.cyan(GITHUB_MARKETPLACE_REPO)}
3009
+ Plugin: ${chalk5.cyan(PLUGIN_NAME)}
3010
+ Skills: /devtronic:brief, /devtronic:spec, ... (auto-namespaced)
3011
+
3012
+ ${chalk5.dim("Restart Claude Code or run /reload-plugins to activate.")}`,
3013
+ "GitHub Marketplace Registered"
2906
3014
  );
2907
- p4.outro(chalk5.green("Migrated to plugin mode!"));
3015
+ p4.outro(chalk5.green("Migrated to GitHub marketplace!"));
2908
3016
  }
2909
3017
  function buildDefaultConfig(analysis) {
2910
3018
  const pm = analysis.packageManager || "npm";
@@ -2931,7 +3039,7 @@ function cleanEmptyDirs(dirPath) {
2931
3039
  const entries = readdirSync2(dirPath);
2932
3040
  for (const entry of entries) {
2933
3041
  const fullPath = join11(dirPath, entry);
2934
- if (existsSync9(fullPath) && lstatSync(fullPath).isDirectory()) {
3042
+ if (existsSync9(fullPath) && lstatSync2(fullPath).isDirectory()) {
2935
3043
  cleanEmptyDirs(fullPath);
2936
3044
  }
2937
3045
  }
@@ -3449,10 +3557,14 @@ Valid options:
3449
3557
  }
3450
3558
  }
3451
3559
  if (regeneratePlugin) {
3452
- if (!manifest.selectedIDEs.includes("claude-code") || manifest.installMode !== "plugin") {
3560
+ if (manifest.installMode === "marketplace") {
3561
+ p8.log.info("Plugin content is managed by the GitHub marketplace.");
3562
+ p8.log.info("Skills, agents, and hooks update automatically when a new CLI version is released.");
3563
+ p8.log.info(`Use ${chalk9.cyan("--rules")} or ${chalk9.cyan("--all")} (without --plugin) to regenerate local files.`);
3564
+ } else if (!manifest.selectedIDEs.includes("claude-code") || manifest.installMode !== "plugin") {
3453
3565
  p8.log.warn("Plugin regeneration only applies to Claude Code in plugin mode. Skipping.");
3454
3566
  } else {
3455
- const { generatePlugin: generatePlugin2 } = await import("./plugin-SGSFVXPA.js");
3567
+ const { generatePlugin: generatePlugin2 } = await import("./plugin-XSNKI5VI.js");
3456
3568
  const pluginResult = generatePlugin2(
3457
3569
  targetDir,
3458
3570
  TEMPLATES_DIR2,
@@ -3487,10 +3599,14 @@ Valid options:
3487
3599
  }
3488
3600
 
3489
3601
  // src/commands/info.ts
3490
- import { resolve as resolve9, join as join16 } from "path";
3602
+ import { resolve as resolve9, join as join16, dirname as dirname9 } from "path";
3491
3603
  import { existsSync as existsSync13, readdirSync as readdirSync3 } from "fs";
3604
+ import { fileURLToPath as fileURLToPath5 } from "url";
3492
3605
  import * as p9 from "@clack/prompts";
3493
3606
  import chalk10 from "chalk";
3607
+ var __info_filename = fileURLToPath5(import.meta.url);
3608
+ var __info_dirname = dirname9(__info_filename);
3609
+ var INFO_TEMPLATES_DIR = existsSync13(resolve9(__info_dirname, "../templates")) ? resolve9(__info_dirname, "../templates") : resolve9(__info_dirname, "../../templates");
3494
3610
  async function infoCommand() {
3495
3611
  const targetDir = resolve9(".");
3496
3612
  p9.intro(introTitle("Info"));
@@ -3516,22 +3632,42 @@ async function infoCommand() {
3516
3632
  ).length;
3517
3633
  }
3518
3634
  }
3519
- if (skillCount === 0) {
3520
- const claudeSkills = join16(targetDir, ".claude", "skills");
3521
- if (existsSync13(claudeSkills)) {
3522
- skillCount = readdirSync3(claudeSkills, { withFileTypes: true }).filter(
3523
- (e) => e.isDirectory() || e.isFile() && e.name.endsWith(".md")
3635
+ if (manifest.installMode === "marketplace" && skillCount === 0) {
3636
+ const templateSkills = join16(INFO_TEMPLATES_DIR, "claude-code", ".claude", "skills");
3637
+ if (existsSync13(templateSkills)) {
3638
+ skillCount = readdirSync3(templateSkills, { withFileTypes: true }).filter(
3639
+ (e) => e.isDirectory()
3524
3640
  ).length;
3525
3641
  }
3526
- }
3527
- if (agentCount === 0) {
3528
- const claudeAgents = join16(targetDir, ".claude", "agents");
3529
- if (existsSync13(claudeAgents)) {
3530
- agentCount = readdirSync3(claudeAgents, { withFileTypes: true }).filter(
3642
+ const templateAgents = join16(INFO_TEMPLATES_DIR, "claude-code", ".claude", "agents");
3643
+ if (existsSync13(templateAgents)) {
3644
+ agentCount = readdirSync3(templateAgents, { withFileTypes: true }).filter(
3531
3645
  (e) => e.isFile() && e.name.endsWith(".md")
3532
3646
  ).length;
3533
3647
  }
3534
3648
  }
3649
+ const claudeSkills = join16(targetDir, ".claude", "skills");
3650
+ if (existsSync13(claudeSkills)) {
3651
+ const localSkillCount = readdirSync3(claudeSkills, { withFileTypes: true }).filter(
3652
+ (e) => e.isDirectory() || e.isFile() && e.name.endsWith(".md")
3653
+ ).length;
3654
+ if (skillCount === 0) {
3655
+ skillCount = localSkillCount;
3656
+ } else if (localSkillCount > 0) {
3657
+ skillCount += localSkillCount;
3658
+ }
3659
+ }
3660
+ const claudeAgents = join16(targetDir, ".claude", "agents");
3661
+ if (existsSync13(claudeAgents)) {
3662
+ const localAgentCount = readdirSync3(claudeAgents, { withFileTypes: true }).filter(
3663
+ (e) => e.isFile() && e.name.endsWith(".md")
3664
+ ).length;
3665
+ if (agentCount === 0) {
3666
+ agentCount = localAgentCount;
3667
+ } else if (localAgentCount > 0) {
3668
+ agentCount += localAgentCount;
3669
+ }
3670
+ }
3535
3671
  }
3536
3672
  const latest = await latestPromise;
3537
3673
  if (latest) {
@@ -3570,10 +3706,14 @@ async function infoCommand() {
3570
3706
  }
3571
3707
 
3572
3708
  // src/commands/list.ts
3573
- import { resolve as resolve10, join as join17 } from "path";
3709
+ import { resolve as resolve10, join as join17, dirname as dirname10 } from "path";
3574
3710
  import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync5 } from "fs";
3711
+ import { fileURLToPath as fileURLToPath6 } from "url";
3575
3712
  import * as p10 from "@clack/prompts";
3576
3713
  import chalk11 from "chalk";
3714
+ var __list_filename = fileURLToPath6(import.meta.url);
3715
+ var __list_dirname = dirname10(__list_filename);
3716
+ var LIST_TEMPLATES_DIR = existsSync14(resolve10(__list_dirname, "../templates")) ? resolve10(__list_dirname, "../templates") : resolve10(__list_dirname, "../../templates");
3577
3717
  async function listCommand(filter, options) {
3578
3718
  const targetDir = resolve10(options.path || ".");
3579
3719
  p10.intro(introTitle("List"));
@@ -3603,6 +3743,20 @@ Valid options: skills, agents`);
3603
3743
  }
3604
3744
  }
3605
3745
  }
3746
+ if (manifest?.installMode === "marketplace") {
3747
+ if (showSkills && skills.length === 0) {
3748
+ const templateSkills = join17(LIST_TEMPLATES_DIR, "claude-code", ".claude", "skills");
3749
+ if (existsSync14(templateSkills)) {
3750
+ skills.push(...discoverSkills(templateSkills));
3751
+ }
3752
+ }
3753
+ if (showAgents && agents.length === 0) {
3754
+ const templateAgents = join17(LIST_TEMPLATES_DIR, "claude-code", ".claude", "agents");
3755
+ if (existsSync14(templateAgents)) {
3756
+ agents.push(...discoverAgents(templateAgents));
3757
+ }
3758
+ }
3759
+ }
3606
3760
  if (showSkills && skills.length === 0) {
3607
3761
  const claudeSkills = join17(targetDir, ".claude", "skills");
3608
3762
  if (existsSync14(claudeSkills)) {
@@ -3619,6 +3773,9 @@ Valid options: skills, agents`);
3619
3773
  if (skills.length > 0) {
3620
3774
  const skillLines = skills.sort((a, b) => a.name.localeCompare(b.name)).map((s) => ` ${symbols.bullet} ${chalk11.bold(s.name.padEnd(18))}${chalk11.dim(s.description)}`);
3621
3775
  p10.note(skillLines.join("\n"), `Skills (${skills.length})`);
3776
+ if (manifest?.installMode === "marketplace") {
3777
+ p10.log.info(chalk11.dim("Plugin skills loaded from GitHub marketplace"));
3778
+ }
3622
3779
  } else {
3623
3780
  p10.log.info("No skills found.");
3624
3781
  }
@@ -3627,6 +3784,9 @@ Valid options: skills, agents`);
3627
3784
  if (agents.length > 0) {
3628
3785
  const agentLines = agents.sort((a, b) => a.name.localeCompare(b.name)).map((a) => ` ${symbols.bullet} ${chalk11.bold(a.name.padEnd(18))}${chalk11.dim(a.description)}`);
3629
3786
  p10.note(agentLines.join("\n"), `Agents (${agents.length})`);
3787
+ if (manifest?.installMode === "marketplace") {
3788
+ p10.log.info(chalk11.dim("Plugin skills loaded from GitHub marketplace"));
3789
+ }
3630
3790
  } else {
3631
3791
  p10.log.info("No agents found.");
3632
3792
  }
@@ -3843,7 +4003,7 @@ async function configResetCommand(options) {
3843
4003
 
3844
4004
  // src/commands/doctor.ts
3845
4005
  import { resolve as resolve12, join as join18 } from "path";
3846
- import { existsSync as existsSync15, readdirSync as readdirSync5, statSync, chmodSync as chmodSync3, mkdirSync as mkdirSync3 } from "fs";
4006
+ import { existsSync as existsSync15, readdirSync as readdirSync5, statSync, chmodSync as chmodSync2, mkdirSync as mkdirSync3 } from "fs";
3847
4007
  import { execSync } from "child_process";
3848
4008
  import * as p12 from "@clack/prompts";
3849
4009
  import chalk13 from "chalk";
@@ -3859,8 +4019,8 @@ async function doctorCommand(options) {
3859
4019
  checks.push(checkManifestValid(manifest));
3860
4020
  checks.push(checkManifestFiles(targetDir, manifest));
3861
4021
  checks.push(checkScriptPermissions(targetDir, manifest));
3862
- if (manifest.installMode === "plugin") {
3863
- checks.push(checkPluginRegistered(targetDir));
4022
+ if (manifest.installMode === "plugin" || manifest.installMode === "marketplace") {
4023
+ checks.push(checkPluginRegistered(targetDir, manifest.installMode));
3864
4024
  }
3865
4025
  checks.push(checkHookScripts(targetDir, manifest));
3866
4026
  checks.push(checkQualityScripts(targetDir));
@@ -4008,13 +4168,29 @@ function checkScriptPermissions(targetDir, manifest) {
4008
4168
  fixable: true,
4009
4169
  fix: () => {
4010
4170
  for (const file of nonExecutable) {
4011
- chmodSync3(file, 493);
4171
+ chmodSync2(file, 493);
4012
4172
  }
4013
4173
  }
4014
4174
  };
4015
4175
  }
4016
- function checkPluginRegistered(targetDir) {
4176
+ function checkPluginRegistered(targetDir, installMode) {
4017
4177
  const settings = readClaudeSettings(targetDir);
4178
+ if (installMode === "marketplace") {
4179
+ const pluginKey2 = `${PLUGIN_NAME}@${GITHUB_MARKETPLACE_NAME}`;
4180
+ const isRegistered2 = settings.enabledPlugins?.[pluginKey2] === true;
4181
+ if (isRegistered2) {
4182
+ return { name: "plugin", status: "pass", message: "Marketplace plugin registered in .claude/settings.json" };
4183
+ }
4184
+ return {
4185
+ name: "plugin",
4186
+ status: "warn",
4187
+ message: "Marketplace plugin not registered in .claude/settings.json",
4188
+ fixable: true,
4189
+ fix: () => {
4190
+ registerGitHubPlugin(targetDir, PLUGIN_NAME, GITHUB_MARKETPLACE_NAME, GITHUB_MARKETPLACE_REPO);
4191
+ }
4192
+ };
4193
+ }
4018
4194
  const pluginKey = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
4019
4195
  const isRegistered = settings.enabledPlugins?.[pluginKey] === true;
4020
4196
  if (isRegistered) {
@@ -4169,8 +4345,8 @@ function checkEslint(targetDir) {
4169
4345
  }
4170
4346
 
4171
4347
  // src/commands/uninstall.ts
4172
- import { existsSync as existsSync16, rmSync as rmSync2, readdirSync as readdirSync6 } from "fs";
4173
- import { resolve as resolve13, join as join19, dirname as dirname9 } from "path";
4348
+ import { existsSync as existsSync16, rmSync as rmSync3, readdirSync as readdirSync6 } from "fs";
4349
+ import { resolve as resolve13, join as join19, dirname as dirname11 } from "path";
4174
4350
  import * as p13 from "@clack/prompts";
4175
4351
  import chalk14 from "chalk";
4176
4352
  var DEVTRONIC_FILES = ["CLAUDE.md", "AGENTS.md"];
@@ -4191,6 +4367,7 @@ async function uninstallCommand(options) {
4191
4367
  const existingFiles = managedFiles.filter((f) => fileExists(join19(targetDir, f)));
4192
4368
  const missingFiles = managedFiles.filter((f) => !fileExists(join19(targetDir, f)));
4193
4369
  const hasPlugin = manifest.installMode === "plugin" && existsSync16(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME));
4370
+ const hasMarketplace = manifest.installMode === "marketplace";
4194
4371
  const hasThoughts = existsSync16(join19(targetDir, "thoughts"));
4195
4372
  const hasClaudeMd = fileExists(join19(targetDir, "CLAUDE.md"));
4196
4373
  const hasAgentsMd = fileExists(join19(targetDir, "AGENTS.md"));
@@ -4198,6 +4375,9 @@ async function uninstallCommand(options) {
4198
4375
  p13.log.info(`IDEs: ${manifest.selectedIDEs.join(", ")}`);
4199
4376
  p13.log.info(`Mode: ${manifest.installMode || "standalone"}`);
4200
4377
  const removalLines = [];
4378
+ if (hasMarketplace) {
4379
+ removalLines.push(` ${symbols.fail} GitHub marketplace registration (${chalk14.cyan(GITHUB_MARKETPLACE_NAME)})`);
4380
+ }
4201
4381
  if (hasPlugin) {
4202
4382
  removalLines.push(` ${symbols.fail} Plugin ${chalk14.cyan(PLUGIN_NAME)} (${PLUGIN_DIR}/${PLUGIN_NAME}/)`);
4203
4383
  }
@@ -4261,6 +4441,27 @@ async function uninstallCommand(options) {
4261
4441
  const removed = [];
4262
4442
  const kept = [];
4263
4443
  const errors = [];
4444
+ if (hasMarketplace) {
4445
+ try {
4446
+ const settings = readClaudeSettings(targetDir);
4447
+ if (settings.extraKnownMarketplaces?.[GITHUB_MARKETPLACE_NAME]) {
4448
+ delete settings.extraKnownMarketplaces[GITHUB_MARKETPLACE_NAME];
4449
+ if (Object.keys(settings.extraKnownMarketplaces).length === 0) {
4450
+ delete settings.extraKnownMarketplaces;
4451
+ }
4452
+ }
4453
+ if (settings.enabledPlugins) {
4454
+ delete settings.enabledPlugins[`${PLUGIN_NAME}@${GITHUB_MARKETPLACE_NAME}`];
4455
+ if (Object.keys(settings.enabledPlugins).length === 0) {
4456
+ delete settings.enabledPlugins;
4457
+ }
4458
+ }
4459
+ writeClaudeSettings(targetDir, settings);
4460
+ removed.push("GitHub marketplace unregistered from .claude/settings.json");
4461
+ } catch (err) {
4462
+ errors.push(`Failed to unregister marketplace: ${err instanceof Error ? err.message : String(err)}`);
4463
+ }
4464
+ }
4264
4465
  if (hasPlugin) {
4265
4466
  try {
4266
4467
  unregisterPlugin(targetDir, PLUGIN_NAME, MARKETPLACE_NAME);
@@ -4279,18 +4480,18 @@ async function uninstallCommand(options) {
4279
4480
  }
4280
4481
  if (hasPlugin) {
4281
4482
  try {
4282
- rmSync2(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME), { recursive: true, force: true });
4483
+ rmSync3(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME), { recursive: true, force: true });
4283
4484
  removed.push(`${PLUGIN_DIR}/${PLUGIN_NAME}/`);
4284
4485
  const marketplaceDescDir = join19(targetDir, PLUGIN_DIR, ".claude-plugin");
4285
4486
  if (existsSync16(marketplaceDescDir)) {
4286
- rmSync2(marketplaceDescDir, { recursive: true, force: true });
4487
+ rmSync3(marketplaceDescDir, { recursive: true, force: true });
4287
4488
  removed.push(`${PLUGIN_DIR}/.claude-plugin/`);
4288
4489
  }
4289
4490
  const pluginsDir = join19(targetDir, PLUGIN_DIR);
4290
4491
  if (existsSync16(pluginsDir)) {
4291
4492
  const remaining = readdirSafe(pluginsDir);
4292
4493
  if (remaining.length === 0) {
4293
- rmSync2(pluginsDir, { recursive: true, force: true });
4494
+ rmSync3(pluginsDir, { recursive: true, force: true });
4294
4495
  removed.push(`${PLUGIN_DIR}/ (empty)`);
4295
4496
  }
4296
4497
  }
@@ -4305,9 +4506,9 @@ async function uninstallCommand(options) {
4305
4506
  try {
4306
4507
  const filePath = join19(targetDir, file);
4307
4508
  if (existsSync16(filePath)) {
4308
- rmSync2(filePath, { force: true });
4509
+ rmSync3(filePath, { force: true });
4309
4510
  removed.push(file);
4310
- cleanEmptyParents(targetDir, dirname9(file));
4511
+ cleanEmptyParents(targetDir, dirname11(file));
4311
4512
  }
4312
4513
  } catch (err) {
4313
4514
  errors.push(`Failed to remove ${file}: ${err instanceof Error ? err.message : String(err)}`);
@@ -4316,7 +4517,7 @@ async function uninstallCommand(options) {
4316
4517
  if (hasClaudeMd) {
4317
4518
  if (removeClaudeMd) {
4318
4519
  try {
4319
- rmSync2(join19(targetDir, "CLAUDE.md"), { force: true });
4520
+ rmSync3(join19(targetDir, "CLAUDE.md"), { force: true });
4320
4521
  removed.push("CLAUDE.md");
4321
4522
  } catch (err) {
4322
4523
  errors.push(`Failed to remove CLAUDE.md: ${err instanceof Error ? err.message : String(err)}`);
@@ -4328,7 +4529,7 @@ async function uninstallCommand(options) {
4328
4529
  if (hasAgentsMd) {
4329
4530
  if (removeAgentsMd) {
4330
4531
  try {
4331
- rmSync2(join19(targetDir, "AGENTS.md"), { force: true });
4532
+ rmSync3(join19(targetDir, "AGENTS.md"), { force: true });
4332
4533
  removed.push("AGENTS.md");
4333
4534
  } catch (err) {
4334
4535
  errors.push(`Failed to remove AGENTS.md: ${err instanceof Error ? err.message : String(err)}`);
@@ -4340,7 +4541,7 @@ async function uninstallCommand(options) {
4340
4541
  if (hasThoughts) {
4341
4542
  if (removeThoughts) {
4342
4543
  try {
4343
- rmSync2(join19(targetDir, "thoughts"), { recursive: true, force: true });
4544
+ rmSync3(join19(targetDir, "thoughts"), { recursive: true, force: true });
4344
4545
  removed.push("thoughts/");
4345
4546
  } catch (err) {
4346
4547
  errors.push(`Failed to remove thoughts/: ${err instanceof Error ? err.message : String(err)}`);
@@ -4352,7 +4553,7 @@ async function uninstallCommand(options) {
4352
4553
  try {
4353
4554
  const manifestDir = join19(targetDir, MANIFEST_DIR);
4354
4555
  if (existsSync16(manifestDir)) {
4355
- rmSync2(manifestDir, { recursive: true, force: true });
4556
+ rmSync3(manifestDir, { recursive: true, force: true });
4356
4557
  removed.push(`${MANIFEST_DIR}/`);
4357
4558
  }
4358
4559
  } catch (err) {
@@ -4402,8 +4603,8 @@ function cleanEmptyParents(targetDir, relDir) {
4402
4603
  const entries = readdirSafe(absDir);
4403
4604
  if (entries.length === 0) {
4404
4605
  try {
4405
- rmSync2(absDir, { recursive: true, force: true });
4406
- cleanEmptyParents(targetDir, dirname9(relDir));
4606
+ rmSync3(absDir, { recursive: true, force: true });
4607
+ cleanEmptyParents(targetDir, dirname11(relDir));
4407
4608
  } catch {
4408
4609
  }
4409
4610
  }
@@ -4417,8 +4618,8 @@ function readdirSafe(dir) {
4417
4618
  }
4418
4619
 
4419
4620
  // src/commands/addon.ts
4420
- import { resolve as resolve14, join as join20, dirname as dirname10 } from "path";
4421
- import { existsSync as existsSync17, unlinkSync as unlinkSync3, rmSync as rmSync3 } from "fs";
4621
+ import { resolve as resolve14, join as join20, dirname as dirname12 } from "path";
4622
+ import { existsSync as existsSync17, unlinkSync as unlinkSync3, rmSync as rmSync4 } from "fs";
4422
4623
  import * as p14 from "@clack/prompts";
4423
4624
  import chalk15 from "chalk";
4424
4625
  function isFileBasedAddon(addonName) {
@@ -4457,8 +4658,8 @@ Valid addons: ${validAddons.join(", ")}`);
4457
4658
  p14.outro("");
4458
4659
  return;
4459
4660
  }
4460
- if (manifest.installMode !== "plugin" || !manifest.pluginPath) {
4461
- p14.log.warn("Addons require Claude Code in plugin mode.");
4661
+ if (manifest.installMode !== "plugin" && manifest.installMode !== "marketplace") {
4662
+ p14.log.warn("The orchestration addon requires Claude Code in plugin or marketplace mode.");
4462
4663
  p14.log.info("Run `npx devtronic init` with Claude Code selected.");
4463
4664
  p14.outro("");
4464
4665
  return;
@@ -4477,6 +4678,29 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
4477
4678
  p14.outro("");
4478
4679
  return;
4479
4680
  }
4681
+ if (manifest.installMode === "marketplace") {
4682
+ const addon2 = ADDONS[addonName];
4683
+ p14.note(
4684
+ [
4685
+ ` ${chalk15.dim("Name:")} ${addon2.label}`,
4686
+ ` ${chalk15.dim("Description:")} ${addon2.description}`,
4687
+ ` ${chalk15.dim("Skills:")} ${addon2.skills.map((s) => chalk15.cyan(`/${s}`)).join(", ")}`
4688
+ ].join("\n"),
4689
+ "Enabling addon"
4690
+ );
4691
+ if (!manifest.projectConfig) {
4692
+ manifest.projectConfig = { architecture: "flat", layers: [], stateManagement: [], dataFetching: [], orm: [], testing: [], ui: [], validation: [], framework: "unknown", qualityCommand: "" };
4693
+ }
4694
+ manifest.projectConfig.enabledAddons = [...currentAddons, addonName];
4695
+ writeManifest(targetDir, manifest);
4696
+ p14.log.success(`${addon2.label} enabled`);
4697
+ p14.note(
4698
+ "Orchestration skills are included in the marketplace plugin.\nAlready available as /devtronic:briefing, /devtronic:recap, /devtronic:handoff.",
4699
+ "Info"
4700
+ );
4701
+ p14.outro("");
4702
+ return;
4703
+ }
4480
4704
  const addon = ADDONS[addonName];
4481
4705
  const pluginRoot = manifest.pluginPath;
4482
4706
  const skillsSourceDir = join20(TEMPLATES_DIR, "claude-code", ".claude", "skills");
@@ -4484,12 +4708,12 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
4484
4708
  [
4485
4709
  ` ${chalk15.dim("Name:")} ${addon.label}`,
4486
4710
  ` ${chalk15.dim("Description:")} ${addon.description}`,
4487
- ` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.cyan(`/devtronic:${s}`)).join(", ")}`,
4711
+ ` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.cyan(`/${s}`)).join(", ")}`,
4488
4712
  ` ${chalk15.dim("Subagents:")} ${addon.agents.length ? addon.agents.join(", ") : chalk15.dim("\u2014")}`
4489
4713
  ].join("\n"),
4490
4714
  "Adding addon"
4491
4715
  );
4492
- const confirmed = await p14.confirm({ message: "Add this addon?" });
4716
+ const confirmed = await p14.confirm({ message: `Add ${addonName}?` });
4493
4717
  if (p14.isCancel(confirmed) || !confirmed) {
4494
4718
  p14.cancel("Addon installation cancelled.");
4495
4719
  process.exit(0);
@@ -4509,7 +4733,7 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
4509
4733
  const content = readFile(join20(sourceDir, file));
4510
4734
  const destRelPath = join20(pluginRoot, "skills", skillDir, file);
4511
4735
  const destAbsPath = join20(targetDir, destRelPath);
4512
- ensureDir(dirname10(destAbsPath));
4736
+ ensureDir(dirname12(destAbsPath));
4513
4737
  writeFile(destAbsPath, content);
4514
4738
  manifest.files[destRelPath] = createManifestEntry(content);
4515
4739
  addedFiles.push(destRelPath);
@@ -4525,7 +4749,7 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
4525
4749
  writeManifest(targetDir, manifest);
4526
4750
  spinner8.stop(`${symbols.pass} ${addon.label} added`);
4527
4751
  p14.note(
4528
- addon.skills.map((s) => ` ${chalk15.cyan(`/devtronic:${s}`)}`).join("\n"),
4752
+ addon.skills.map((s) => ` ${chalk15.cyan(`/${s}`)}`).join("\n"),
4529
4753
  "New skills available"
4530
4754
  );
4531
4755
  p14.outro("Done. Restart Claude Code to load the new skills.");
@@ -4536,18 +4760,26 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
4536
4760
  p14.outro("");
4537
4761
  return;
4538
4762
  }
4763
+ if (manifest.installMode === "marketplace") {
4764
+ const addon2 = ADDONS[addonName];
4765
+ manifest.projectConfig.enabledAddons = currentAddons.filter((a) => a !== addonName);
4766
+ writeManifest(targetDir, manifest);
4767
+ p14.log.success(`${addon2.label} disabled`);
4768
+ p14.outro("");
4769
+ return;
4770
+ }
4539
4771
  const addon = ADDONS[addonName];
4540
4772
  const pluginRoot = manifest.pluginPath;
4541
4773
  p14.note(
4542
4774
  [
4543
4775
  ` ${chalk15.dim("Name:")} ${addon.label}`,
4544
4776
  ` ${chalk15.dim("Description:")} ${addon.description}`,
4545
- ` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.dim(`/devtronic:${s}`)).join(", ")}`,
4777
+ ` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.dim(`/${s}`)).join(", ")}`,
4546
4778
  ` ${chalk15.dim("Subagents:")} ${addon.agents.length ? addon.agents.join(", ") : chalk15.dim("\u2014")}`
4547
4779
  ].join("\n"),
4548
4780
  "Removing addon"
4549
4781
  );
4550
- const confirmed = await p14.confirm({ message: "Remove this addon?" });
4782
+ const confirmed = await p14.confirm({ message: `Remove ${addonName}?` });
4551
4783
  if (p14.isCancel(confirmed) || !confirmed) {
4552
4784
  p14.cancel("Addon removal cancelled.");
4553
4785
  process.exit(0);
@@ -4592,7 +4824,7 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
4592
4824
  }
4593
4825
  if (existsSync17(skillAbsDir)) {
4594
4826
  try {
4595
- rmSync3(skillAbsDir, { recursive: true });
4827
+ rmSync4(skillAbsDir, { recursive: true });
4596
4828
  } catch {
4597
4829
  }
4598
4830
  }
@@ -4604,7 +4836,7 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
4604
4836
  writeManifest(targetDir, manifest);
4605
4837
  spinner8.stop(`${symbols.pass} ${addon.label} removed`);
4606
4838
  p14.note(
4607
- addon.skills.map((s) => ` ${chalk15.dim(`/devtronic:${s}`)}`).join("\n"),
4839
+ addon.skills.map((s) => ` ${chalk15.dim(`/${s}`)}`).join("\n"),
4608
4840
  "Skills removed"
4609
4841
  );
4610
4842
  p14.outro("Done. Restart Claude Code to apply the changes.");
@@ -4626,7 +4858,7 @@ async function addFileBasedAddon(targetDir, addonName, _options) {
4626
4858
  ].join("\n"),
4627
4859
  "Adding addon"
4628
4860
  );
4629
- const confirmed = await p14.confirm({ message: "Add this addon?" });
4861
+ const confirmed = await p14.confirm({ message: `Add ${addonName}?` });
4630
4862
  if (p14.isCancel(confirmed) || !confirmed) {
4631
4863
  p14.cancel("Addon installation cancelled.");
4632
4864
  process.exit(0);
@@ -4877,7 +5109,7 @@ program.command("uninstall").description("Remove devtronic from your project").o
4877
5109
  });
4878
5110
  program.command("mode").description("Set or show the execution mode (hitl or afk)").argument("<mode>", "Mode: afk, hitl, or show").option("--path <path>", "Target directory (default: current directory)").action(async (mode, options) => {
4879
5111
  if (!["afk", "hitl", "show"].includes(mode)) {
4880
- console.error(`Invalid mode: "${mode}". Valid values: afk, hitl, show`);
5112
+ p16.cancel(`Invalid mode: "${mode}". Valid values: afk, hitl, show`);
4881
5113
  process.exit(1);
4882
5114
  }
4883
5115
  await modeCommand(mode, { path: options.path });