devtronic 1.2.6 → 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.
package/README.md CHANGED
@@ -36,7 +36,7 @@ The CLI analyzes your project (framework, architecture, stack) and generates per
36
36
  - **AGENTS.md** — Universal AI context personalized to your stack
37
37
  - **Architecture rules** — IDE-specific format (`.claude/rules/`, `.cursor/rules/`, etc.)
38
38
  - **Skills** (20 core + 12 design + 9 addon) — Reusable workflows (`/brief`, `/spec`, `/create-plan`, `/summary`, `/audit`, `/devtronic-help`, etc.)
39
- - **Agents** (15 + 3 addon) — Specialized subagents (code-reviewer, quality-runner, etc.)
39
+ - **Agents** (15 + 4 addon) — Specialized subagents (code-reviewer, quality-runner, etc.)
40
40
  - **Hooks** (5) — Automated workflow (lint-on-save, checkpoint, etc.)
41
41
  - **thoughts/** — Structured directory for AI working documents
42
42
 
@@ -55,7 +55,7 @@ npx devtronic addon remove <name> # Uninstall
55
55
  | Addon | Skills | Description |
56
56
  |-------|--------|-------------|
57
57
  | `orchestration` | `briefing`, `recap`, `handoff` | Pre-planning alignment, session recaps, context rotation for multi-session work |
58
- | `design-best-practices` | `design-init`, `design-review`, `design-refine`, `design-system`, `design-harden` | Frontend design quality: typography, color, layout, accessibility, motion, UX writing |
58
+ | `design-best-practices` | `design-init`, `design-critique`, `design-refine`, `design-tokens`, `design-harden` | Frontend design quality: typography, color, layout, accessibility, motion, UX writing |
59
59
  | `auto-devtronic` | `auto-devtronic`, `validate-task-afk` | Autonomous engineering loop — takes a GitHub issue, runs spec → tests → plan → implement → PR, self-corrects via failing tests |
60
60
 
61
61
  During `npx devtronic init` (Claude Code only), a multiselect prompt lets you enable any combination of addons upfront.
@@ -22,7 +22,7 @@ var ADDONS = {
22
22
  label: "auto-devtronic \u2014 Autonomous Engineering Loop",
23
23
  description: "Runs the full spec\u2192test\u2192plan\u2192execute\u2192PR pipeline autonomously. Self-corrects via failing tests. HITL and AFK modes.",
24
24
  skills: ["auto-devtronic"],
25
- agents: ["issue-parser", "failure-analyst", "quality-executor"]
25
+ agents: ["issue-parser", "failure-analyst", "quality-executor", "afk-task-validator"]
26
26
  }
27
27
  };
28
28
  var PRESETS = {
@@ -622,6 +622,8 @@ function generateSkillsSection(enabledAddons) {
622
622
  var PLUGIN_NAME = "devtronic";
623
623
  var MARKETPLACE_NAME = "devtronic-local";
624
624
  var PLUGIN_DIR = ".claude-plugins";
625
+ var GITHUB_MARKETPLACE_REPO = "r-bart/devtronic-plugin";
626
+ var GITHUB_MARKETPLACE_NAME = "devtronic";
625
627
  var BASE_SKILL_COUNT = CORE_SKILLS.length;
626
628
  var DESIGN_SKILL_COUNT = 12;
627
629
  var BASE_AGENT_COUNT = 15;
@@ -631,18 +633,11 @@ function generatePluginJson(cliVersion, addonSkillCount = 0) {
631
633
  return JSON.stringify(
632
634
  {
633
635
  name: PLUGIN_NAME,
636
+ description: `Agentic development toolkit \u2014 ${skillLabel}, ${BASE_AGENT_COUNT} agents, workflow hooks`,
634
637
  version: cliVersion,
635
- description: `devtronic \u2014 ${skillLabel}, ${BASE_AGENT_COUNT} agents, full workflow hooks`,
636
638
  author: {
637
- name: "r-bart",
638
- url: "https://github.com/r-bart/devtronic"
639
- },
640
- repository: "https://github.com/r-bart/devtronic",
641
- license: "MIT",
642
- keywords: ["agentic", "architecture", "clean-architecture", "ddd", "workflow", "skills"],
643
- skills: "./skills/",
644
- agents: "./agents/",
645
- hooks: "./hooks/hooks.json"
639
+ name: "r-bart"
640
+ }
646
641
  },
647
642
  null,
648
643
  2
@@ -661,7 +656,7 @@ function generateMarketplaceJson(addonSkillCount = 0) {
661
656
  plugins: [
662
657
  {
663
658
  name: PLUGIN_NAME,
664
- source: `./${PLUGIN_NAME}`,
659
+ source: `./plugins/${PLUGIN_NAME}`,
665
660
  description: `devtronic \u2014 ${skillLabel}, ${BASE_AGENT_COUNT} agents, full workflow hooks`
666
661
  }
667
662
  ]
@@ -752,6 +747,8 @@ export {
752
747
  PLUGIN_NAME,
753
748
  MARKETPLACE_NAME,
754
749
  PLUGIN_DIR,
750
+ GITHUB_MARKETPLACE_REPO,
751
+ GITHUB_MARKETPLACE_NAME,
755
752
  BASE_SKILL_COUNT,
756
753
  DESIGN_SKILL_COUNT,
757
754
  BASE_AGENT_COUNT,
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-WM7R52TC.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";
@@ -1128,7 +1130,7 @@ function writeClaudeSettings(targetDir, settings) {
1128
1130
  writeFile(settingsPath, JSON.stringify(settings, null, 2));
1129
1131
  }
1130
1132
  var LEGACY_PLUGIN_NAMES = ["dev-ai", "ai-agentic"];
1131
- var LEGACY_MARKETPLACE_NAMES = ["dev-ai-local", "ai-agentic-local"];
1133
+ var LEGACY_MARKETPLACE_NAMES = ["dev-ai-local", "ai-agentic-local", "devtronic-local"];
1132
1134
  function registerPlugin(targetDir, pluginName, marketplaceName, marketplacePath) {
1133
1135
  const settings = readClaudeSettings(targetDir);
1134
1136
  if (settings.extraKnownMarketplaces) {
@@ -1160,6 +1162,38 @@ function registerPlugin(targetDir, pluginName, marketplaceName, marketplacePath)
1160
1162
  }
1161
1163
  writeClaudeSettings(targetDir, settings);
1162
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
+ }
1163
1197
  function unregisterPlugin(targetDir, pluginName, marketplaceName) {
1164
1198
  const settings = readClaudeSettings(targetDir);
1165
1199
  const pluginKey = `${pluginName}@${marketplaceName}`;
@@ -1454,27 +1488,17 @@ Valid addons: ${validAddons.join(", ")}`);
1454
1488
  const generatedFiles = [];
1455
1489
  const usePluginMode = selectedIDEs.includes("claude-code");
1456
1490
  if (usePluginMode) {
1457
- const pluginResult = generatePlugin(
1491
+ registerGitHubPlugin(
1458
1492
  targetDir,
1459
- TEMPLATES_DIR,
1460
- getCliVersion(),
1461
- projectConfig,
1462
- analysis.packageManager
1493
+ PLUGIN_NAME,
1494
+ GITHUB_MARKETPLACE_NAME,
1495
+ GITHUB_MARKETPLACE_REPO
1463
1496
  );
1464
- for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
1465
- const scriptPath = join7(targetDir, pluginResult.pluginPath, "scripts", script);
1466
- if (existsSync5(scriptPath)) {
1467
- chmodSync(scriptPath, 493);
1468
- }
1469
- }
1470
- registerPlugin(targetDir, PLUGIN_NAME, MARKETPLACE_NAME, PLUGIN_DIR);
1471
- Object.assign(manifest.files, pluginResult.files);
1472
- manifest.installMode = "plugin";
1473
- manifest.pluginPath = pluginResult.pluginPath;
1497
+ manifest.installMode = "marketplace";
1474
1498
  const addonSkillCount = (projectConfig.enabledAddons || []).reduce((sum, a) => sum + (ADDONS[a]?.skills.length ?? 0), 0);
1475
1499
  const baseTotal = BASE_SKILL_COUNT + DESIGN_SKILL_COUNT;
1476
1500
  const skillLabel = addonSkillCount > 0 ? `${baseTotal} + ${addonSkillCount} addon skills` : `${baseTotal} skills`;
1477
- 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`);
1478
1502
  }
1479
1503
  for (const ide of selectedIDEs) {
1480
1504
  const templateName = IDE_TEMPLATE_MAP[ide];
@@ -1601,11 +1625,14 @@ Valid addons: ${validAddons.join(", ")}`);
1601
1625
  if (usePluginMode) {
1602
1626
  p3.note(
1603
1627
  [
1604
- ` Name: ${chalk4.cyan(PLUGIN_NAME)} at ${PLUGIN_DIR}/${PLUGIN_NAME}/`,
1605
- ` Skills: /brief, /spec, /research, ... (auto-namespaced in plugin mode)`,
1606
- ` 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.")}`
1607
1634
  ].join("\n"),
1608
- "Plugin Installed"
1635
+ "GitHub Marketplace Registered"
1609
1636
  );
1610
1637
  }
1611
1638
  p3.note(
@@ -1739,7 +1766,7 @@ function buildProjectConfigFromPreset(presetConfig, analysis) {
1739
1766
 
1740
1767
  // src/commands/update.ts
1741
1768
  import { resolve as resolve4, join as join11, dirname as dirname6 } from "path";
1742
- import { existsSync as existsSync9, unlinkSync as unlinkSync2, lstatSync as lstatSync2, 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";
1743
1770
  import * as p4 from "@clack/prompts";
1744
1771
  import chalk5 from "chalk";
1745
1772
 
@@ -2374,18 +2401,37 @@ async function updateCommand(options) {
2374
2401
  );
2375
2402
  if (!options.check) {
2376
2403
  const migrate = await p4.confirm({
2377
- 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)",
2378
2422
  initialValue: true
2379
2423
  });
2380
2424
  if (!p4.isCancel(migrate) && migrate) {
2381
- await migrateToPlugin(targetDir, manifest, analysis, options.dryRun);
2425
+ await migrateToMarketplace(targetDir, manifest, options.dryRun);
2382
2426
  return;
2383
2427
  }
2384
2428
  }
2385
2429
  }
2386
2430
  if (options.check) {
2387
- if (installedVersion === currentVersion && stackChanges.length === 0 && !shouldMigrate) {
2431
+ if (installedVersion === currentVersion && stackChanges.length === 0 && !shouldMigrate && !shouldMigrateToMarketplace) {
2388
2432
  p4.log.success("Already up to date!");
2433
+ } else if (shouldMigrateToMarketplace) {
2434
+ p4.log.info("Marketplace migration available: local plugin \u2192 GitHub marketplace");
2389
2435
  } else if (shouldMigrate) {
2390
2436
  p4.log.info("Plugin migration available: standalone \u2192 devtronic plugin");
2391
2437
  } else if (installedVersion !== currentVersion) {
@@ -2569,7 +2615,7 @@ async function updateCommand(options) {
2569
2615
  for (const ide of manifest.selectedIDEs) {
2570
2616
  const templateDir = join11(TEMPLATES_DIR, IDE_TEMPLATE_MAP[ide]);
2571
2617
  if (!existsSync9(templateDir)) continue;
2572
- const isPluginMode = ide === "claude-code" && manifest.installMode === "plugin";
2618
+ const isPluginMode = ide === "claude-code" && (manifest.installMode === "plugin" || manifest.installMode === "marketplace");
2573
2619
  const files = getAllFilesRecursive(templateDir);
2574
2620
  for (const file of files) {
2575
2621
  if (modifiedFiles.includes(file)) {
@@ -2586,6 +2632,9 @@ async function updateCommand(options) {
2586
2632
  updatedManifest.files[file] = createManifestEntry(templateContent);
2587
2633
  }
2588
2634
  }
2635
+ if (manifest.installMode === "marketplace") {
2636
+ registerGitHubPlugin(targetDir, PLUGIN_NAME, GITHUB_MARKETPLACE_NAME, GITHUB_MARKETPLACE_REPO);
2637
+ }
2589
2638
  if (manifest.installMode === "plugin" && manifest.pluginPath) {
2590
2639
  const userModifiedPluginFiles = /* @__PURE__ */ new Map();
2591
2640
  for (const [relPath, fileInfo] of Object.entries(updatedManifest.files)) {
@@ -2608,7 +2657,7 @@ async function updateCommand(options) {
2608
2657
  for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
2609
2658
  const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
2610
2659
  if (existsSync9(scriptPath)) {
2611
- chmodSync2(scriptPath, 493);
2660
+ chmodSync(scriptPath, 493);
2612
2661
  }
2613
2662
  }
2614
2663
  for (const [relPath, content] of userModifiedPluginFiles) {
@@ -2812,6 +2861,10 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
2812
2861
  }
2813
2862
  }
2814
2863
  }
2864
+ if (manifest.installMode === "marketplace") {
2865
+ registerGitHubPlugin(targetDir, PLUGIN_NAME, GITHUB_MARKETPLACE_NAME, GITHUB_MARKETPLACE_REPO);
2866
+ regeneratedFiles.push("GitHub marketplace registration");
2867
+ }
2815
2868
  if (manifest.installMode === "plugin" && manifest.pluginPath) {
2816
2869
  const userModifiedPluginFiles = /* @__PURE__ */ new Map();
2817
2870
  for (const [relPath, fileInfo] of Object.entries(manifest.files)) {
@@ -2833,7 +2886,7 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
2833
2886
  for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
2834
2887
  const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
2835
2888
  if (existsSync9(scriptPath)) {
2836
- chmodSync2(scriptPath, 493);
2889
+ chmodSync(scriptPath, 493);
2837
2890
  }
2838
2891
  }
2839
2892
  for (const [relPath, content] of userModifiedPluginFiles) {
@@ -2863,29 +2916,15 @@ function hasStandaloneSkills(manifest) {
2863
2916
  (f) => f.startsWith(".claude/skills/") || f.startsWith(".claude/agents/")
2864
2917
  );
2865
2918
  }
2866
- async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
2919
+ async function migrateStandaloneToMarketplace(targetDir, manifest, analysis, dryRun) {
2867
2920
  if (dryRun) {
2868
2921
  p4.log.info("Would migrate standalone skills/agents to devtronic plugin.");
2869
2922
  p4.outro("Dry run complete \u2014 no changes made");
2870
2923
  return;
2871
2924
  }
2872
2925
  const spinner8 = p4.spinner();
2873
- spinner8.start("Migrating to plugin mode...");
2874
- const config = manifest.projectConfig || buildDefaultConfig(analysis);
2875
- const pluginResult = generatePlugin(
2876
- targetDir,
2877
- TEMPLATES_DIR,
2878
- getCliVersion(),
2879
- config,
2880
- analysis.packageManager
2881
- );
2882
- for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
2883
- const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
2884
- if (existsSync9(scriptPath)) {
2885
- chmodSync2(scriptPath, 493);
2886
- }
2887
- }
2888
- 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);
2889
2928
  const removed = [];
2890
2929
  const preserved = [];
2891
2930
  for (const [path, fileInfo] of Object.entries(manifest.files)) {
@@ -2903,9 +2942,7 @@ async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
2903
2942
  }
2904
2943
  cleanEmptyDirs(join11(targetDir, ".claude", "skills"));
2905
2944
  cleanEmptyDirs(join11(targetDir, ".claude", "agents"));
2906
- Object.assign(manifest.files, pluginResult.files);
2907
- manifest.installMode = "plugin";
2908
- manifest.pluginPath = pluginResult.pluginPath;
2945
+ manifest.installMode = "marketplace";
2909
2946
  manifest.version = getCliVersion();
2910
2947
  manifest.implantedAt = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2911
2948
  writeManifest(targetDir, manifest);
@@ -2923,12 +2960,59 @@ async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
2923
2960
  );
2924
2961
  }
2925
2962
  p4.note(
2926
- ` Plugin: ${chalk5.cyan("devtronic")} at .claude-plugins/devtronic/
2927
- Skills: /brief, /spec, ... (auto-namespaced in plugin mode)
2928
- Hooks: 5 workflow hooks enabled`,
2929
- "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"
2930
2969
  );
2931
- p4.outro(chalk5.green("Migrated to plugin mode!"));
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"
3014
+ );
3015
+ p4.outro(chalk5.green("Migrated to GitHub marketplace!"));
2932
3016
  }
2933
3017
  function buildDefaultConfig(analysis) {
2934
3018
  const pm = analysis.packageManager || "npm";
@@ -3473,10 +3557,14 @@ Valid options:
3473
3557
  }
3474
3558
  }
3475
3559
  if (regeneratePlugin) {
3476
- 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") {
3477
3565
  p8.log.warn("Plugin regeneration only applies to Claude Code in plugin mode. Skipping.");
3478
3566
  } else {
3479
- const { generatePlugin: generatePlugin2 } = await import("./plugin-KQQASBQX.js");
3567
+ const { generatePlugin: generatePlugin2 } = await import("./plugin-XSNKI5VI.js");
3480
3568
  const pluginResult = generatePlugin2(
3481
3569
  targetDir,
3482
3570
  TEMPLATES_DIR2,
@@ -3511,10 +3599,14 @@ Valid options:
3511
3599
  }
3512
3600
 
3513
3601
  // src/commands/info.ts
3514
- import { resolve as resolve9, join as join16 } from "path";
3602
+ import { resolve as resolve9, join as join16, dirname as dirname9 } from "path";
3515
3603
  import { existsSync as existsSync13, readdirSync as readdirSync3 } from "fs";
3604
+ import { fileURLToPath as fileURLToPath5 } from "url";
3516
3605
  import * as p9 from "@clack/prompts";
3517
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");
3518
3610
  async function infoCommand() {
3519
3611
  const targetDir = resolve9(".");
3520
3612
  p9.intro(introTitle("Info"));
@@ -3540,22 +3632,42 @@ async function infoCommand() {
3540
3632
  ).length;
3541
3633
  }
3542
3634
  }
3543
- if (skillCount === 0) {
3544
- const claudeSkills = join16(targetDir, ".claude", "skills");
3545
- if (existsSync13(claudeSkills)) {
3546
- skillCount = readdirSync3(claudeSkills, { withFileTypes: true }).filter(
3547
- (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()
3548
3640
  ).length;
3549
3641
  }
3550
- }
3551
- if (agentCount === 0) {
3552
- const claudeAgents = join16(targetDir, ".claude", "agents");
3553
- if (existsSync13(claudeAgents)) {
3554
- 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(
3555
3645
  (e) => e.isFile() && e.name.endsWith(".md")
3556
3646
  ).length;
3557
3647
  }
3558
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
+ }
3559
3671
  }
3560
3672
  const latest = await latestPromise;
3561
3673
  if (latest) {
@@ -3594,10 +3706,14 @@ async function infoCommand() {
3594
3706
  }
3595
3707
 
3596
3708
  // src/commands/list.ts
3597
- import { resolve as resolve10, join as join17 } from "path";
3709
+ import { resolve as resolve10, join as join17, dirname as dirname10 } from "path";
3598
3710
  import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync5 } from "fs";
3711
+ import { fileURLToPath as fileURLToPath6 } from "url";
3599
3712
  import * as p10 from "@clack/prompts";
3600
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");
3601
3717
  async function listCommand(filter, options) {
3602
3718
  const targetDir = resolve10(options.path || ".");
3603
3719
  p10.intro(introTitle("List"));
@@ -3627,6 +3743,20 @@ Valid options: skills, agents`);
3627
3743
  }
3628
3744
  }
3629
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
+ }
3630
3760
  if (showSkills && skills.length === 0) {
3631
3761
  const claudeSkills = join17(targetDir, ".claude", "skills");
3632
3762
  if (existsSync14(claudeSkills)) {
@@ -3643,6 +3773,9 @@ Valid options: skills, agents`);
3643
3773
  if (skills.length > 0) {
3644
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)}`);
3645
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
+ }
3646
3779
  } else {
3647
3780
  p10.log.info("No skills found.");
3648
3781
  }
@@ -3651,6 +3784,9 @@ Valid options: skills, agents`);
3651
3784
  if (agents.length > 0) {
3652
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)}`);
3653
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
+ }
3654
3790
  } else {
3655
3791
  p10.log.info("No agents found.");
3656
3792
  }
@@ -3867,7 +4003,7 @@ async function configResetCommand(options) {
3867
4003
 
3868
4004
  // src/commands/doctor.ts
3869
4005
  import { resolve as resolve12, join as join18 } from "path";
3870
- 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";
3871
4007
  import { execSync } from "child_process";
3872
4008
  import * as p12 from "@clack/prompts";
3873
4009
  import chalk13 from "chalk";
@@ -3883,8 +4019,8 @@ async function doctorCommand(options) {
3883
4019
  checks.push(checkManifestValid(manifest));
3884
4020
  checks.push(checkManifestFiles(targetDir, manifest));
3885
4021
  checks.push(checkScriptPermissions(targetDir, manifest));
3886
- if (manifest.installMode === "plugin") {
3887
- checks.push(checkPluginRegistered(targetDir));
4022
+ if (manifest.installMode === "plugin" || manifest.installMode === "marketplace") {
4023
+ checks.push(checkPluginRegistered(targetDir, manifest.installMode));
3888
4024
  }
3889
4025
  checks.push(checkHookScripts(targetDir, manifest));
3890
4026
  checks.push(checkQualityScripts(targetDir));
@@ -4032,13 +4168,29 @@ function checkScriptPermissions(targetDir, manifest) {
4032
4168
  fixable: true,
4033
4169
  fix: () => {
4034
4170
  for (const file of nonExecutable) {
4035
- chmodSync3(file, 493);
4171
+ chmodSync2(file, 493);
4036
4172
  }
4037
4173
  }
4038
4174
  };
4039
4175
  }
4040
- function checkPluginRegistered(targetDir) {
4176
+ function checkPluginRegistered(targetDir, installMode) {
4041
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
+ }
4042
4194
  const pluginKey = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
4043
4195
  const isRegistered = settings.enabledPlugins?.[pluginKey] === true;
4044
4196
  if (isRegistered) {
@@ -4193,8 +4345,8 @@ function checkEslint(targetDir) {
4193
4345
  }
4194
4346
 
4195
4347
  // src/commands/uninstall.ts
4196
- import { existsSync as existsSync16, rmSync as rmSync2, readdirSync as readdirSync6 } from "fs";
4197
- 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";
4198
4350
  import * as p13 from "@clack/prompts";
4199
4351
  import chalk14 from "chalk";
4200
4352
  var DEVTRONIC_FILES = ["CLAUDE.md", "AGENTS.md"];
@@ -4215,6 +4367,7 @@ async function uninstallCommand(options) {
4215
4367
  const existingFiles = managedFiles.filter((f) => fileExists(join19(targetDir, f)));
4216
4368
  const missingFiles = managedFiles.filter((f) => !fileExists(join19(targetDir, f)));
4217
4369
  const hasPlugin = manifest.installMode === "plugin" && existsSync16(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME));
4370
+ const hasMarketplace = manifest.installMode === "marketplace";
4218
4371
  const hasThoughts = existsSync16(join19(targetDir, "thoughts"));
4219
4372
  const hasClaudeMd = fileExists(join19(targetDir, "CLAUDE.md"));
4220
4373
  const hasAgentsMd = fileExists(join19(targetDir, "AGENTS.md"));
@@ -4222,6 +4375,9 @@ async function uninstallCommand(options) {
4222
4375
  p13.log.info(`IDEs: ${manifest.selectedIDEs.join(", ")}`);
4223
4376
  p13.log.info(`Mode: ${manifest.installMode || "standalone"}`);
4224
4377
  const removalLines = [];
4378
+ if (hasMarketplace) {
4379
+ removalLines.push(` ${symbols.fail} GitHub marketplace registration (${chalk14.cyan(GITHUB_MARKETPLACE_NAME)})`);
4380
+ }
4225
4381
  if (hasPlugin) {
4226
4382
  removalLines.push(` ${symbols.fail} Plugin ${chalk14.cyan(PLUGIN_NAME)} (${PLUGIN_DIR}/${PLUGIN_NAME}/)`);
4227
4383
  }
@@ -4285,6 +4441,27 @@ async function uninstallCommand(options) {
4285
4441
  const removed = [];
4286
4442
  const kept = [];
4287
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
+ }
4288
4465
  if (hasPlugin) {
4289
4466
  try {
4290
4467
  unregisterPlugin(targetDir, PLUGIN_NAME, MARKETPLACE_NAME);
@@ -4303,18 +4480,18 @@ async function uninstallCommand(options) {
4303
4480
  }
4304
4481
  if (hasPlugin) {
4305
4482
  try {
4306
- rmSync2(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME), { recursive: true, force: true });
4483
+ rmSync3(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME), { recursive: true, force: true });
4307
4484
  removed.push(`${PLUGIN_DIR}/${PLUGIN_NAME}/`);
4308
4485
  const marketplaceDescDir = join19(targetDir, PLUGIN_DIR, ".claude-plugin");
4309
4486
  if (existsSync16(marketplaceDescDir)) {
4310
- rmSync2(marketplaceDescDir, { recursive: true, force: true });
4487
+ rmSync3(marketplaceDescDir, { recursive: true, force: true });
4311
4488
  removed.push(`${PLUGIN_DIR}/.claude-plugin/`);
4312
4489
  }
4313
4490
  const pluginsDir = join19(targetDir, PLUGIN_DIR);
4314
4491
  if (existsSync16(pluginsDir)) {
4315
4492
  const remaining = readdirSafe(pluginsDir);
4316
4493
  if (remaining.length === 0) {
4317
- rmSync2(pluginsDir, { recursive: true, force: true });
4494
+ rmSync3(pluginsDir, { recursive: true, force: true });
4318
4495
  removed.push(`${PLUGIN_DIR}/ (empty)`);
4319
4496
  }
4320
4497
  }
@@ -4329,9 +4506,9 @@ async function uninstallCommand(options) {
4329
4506
  try {
4330
4507
  const filePath = join19(targetDir, file);
4331
4508
  if (existsSync16(filePath)) {
4332
- rmSync2(filePath, { force: true });
4509
+ rmSync3(filePath, { force: true });
4333
4510
  removed.push(file);
4334
- cleanEmptyParents(targetDir, dirname9(file));
4511
+ cleanEmptyParents(targetDir, dirname11(file));
4335
4512
  }
4336
4513
  } catch (err) {
4337
4514
  errors.push(`Failed to remove ${file}: ${err instanceof Error ? err.message : String(err)}`);
@@ -4340,7 +4517,7 @@ async function uninstallCommand(options) {
4340
4517
  if (hasClaudeMd) {
4341
4518
  if (removeClaudeMd) {
4342
4519
  try {
4343
- rmSync2(join19(targetDir, "CLAUDE.md"), { force: true });
4520
+ rmSync3(join19(targetDir, "CLAUDE.md"), { force: true });
4344
4521
  removed.push("CLAUDE.md");
4345
4522
  } catch (err) {
4346
4523
  errors.push(`Failed to remove CLAUDE.md: ${err instanceof Error ? err.message : String(err)}`);
@@ -4352,7 +4529,7 @@ async function uninstallCommand(options) {
4352
4529
  if (hasAgentsMd) {
4353
4530
  if (removeAgentsMd) {
4354
4531
  try {
4355
- rmSync2(join19(targetDir, "AGENTS.md"), { force: true });
4532
+ rmSync3(join19(targetDir, "AGENTS.md"), { force: true });
4356
4533
  removed.push("AGENTS.md");
4357
4534
  } catch (err) {
4358
4535
  errors.push(`Failed to remove AGENTS.md: ${err instanceof Error ? err.message : String(err)}`);
@@ -4364,7 +4541,7 @@ async function uninstallCommand(options) {
4364
4541
  if (hasThoughts) {
4365
4542
  if (removeThoughts) {
4366
4543
  try {
4367
- rmSync2(join19(targetDir, "thoughts"), { recursive: true, force: true });
4544
+ rmSync3(join19(targetDir, "thoughts"), { recursive: true, force: true });
4368
4545
  removed.push("thoughts/");
4369
4546
  } catch (err) {
4370
4547
  errors.push(`Failed to remove thoughts/: ${err instanceof Error ? err.message : String(err)}`);
@@ -4376,7 +4553,7 @@ async function uninstallCommand(options) {
4376
4553
  try {
4377
4554
  const manifestDir = join19(targetDir, MANIFEST_DIR);
4378
4555
  if (existsSync16(manifestDir)) {
4379
- rmSync2(manifestDir, { recursive: true, force: true });
4556
+ rmSync3(manifestDir, { recursive: true, force: true });
4380
4557
  removed.push(`${MANIFEST_DIR}/`);
4381
4558
  }
4382
4559
  } catch (err) {
@@ -4426,8 +4603,8 @@ function cleanEmptyParents(targetDir, relDir) {
4426
4603
  const entries = readdirSafe(absDir);
4427
4604
  if (entries.length === 0) {
4428
4605
  try {
4429
- rmSync2(absDir, { recursive: true, force: true });
4430
- cleanEmptyParents(targetDir, dirname9(relDir));
4606
+ rmSync3(absDir, { recursive: true, force: true });
4607
+ cleanEmptyParents(targetDir, dirname11(relDir));
4431
4608
  } catch {
4432
4609
  }
4433
4610
  }
@@ -4441,8 +4618,8 @@ function readdirSafe(dir) {
4441
4618
  }
4442
4619
 
4443
4620
  // src/commands/addon.ts
4444
- import { resolve as resolve14, join as join20, dirname as dirname10 } from "path";
4445
- 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";
4446
4623
  import * as p14 from "@clack/prompts";
4447
4624
  import chalk15 from "chalk";
4448
4625
  function isFileBasedAddon(addonName) {
@@ -4481,8 +4658,8 @@ Valid addons: ${validAddons.join(", ")}`);
4481
4658
  p14.outro("");
4482
4659
  return;
4483
4660
  }
4484
- if (manifest.installMode !== "plugin" || !manifest.pluginPath) {
4485
- 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.");
4486
4663
  p14.log.info("Run `npx devtronic init` with Claude Code selected.");
4487
4664
  p14.outro("");
4488
4665
  return;
@@ -4501,6 +4678,29 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
4501
4678
  p14.outro("");
4502
4679
  return;
4503
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
+ }
4504
4704
  const addon = ADDONS[addonName];
4505
4705
  const pluginRoot = manifest.pluginPath;
4506
4706
  const skillsSourceDir = join20(TEMPLATES_DIR, "claude-code", ".claude", "skills");
@@ -4533,7 +4733,7 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
4533
4733
  const content = readFile(join20(sourceDir, file));
4534
4734
  const destRelPath = join20(pluginRoot, "skills", skillDir, file);
4535
4735
  const destAbsPath = join20(targetDir, destRelPath);
4536
- ensureDir(dirname10(destAbsPath));
4736
+ ensureDir(dirname12(destAbsPath));
4537
4737
  writeFile(destAbsPath, content);
4538
4738
  manifest.files[destRelPath] = createManifestEntry(content);
4539
4739
  addedFiles.push(destRelPath);
@@ -4560,6 +4760,14 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
4560
4760
  p14.outro("");
4561
4761
  return;
4562
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
+ }
4563
4771
  const addon = ADDONS[addonName];
4564
4772
  const pluginRoot = manifest.pluginPath;
4565
4773
  p14.note(
@@ -4616,7 +4824,7 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
4616
4824
  }
4617
4825
  if (existsSync17(skillAbsDir)) {
4618
4826
  try {
4619
- rmSync3(skillAbsDir, { recursive: true });
4827
+ rmSync4(skillAbsDir, { recursive: true });
4620
4828
  } catch {
4621
4829
  }
4622
4830
  }
@@ -2,17 +2,21 @@ import {
2
2
  BASE_AGENT_COUNT,
3
3
  BASE_SKILL_COUNT,
4
4
  DESIGN_SKILL_COUNT,
5
+ GITHUB_MARKETPLACE_NAME,
6
+ GITHUB_MARKETPLACE_REPO,
5
7
  MARKETPLACE_NAME,
6
8
  PLUGIN_DIR,
7
9
  PLUGIN_NAME,
8
10
  generateMarketplaceJson,
9
11
  generatePlugin,
10
12
  generatePluginJson
11
- } from "./chunk-WM7R52TC.js";
13
+ } from "./chunk-YBT2XOOQ.js";
12
14
  export {
13
15
  BASE_AGENT_COUNT,
14
16
  BASE_SKILL_COUNT,
15
17
  DESIGN_SKILL_COUNT,
18
+ GITHUB_MARKETPLACE_NAME,
19
+ GITHUB_MARKETPLACE_REPO,
16
20
  MARKETPLACE_NAME,
17
21
  PLUGIN_DIR,
18
22
  PLUGIN_NAME,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devtronic",
3
- "version": "1.2.6",
3
+ "version": "1.3.0",
4
4
  "description": "AI-assisted development toolkit — skills, agents, quality gates, and rules for Claude Code, Cursor, Copilot, and Antigravity",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+ # Auto-lint after file changes
3
+ # Generated by devtronic — generic version (auto-detects package manager)
4
+
5
+ # Detect package manager
6
+ if [ -f "pnpm-lock.yaml" ]; then
7
+ PM="pnpm"
8
+ elif [ -f "yarn.lock" ]; then
9
+ PM="yarn"
10
+ elif [ -f "bun.lockb" ]; then
11
+ PM="bun"
12
+ else
13
+ PM="npm"
14
+ fi
15
+
16
+ RUN="$PM run"
17
+ if [ "$PM" != "npm" ]; then
18
+ RUN="$PM"
19
+ fi
20
+
21
+ # Try lint:fix first, fall back to lint --fix
22
+ if node -e "const p=require('./package.json');process.exit(p.scripts?.['lint:fix']?0:1)" 2>/dev/null; then
23
+ $RUN lint:fix -- --quiet 2>/dev/null || true
24
+ elif node -e "const p=require('./package.json');process.exit(p.scripts?.lint?0:1)" 2>/dev/null; then
25
+ $RUN lint --fix --quiet 2>/dev/null || true
26
+ fi
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+ # Auto-checkpoint before context compaction
3
+ # Generated by devtronic
4
+
5
+ CHECKPOINT_DIR="thoughts/checkpoints"
6
+ TIMESTAMP=$(date +%Y%m%d_%H%M%S)
7
+
8
+ mkdir -p "$CHECKPOINT_DIR"
9
+
10
+ {
11
+ echo "# Auto-checkpoint: $TIMESTAMP"
12
+ echo ""
13
+ echo "## Git Status"
14
+ git diff --stat 2>/dev/null || echo "Not a git repo"
15
+ echo ""
16
+ echo "## Recent Commits"
17
+ git log --oneline -5 2>/dev/null || echo "No commits"
18
+ } > "$CHECKPOINT_DIR/${TIMESTAMP}_pre-compact.md"
19
+
20
+ echo "Checkpoint saved: $CHECKPOINT_DIR/${TIMESTAMP}_pre-compact.md"
21
+
22
+ # Update persistent state (minimal — skill-level checkpoint writes richer state)
23
+ STATE_FILE="thoughts/STATE.md"
24
+ mkdir -p "$(dirname "$STATE_FILE")"
25
+ BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
26
+ {
27
+ echo "# Project State"
28
+ echo ""
29
+ echo "**Updated**: $(date '+%Y-%m-%d %H:%M') (auto-compact)"
30
+ echo "**Branch**: $BRANCH"
31
+ echo ""
32
+ echo "## Last Auto-Checkpoint"
33
+ echo ""
34
+ echo "Context was compacted. See: \`$CHECKPOINT_DIR/${TIMESTAMP}_pre-compact.md\`"
35
+ echo ""
36
+ echo "## Recent Commits"
37
+ echo ""
38
+ git log --oneline -5 2>/dev/null || echo "No commits"
39
+ } > "$STATE_FILE"
@@ -0,0 +1,75 @@
1
+ {
2
+ "description": "r-bart devtronic — workflow hooks",
3
+ "hooks": {
4
+ "SessionStart": [
5
+ {
6
+ "matcher": "startup",
7
+ "hooks": [
8
+ {
9
+ "type": "prompt",
10
+ "prompt": "Quick project orientation: First check if thoughts/STATE.md exists — if so, read it and summarize the current project state. Then check git status, recent commits, and any in-progress work. Give a 3-line summary prioritizing STATE.md context if available.\n\nContext: $ARGUMENTS",
11
+ "model": "haiku",
12
+ "timeout": 30
13
+ }
14
+ ]
15
+ }
16
+ ],
17
+ "PostToolUse": [
18
+ {
19
+ "matcher": "Write|Edit",
20
+ "hooks": [
21
+ {
22
+ "type": "command",
23
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/auto-lint.sh 2>/dev/null || true",
24
+ "timeout": 30,
25
+ "statusMessage": "Auto-linting..."
26
+ }
27
+ ]
28
+ }
29
+ ],
30
+ "Stop": [
31
+ {
32
+ "hooks": [
33
+ {
34
+ "type": "command",
35
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/stop-guard.sh",
36
+ "timeout": 60,
37
+ "statusMessage": "Running quality checks..."
38
+ },
39
+ {
40
+ "type": "prompt",
41
+ "prompt": "If thoughts/plans/ contains a recent plan with a \"Done Criteria\" section, quickly check: are there unmet criteria? If yes, list them as a brief reminder. Do NOT run a full review. If no plan or all criteria met, say nothing.",
42
+ "model": "haiku",
43
+ "timeout": 15,
44
+ "statusMessage": "Checking done criteria..."
45
+ }
46
+ ]
47
+ }
48
+ ],
49
+ "SubagentStop": [
50
+ {
51
+ "hooks": [
52
+ {
53
+ "type": "prompt",
54
+ "prompt": "A subagent has finished. Based on the metadata below, assess if it completed successfully.\nConsider the agent type and whether the session ended normally.\nRespond with {\"ok\": true} if it appears complete, or {\"ok\": false, \"reason\": \"...\"} if something seems off.\n\nIMPORTANT: If stop_hook_active is true, always respond {\"ok\": true} to prevent infinite loops.\n\nContext: $ARGUMENTS",
55
+ "model": "haiku",
56
+ "timeout": 30
57
+ }
58
+ ]
59
+ }
60
+ ],
61
+ "PreCompact": [
62
+ {
63
+ "matcher": "auto",
64
+ "hooks": [
65
+ {
66
+ "type": "command",
67
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/checkpoint.sh",
68
+ "timeout": 30,
69
+ "statusMessage": "Auto-checkpoint before compaction..."
70
+ }
71
+ ]
72
+ }
73
+ ]
74
+ }
75
+ }
@@ -0,0 +1,54 @@
1
+ #!/bin/bash
2
+ # Quality gate: runs checks before allowing Claude to stop
3
+ # Generated by devtronic — generic version (auto-detects package manager)
4
+
5
+ INPUT=$(cat)
6
+
7
+ # Prevent infinite loops: if already triggered by a stop hook, allow stop
8
+ if echo "$INPUT" | grep -q '"stop_hook_active"'; then
9
+ if echo "$INPUT" | grep -q '"stop_hook_active"[[:space:]]*:[[:space:]]*true'; then
10
+ exit 0
11
+ fi
12
+ fi
13
+
14
+ # Detect package manager
15
+ if [ -f "pnpm-lock.yaml" ]; then
16
+ PM="pnpm"
17
+ elif [ -f "yarn.lock" ]; then
18
+ PM="yarn"
19
+ elif [ -f "bun.lockb" ]; then
20
+ PM="bun"
21
+ else
22
+ PM="npm"
23
+ fi
24
+
25
+ RUN="$PM run"
26
+ if [ "$PM" != "npm" ]; then
27
+ RUN="$PM"
28
+ fi
29
+
30
+ # Build quality command from available scripts
31
+ QUALITY_CMD=""
32
+ if node -e "const p=require('./package.json');process.exit(p.scripts?.typecheck?0:1)" 2>/dev/null; then
33
+ QUALITY_CMD="$RUN typecheck"
34
+ fi
35
+ if node -e "const p=require('./package.json');process.exit(p.scripts?.lint?0:1)" 2>/dev/null; then
36
+ if [ -n "$QUALITY_CMD" ]; then
37
+ QUALITY_CMD="$QUALITY_CMD && $RUN lint"
38
+ else
39
+ QUALITY_CMD="$RUN lint"
40
+ fi
41
+ fi
42
+
43
+ # If no quality scripts found, allow stop
44
+ if [ -z "$QUALITY_CMD" ]; then
45
+ exit 0
46
+ fi
47
+
48
+ # Run quality checks
49
+ if ! eval "$QUALITY_CMD" > /dev/null 2>&1; then
50
+ echo "Quality checks failed. Run '$QUALITY_CMD' to see details." >&2
51
+ exit 2
52
+ fi
53
+
54
+ exit 0