devtronic 1.2.4 → 1.2.6
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 +9 -9
- package/dist/{chunk-V4QEAL7Y.js → chunk-WM7R52TC.js} +30 -27
- package/dist/index.js +112 -61
- package/dist/{plugin-SGSFVXPA.js → plugin-KQQASBQX.js} +1 -1
- package/package.json +1 -1
- package/templates/addons/auto-devtronic/agents/afk-task-validator.md +78 -0
- package/templates/addons/auto-devtronic/agents/{quality-runner.md → quality-executor.md} +1 -1
- package/templates/addons/auto-devtronic/manifest.json +2 -1
- package/templates/addons/auto-devtronic/skills/auto-devtronic/SKILL.md +14 -14
- package/templates/addons/design-best-practices/manifest.json +2 -2
- package/templates/addons/design-best-practices/skills/{design-review → design-critique}/SKILL.md +2 -1
- package/templates/addons/design-best-practices/skills/design-harden/SKILL.md +1 -0
- package/templates/addons/design-best-practices/skills/design-init/SKILL.md +1 -0
- package/templates/addons/design-best-practices/skills/design-refine/SKILL.md +2 -0
- package/templates/addons/design-best-practices/skills/{design-system → design-tokens}/SKILL.md +3 -1
- package/templates/claude-code/.claude/rules/quality.md +4 -0
- package/templates/claude-code/.claude/skills/audit/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/backlog/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/brief/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/briefing/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/checkpoint/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/create-plan/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/create-skill/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-audit/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-define/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-ia/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-research/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-review/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-spec/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-system/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-system-audit/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-system-define/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-system-sync/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/design-wireframe/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/devtronic-help/SKILL.md +338 -0
- package/templates/claude-code/.claude/skills/execute-plan/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/generate-tests/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/handoff/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/investigate/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/learn/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/opensrc/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/post-review/SKILL.md +2 -2
- package/templates/claude-code/.claude/skills/quick/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/recap/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/research/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/scaffold/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/setup/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/spec/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/summary/SKILL.md +1 -1
- package/templates/claude-code/.claude/skills/worktree/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -20,8 +20,8 @@ The CLI analyzes your project (framework, architecture, stack) and generates per
|
|
|
20
20
|
| `doctor [--fix]` | Run health diagnostics |
|
|
21
21
|
| `list [skills\|agents]` | List installed skills and agents |
|
|
22
22
|
| `config` | View or manage project configuration |
|
|
23
|
-
| `addon
|
|
24
|
-
| `addon
|
|
23
|
+
| `addon add <name>` | Add an addon skill pack |
|
|
24
|
+
| `addon remove <name>` | Remove an addon skill pack |
|
|
25
25
|
| `addon list` | List available and installed addons |
|
|
26
26
|
| `addon sync` | Regenerate addon files for current agents |
|
|
27
27
|
| `add <ide>` | Add another IDE |
|
|
@@ -35,8 +35,8 @@ The CLI analyzes your project (framework, architecture, stack) and generates per
|
|
|
35
35
|
|
|
36
36
|
- **AGENTS.md** — Universal AI context personalized to your stack
|
|
37
37
|
- **Architecture rules** — IDE-specific format (`.claude/rules/`, `.cursor/rules/`, etc.)
|
|
38
|
-
- **Skills** (
|
|
39
|
-
- **Agents** (15) — Specialized subagents (code-reviewer, quality-runner, etc.)
|
|
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.)
|
|
40
40
|
- **Hooks** (5) — Automated workflow (lint-on-save, checkpoint, etc.)
|
|
41
41
|
- **thoughts/** — Structured directory for AI working documents
|
|
42
42
|
|
|
@@ -45,11 +45,11 @@ The CLI analyzes your project (framework, architecture, stack) and generates per
|
|
|
45
45
|
Three optional addon packs extend the core toolkit. Select them during `init` or manage them at any time:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npx devtronic addon list
|
|
49
|
-
npx devtronic addon
|
|
50
|
-
npx devtronic addon
|
|
51
|
-
npx devtronic addon
|
|
52
|
-
npx devtronic addon
|
|
48
|
+
npx devtronic addon list # See available addons and status
|
|
49
|
+
npx devtronic addon add orchestration # Install
|
|
50
|
+
npx devtronic addon add design-best-practices
|
|
51
|
+
npx devtronic addon add auto-devtronic
|
|
52
|
+
npx devtronic addon remove <name> # Uninstall
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
| Addon | Skills | Description |
|
|
@@ -14,7 +14,7 @@ var ADDONS = {
|
|
|
14
14
|
name: "design-best-practices",
|
|
15
15
|
label: "Design Best Practices",
|
|
16
16
|
description: "Frontend design quality skills: typography, color, layout, accessibility, motion, and UX writing.",
|
|
17
|
-
skills: ["design-init", "design-
|
|
17
|
+
skills: ["design-init", "design-critique", "design-refine", "design-tokens", "design-harden"],
|
|
18
18
|
agents: []
|
|
19
19
|
},
|
|
20
20
|
"auto-devtronic": {
|
|
@@ -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-
|
|
25
|
+
agents: ["issue-parser", "failure-analyst", "quality-executor"]
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
var PRESETS = {
|
|
@@ -79,7 +79,7 @@ var PRESETS = {
|
|
|
79
79
|
|
|
80
80
|
// src/utils/files.ts
|
|
81
81
|
import { createHash } from "crypto";
|
|
82
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, readdirSync } from "fs";
|
|
82
|
+
import { existsSync, lstatSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, cpSync, readdirSync } from "fs";
|
|
83
83
|
import { dirname, join, relative } from "path";
|
|
84
84
|
var MANIFEST_DIR = ".ai-template";
|
|
85
85
|
var MANIFEST_FILE = "manifest.json";
|
|
@@ -91,13 +91,15 @@ function readFile(path) {
|
|
|
91
91
|
}
|
|
92
92
|
function writeFile(path, content) {
|
|
93
93
|
const dir = dirname(path);
|
|
94
|
-
|
|
95
|
-
mkdirSync(dir, { recursive: true });
|
|
96
|
-
}
|
|
94
|
+
ensureDir(dir);
|
|
97
95
|
writeFileSync(path, content, "utf-8");
|
|
98
96
|
}
|
|
99
97
|
function ensureDir(path) {
|
|
100
98
|
if (!existsSync(path)) {
|
|
99
|
+
try {
|
|
100
|
+
if (lstatSync(path).isSymbolicLink()) unlinkSync(path);
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
101
103
|
mkdirSync(path, { recursive: true });
|
|
102
104
|
}
|
|
103
105
|
}
|
|
@@ -564,25 +566,26 @@ ${skillsSection}
|
|
|
564
566
|
`;
|
|
565
567
|
}
|
|
566
568
|
var CORE_SKILLS = [
|
|
567
|
-
{ name: "
|
|
568
|
-
{ name: "
|
|
569
|
-
{ name: "
|
|
570
|
-
{ name: "
|
|
571
|
-
{ name: "
|
|
572
|
-
{ name: "
|
|
573
|
-
{ name: "
|
|
574
|
-
{ name: "
|
|
575
|
-
{ name: "
|
|
576
|
-
{ name: "
|
|
577
|
-
{ name: "
|
|
578
|
-
{ name: "
|
|
579
|
-
{ name: "
|
|
580
|
-
{ name: "
|
|
581
|
-
{ name: "
|
|
582
|
-
{ name: "
|
|
583
|
-
{ name: "
|
|
584
|
-
{ name: "
|
|
585
|
-
{ name: "
|
|
569
|
+
{ name: "brief", desc: "Session orientation with pre-flight checks" },
|
|
570
|
+
{ name: "spec", desc: "Product specification interview (PRD)" },
|
|
571
|
+
{ name: "research", desc: "Codebase investigation (--deep, --external)" },
|
|
572
|
+
{ name: "create-plan", desc: "Phased implementation plan with task dependencies" },
|
|
573
|
+
{ name: "execute-plan", desc: "Parallel phase execution of plans" },
|
|
574
|
+
{ name: "quick", desc: "Fast ad-hoc tasks: implement, verify, commit" },
|
|
575
|
+
{ name: "generate-tests", desc: "Failing tests from spec (Tests-as-DoD)" },
|
|
576
|
+
{ name: "post-review", desc: "Pre-PR review (architecture, quality, requirements)" },
|
|
577
|
+
{ name: "audit", desc: "Codebase audit (security, complexity, architecture)" },
|
|
578
|
+
{ name: "summary", desc: "Post-change documentation" },
|
|
579
|
+
{ name: "checkpoint", desc: "Save session progress for resumption" },
|
|
580
|
+
{ name: "backlog", desc: "Issue management with BACK-### IDs" },
|
|
581
|
+
{ name: "investigate", desc: "Deep error and bug analysis" },
|
|
582
|
+
{ name: "learn", desc: "Post-task teaching breakdown" },
|
|
583
|
+
{ name: "scaffold", desc: "Create new projects from scratch" },
|
|
584
|
+
{ name: "setup", desc: "Interactive project configuration" },
|
|
585
|
+
{ name: "worktree", desc: "Git worktree management" },
|
|
586
|
+
{ name: "opensrc", desc: "Fetch npm/GitHub source for full context" },
|
|
587
|
+
{ name: "create-skill", desc: "Generate new custom skills" },
|
|
588
|
+
{ name: "devtronic-help", desc: "Discover skills, agents, addons, and workflows from the IDE" }
|
|
586
589
|
];
|
|
587
590
|
var ADDON_SKILLS = {
|
|
588
591
|
orchestration: [
|
|
@@ -592,9 +595,9 @@ var ADDON_SKILLS = {
|
|
|
592
595
|
],
|
|
593
596
|
"design-best-practices": [
|
|
594
597
|
{ name: "design-init", desc: "One-time project design context setup" },
|
|
595
|
-
{ name: "design-
|
|
598
|
+
{ name: "design-critique", desc: "Design critique with AI slop detection" },
|
|
596
599
|
{ name: "design-refine", desc: "Directional design refinement" },
|
|
597
|
-
{ name: "design-
|
|
600
|
+
{ name: "design-tokens", desc: "Design system extraction and normalization" },
|
|
598
601
|
{ name: "design-harden", desc: "Production hardening for edge cases" }
|
|
599
602
|
],
|
|
600
603
|
"auto-devtronic": [
|
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
readManifest,
|
|
25
25
|
writeFile,
|
|
26
26
|
writeManifest
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-WM7R52TC.js";
|
|
28
28
|
|
|
29
29
|
// src/index.ts
|
|
30
30
|
import { Command } from "commander";
|
|
@@ -1127,8 +1127,22 @@ function writeClaudeSettings(targetDir, settings) {
|
|
|
1127
1127
|
ensureDir(join6(targetDir, ".claude"));
|
|
1128
1128
|
writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
1129
1129
|
}
|
|
1130
|
+
var LEGACY_PLUGIN_NAMES = ["dev-ai", "ai-agentic"];
|
|
1131
|
+
var LEGACY_MARKETPLACE_NAMES = ["dev-ai-local", "ai-agentic-local"];
|
|
1130
1132
|
function registerPlugin(targetDir, pluginName, marketplaceName, marketplacePath) {
|
|
1131
1133
|
const settings = readClaudeSettings(targetDir);
|
|
1134
|
+
if (settings.extraKnownMarketplaces) {
|
|
1135
|
+
for (const legacy of LEGACY_MARKETPLACE_NAMES) {
|
|
1136
|
+
delete settings.extraKnownMarketplaces[legacy];
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
if (settings.enabledPlugins) {
|
|
1140
|
+
for (const key of Object.keys(settings.enabledPlugins)) {
|
|
1141
|
+
if (LEGACY_PLUGIN_NAMES.some((lp) => key.startsWith(`${lp}@`))) {
|
|
1142
|
+
delete settings.enabledPlugins[key];
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1132
1146
|
if (!settings.extraKnownMarketplaces) {
|
|
1133
1147
|
settings.extraKnownMarketplaces = {};
|
|
1134
1148
|
}
|
|
@@ -1469,7 +1483,7 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
1469
1483
|
p3.log.warn(`Template not found: ${templateName}`);
|
|
1470
1484
|
continue;
|
|
1471
1485
|
}
|
|
1472
|
-
const resolution = conflictResolutions.get(ide) || "
|
|
1486
|
+
const resolution = conflictResolutions.get(ide) || "replace";
|
|
1473
1487
|
const files = getAllFilesRecursive(templateDir);
|
|
1474
1488
|
const dynamicFiles = DYNAMIC_RULE_FILES[ide] || [];
|
|
1475
1489
|
for (const file of files) {
|
|
@@ -1507,7 +1521,7 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
1507
1521
|
const rulePath = dynamicFiles[0];
|
|
1508
1522
|
if (rulePath) {
|
|
1509
1523
|
const destPath = join7(targetDir, rulePath);
|
|
1510
|
-
const resolution2 = conflictResolutions.get(ide) || "
|
|
1524
|
+
const resolution2 = conflictResolutions.get(ide) || "replace";
|
|
1511
1525
|
if (fileExists(destPath) && resolution2 === "keep") {
|
|
1512
1526
|
skippedFiles.push(rulePath);
|
|
1513
1527
|
} else if (fileExists(destPath) && resolution2 === "merge") {
|
|
@@ -1588,7 +1602,7 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
1588
1602
|
p3.note(
|
|
1589
1603
|
[
|
|
1590
1604
|
` Name: ${chalk4.cyan(PLUGIN_NAME)} at ${PLUGIN_DIR}/${PLUGIN_NAME}/`,
|
|
1591
|
-
` Skills: /
|
|
1605
|
+
` Skills: /brief, /spec, /research, ... (auto-namespaced in plugin mode)`,
|
|
1592
1606
|
` Hooks: SessionStart, PostToolUse, Stop, SubagentStop, PreCompact`
|
|
1593
1607
|
].join("\n"),
|
|
1594
1608
|
"Plugin Installed"
|
|
@@ -1615,6 +1629,13 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
1615
1629
|
].join("\n"),
|
|
1616
1630
|
"Autonomous Mode"
|
|
1617
1631
|
);
|
|
1632
|
+
p3.note(
|
|
1633
|
+
[
|
|
1634
|
+
`${chalk4.dim("In Claude Code:")} ${chalk4.cyan("/devtronic-help")} ${chalk4.dim("\u2014 discover skills, agents, and workflows")}`,
|
|
1635
|
+
`${chalk4.dim("From terminal:")} ${chalk4.cyan("npx devtronic list")} ${chalk4.dim("\u2014 list installed skills and agents")}`
|
|
1636
|
+
].join("\n"),
|
|
1637
|
+
"Need Help?"
|
|
1638
|
+
);
|
|
1618
1639
|
p3.outro(chalk4.green("Setup complete!"));
|
|
1619
1640
|
} catch (err) {
|
|
1620
1641
|
spinner8.stop("Configuration failed");
|
|
@@ -1718,7 +1739,7 @@ function buildProjectConfigFromPreset(presetConfig, analysis) {
|
|
|
1718
1739
|
|
|
1719
1740
|
// src/commands/update.ts
|
|
1720
1741
|
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";
|
|
1742
|
+
import { existsSync as existsSync9, unlinkSync as unlinkSync2, lstatSync as lstatSync2, readdirSync as readdirSync2, rmdirSync as rmdirSync2, chmodSync as chmodSync2 } from "fs";
|
|
1722
1743
|
import * as p4 from "@clack/prompts";
|
|
1723
1744
|
import chalk5 from "chalk";
|
|
1724
1745
|
|
|
@@ -1795,14 +1816,18 @@ function readAddonConfig(targetDir) {
|
|
|
1795
1816
|
if (!existsSync7(configPath)) {
|
|
1796
1817
|
return { agents: ["claude"], installed: {} };
|
|
1797
1818
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1819
|
+
try {
|
|
1820
|
+
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1821
|
+
const data = raw.addons ?? raw;
|
|
1822
|
+
return {
|
|
1823
|
+
version: 1,
|
|
1824
|
+
mode: data.mode,
|
|
1825
|
+
agents: data.agents ?? ["claude"],
|
|
1826
|
+
installed: data.installed ?? {}
|
|
1827
|
+
};
|
|
1828
|
+
} catch {
|
|
1829
|
+
return { agents: ["claude"], installed: {} };
|
|
1830
|
+
}
|
|
1806
1831
|
}
|
|
1807
1832
|
function writeAddonConfig(targetDir, config) {
|
|
1808
1833
|
const configPath = getConfigPath(targetDir);
|
|
@@ -1857,6 +1882,7 @@ function registerAddonInConfig(targetDir, addonName) {
|
|
|
1857
1882
|
// src/generators/addonFiles.ts
|
|
1858
1883
|
import {
|
|
1859
1884
|
existsSync as existsSync8,
|
|
1885
|
+
lstatSync,
|
|
1860
1886
|
readFileSync as readFileSync4,
|
|
1861
1887
|
writeFileSync as writeFileSync2,
|
|
1862
1888
|
mkdirSync as mkdirSync2,
|
|
@@ -1866,7 +1892,6 @@ import {
|
|
|
1866
1892
|
rmdirSync
|
|
1867
1893
|
} from "fs";
|
|
1868
1894
|
import { join as join10, dirname as dirname5 } from "path";
|
|
1869
|
-
import { createHash } from "crypto";
|
|
1870
1895
|
var AGENT_PATHS = {
|
|
1871
1896
|
claude: ".claude",
|
|
1872
1897
|
cursor: ".cursor",
|
|
@@ -1918,11 +1943,14 @@ var RUNTIME_SPECS = {
|
|
|
1918
1943
|
})
|
|
1919
1944
|
}
|
|
1920
1945
|
};
|
|
1921
|
-
function checksum(content) {
|
|
1922
|
-
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
1923
|
-
}
|
|
1924
1946
|
function ensureDir2(dir) {
|
|
1925
|
-
if (!existsSync8(dir))
|
|
1947
|
+
if (!existsSync8(dir)) {
|
|
1948
|
+
try {
|
|
1949
|
+
if (lstatSync(dir).isSymbolicLink()) unlinkSync(dir);
|
|
1950
|
+
} catch {
|
|
1951
|
+
}
|
|
1952
|
+
mkdirSync2(dir, { recursive: true });
|
|
1953
|
+
}
|
|
1926
1954
|
}
|
|
1927
1955
|
function readManifest2(addonSourceDir) {
|
|
1928
1956
|
const manifestPath = join10(addonSourceDir, "manifest.json");
|
|
@@ -1976,7 +2004,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
1976
2004
|
ensureDir2(dirname5(destPath));
|
|
1977
2005
|
writeFileSync2(destPath, content);
|
|
1978
2006
|
result.written++;
|
|
1979
|
-
result.checksums[relPath] =
|
|
2007
|
+
result.checksums[relPath] = calculateChecksum(content);
|
|
1980
2008
|
}
|
|
1981
2009
|
continue;
|
|
1982
2010
|
}
|
|
@@ -1990,15 +2018,15 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
1990
2018
|
const existing = readFileSync4(destPath, "utf-8");
|
|
1991
2019
|
if (existing === content) {
|
|
1992
2020
|
result.skipped++;
|
|
1993
|
-
|
|
2021
|
+
} else {
|
|
2022
|
+
result.conflicts.push(relPath);
|
|
1994
2023
|
}
|
|
1995
|
-
result.skipped++;
|
|
1996
2024
|
continue;
|
|
1997
2025
|
}
|
|
1998
2026
|
ensureDir2(dirname5(destPath));
|
|
1999
2027
|
writeFileSync2(destPath, content);
|
|
2000
2028
|
result.written++;
|
|
2001
|
-
result.checksums[relPath] =
|
|
2029
|
+
result.checksums[relPath] = calculateChecksum(content);
|
|
2002
2030
|
}
|
|
2003
2031
|
for (const agentName of manifest.files.agents ?? []) {
|
|
2004
2032
|
const agentFile = join10(addonSourceDir, "agents", `${agentName}.md`);
|
|
@@ -2009,7 +2037,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2009
2037
|
ensureDir2(dirname5(destPath));
|
|
2010
2038
|
writeFileSync2(destPath, content);
|
|
2011
2039
|
result.written++;
|
|
2012
|
-
result.checksums[`agents/${agentName}.md`] =
|
|
2040
|
+
result.checksums[`agents/${agentName}.md`] = calculateChecksum(content);
|
|
2013
2041
|
} else {
|
|
2014
2042
|
result.skipped++;
|
|
2015
2043
|
}
|
|
@@ -2024,7 +2052,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2024
2052
|
ensureDir2(dirname5(destPath));
|
|
2025
2053
|
writeFileSync2(destPath, content);
|
|
2026
2054
|
result.written++;
|
|
2027
|
-
result.checksums[`rules/${rule}`] =
|
|
2055
|
+
result.checksums[`rules/${rule}`] = calculateChecksum(content);
|
|
2028
2056
|
} else {
|
|
2029
2057
|
result.skipped++;
|
|
2030
2058
|
}
|
|
@@ -2040,7 +2068,7 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2040
2068
|
ensureDir2(dirname5(destPath));
|
|
2041
2069
|
writeFileSync2(destPath, content);
|
|
2042
2070
|
result.written++;
|
|
2043
|
-
result.checksums[relPath] =
|
|
2071
|
+
result.checksums[relPath] = calculateChecksum(content);
|
|
2044
2072
|
} else {
|
|
2045
2073
|
result.skipped++;
|
|
2046
2074
|
}
|
|
@@ -2120,8 +2148,10 @@ function removeAddonFiles(projectDir, addonName, agents, addonSourceDir) {
|
|
|
2120
2148
|
}
|
|
2121
2149
|
}
|
|
2122
2150
|
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2151
|
+
if (manifest.attribution) {
|
|
2152
|
+
const noticePath = join10(projectDir, "NOTICE.md");
|
|
2153
|
+
if (existsSync8(noticePath)) unlinkSync(noticePath);
|
|
2154
|
+
}
|
|
2125
2155
|
}
|
|
2126
2156
|
function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
2127
2157
|
const fileMap = buildFileMap(addonSourceDir);
|
|
@@ -2150,7 +2180,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2150
2180
|
continue;
|
|
2151
2181
|
}
|
|
2152
2182
|
const existing = readFileSync4(destPath, "utf-8");
|
|
2153
|
-
const existingChecksum =
|
|
2183
|
+
const existingChecksum = calculateChecksum(existing);
|
|
2154
2184
|
const originalChecksum = installedChecksums[relPath];
|
|
2155
2185
|
if (existing === newContent) {
|
|
2156
2186
|
result.skipped++;
|
|
@@ -2178,7 +2208,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2178
2208
|
continue;
|
|
2179
2209
|
}
|
|
2180
2210
|
const existing = readFileSync4(destPath, "utf-8");
|
|
2181
|
-
const existingChecksum =
|
|
2211
|
+
const existingChecksum = calculateChecksum(existing);
|
|
2182
2212
|
const originalChecksum = installedChecksums[relPath];
|
|
2183
2213
|
if (existing === newContent) {
|
|
2184
2214
|
result.skipped++;
|
|
@@ -2204,7 +2234,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2204
2234
|
continue;
|
|
2205
2235
|
}
|
|
2206
2236
|
const existing = readFileSync4(destPath, "utf-8");
|
|
2207
|
-
const existingChecksum =
|
|
2237
|
+
const existingChecksum = calculateChecksum(existing);
|
|
2208
2238
|
const originalChecksum = installedChecksums[relPath];
|
|
2209
2239
|
if (existing === newContent) {
|
|
2210
2240
|
result.skipped++;
|
|
@@ -2231,7 +2261,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2231
2261
|
continue;
|
|
2232
2262
|
}
|
|
2233
2263
|
const existing = readFileSync4(destPath, "utf-8");
|
|
2234
|
-
const existingChecksum =
|
|
2264
|
+
const existingChecksum = calculateChecksum(existing);
|
|
2235
2265
|
const originalChecksum = installedChecksums[relPath];
|
|
2236
2266
|
if (existing === newContent) {
|
|
2237
2267
|
result.skipped++;
|
|
@@ -2258,7 +2288,7 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2258
2288
|
continue;
|
|
2259
2289
|
}
|
|
2260
2290
|
const existing = readFileSync4(destPath, "utf-8");
|
|
2261
|
-
const existingChecksum =
|
|
2291
|
+
const existingChecksum = calculateChecksum(existing);
|
|
2262
2292
|
const originalChecksum = installedChecksums[relPath];
|
|
2263
2293
|
if (existing === newContent) {
|
|
2264
2294
|
result.skipped++;
|
|
@@ -2276,20 +2306,24 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2276
2306
|
}
|
|
2277
2307
|
function detectModifiedAddonFiles(projectDir, addonName) {
|
|
2278
2308
|
let installedChecksums = {};
|
|
2309
|
+
let agents = ["claude"];
|
|
2279
2310
|
try {
|
|
2280
2311
|
const config = readAddonConfig(projectDir);
|
|
2281
2312
|
const installed = config.installed?.[addonName];
|
|
2282
2313
|
if (!installed?.checksums) return [];
|
|
2283
2314
|
installedChecksums = installed.checksums;
|
|
2315
|
+
agents = config.agents ?? ["claude"];
|
|
2284
2316
|
} catch {
|
|
2285
2317
|
return [];
|
|
2286
2318
|
}
|
|
2287
2319
|
const modified = [];
|
|
2288
|
-
for (const
|
|
2320
|
+
for (const agent of agents) {
|
|
2321
|
+
const spec = RUNTIME_SPECS[agent];
|
|
2322
|
+
const baseDir = spec?.baseDir ?? AGENT_PATHS[agent] ?? `.${agent}`;
|
|
2289
2323
|
for (const [relPath, originalHash] of Object.entries(installedChecksums)) {
|
|
2290
|
-
const absPath = join10(projectDir,
|
|
2324
|
+
const absPath = join10(projectDir, baseDir, relPath);
|
|
2291
2325
|
if (!existsSync8(absPath)) continue;
|
|
2292
|
-
const current =
|
|
2326
|
+
const current = calculateChecksum(readFileSync4(absPath, "utf-8"));
|
|
2293
2327
|
if (current !== originalHash) {
|
|
2294
2328
|
modified.push(relPath);
|
|
2295
2329
|
}
|
|
@@ -2335,7 +2369,7 @@ async function updateCommand(options) {
|
|
|
2335
2369
|
const shouldMigrate = manifest.selectedIDEs.includes("claude-code") && !manifest.installMode && hasStandaloneSkills(manifest);
|
|
2336
2370
|
if (shouldMigrate) {
|
|
2337
2371
|
p4.note(
|
|
2338
|
-
"Claude Code skills/agents detected as standalone.\nThe new version uses plugin mode
|
|
2372
|
+
"Claude Code skills/agents detected as standalone.\nThe new version uses plugin mode. Skills are auto-namespaced in plugin mode.",
|
|
2339
2373
|
"Migration Available"
|
|
2340
2374
|
);
|
|
2341
2375
|
if (!options.check) {
|
|
@@ -2602,7 +2636,7 @@ async function updateCommand(options) {
|
|
|
2602
2636
|
}
|
|
2603
2637
|
const claudeMdPath = join11(targetDir, "CLAUDE.md");
|
|
2604
2638
|
if (fileExists(claudeMdPath)) {
|
|
2605
|
-
const stat =
|
|
2639
|
+
const stat = lstatSync2(claudeMdPath);
|
|
2606
2640
|
if (stat.isSymbolicLink()) {
|
|
2607
2641
|
const content = readFile(claudeMdPath);
|
|
2608
2642
|
unlinkSync2(claudeMdPath);
|
|
@@ -2629,6 +2663,7 @@ async function updateCommand(options) {
|
|
|
2629
2663
|
syncSpinner.start("Updating enabled addon files...");
|
|
2630
2664
|
let totalUpdated = 0;
|
|
2631
2665
|
for (const name of enabledAddons) {
|
|
2666
|
+
if (name === "orchestration") continue;
|
|
2632
2667
|
const addonSourceDir = getAddonSourceDir(name);
|
|
2633
2668
|
const result = syncAddonFiles(targetDir, addonSourceDir, addonConfig.agents);
|
|
2634
2669
|
totalUpdated += (result.updated ?? 0) + result.written;
|
|
@@ -2754,7 +2789,7 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
|
|
|
2754
2789
|
}
|
|
2755
2790
|
const claudeMdPath = join11(targetDir, "CLAUDE.md");
|
|
2756
2791
|
if (fileExists(claudeMdPath)) {
|
|
2757
|
-
const stat =
|
|
2792
|
+
const stat = lstatSync2(claudeMdPath);
|
|
2758
2793
|
if (stat.isSymbolicLink()) {
|
|
2759
2794
|
const content = readFile(claudeMdPath);
|
|
2760
2795
|
unlinkSync2(claudeMdPath);
|
|
@@ -2889,7 +2924,7 @@ async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
|
|
|
2889
2924
|
}
|
|
2890
2925
|
p4.note(
|
|
2891
2926
|
` Plugin: ${chalk5.cyan("devtronic")} at .claude-plugins/devtronic/
|
|
2892
|
-
Skills: /
|
|
2927
|
+
Skills: /brief, /spec, ... (auto-namespaced in plugin mode)
|
|
2893
2928
|
Hooks: 5 workflow hooks enabled`,
|
|
2894
2929
|
"Plugin Generated"
|
|
2895
2930
|
);
|
|
@@ -2920,7 +2955,7 @@ function cleanEmptyDirs(dirPath) {
|
|
|
2920
2955
|
const entries = readdirSync2(dirPath);
|
|
2921
2956
|
for (const entry of entries) {
|
|
2922
2957
|
const fullPath = join11(dirPath, entry);
|
|
2923
|
-
if (existsSync9(fullPath) &&
|
|
2958
|
+
if (existsSync9(fullPath) && lstatSync2(fullPath).isDirectory()) {
|
|
2924
2959
|
cleanEmptyDirs(fullPath);
|
|
2925
2960
|
}
|
|
2926
2961
|
}
|
|
@@ -3441,7 +3476,7 @@ Valid options:
|
|
|
3441
3476
|
if (!manifest.selectedIDEs.includes("claude-code") || manifest.installMode !== "plugin") {
|
|
3442
3477
|
p8.log.warn("Plugin regeneration only applies to Claude Code in plugin mode. Skipping.");
|
|
3443
3478
|
} else {
|
|
3444
|
-
const { generatePlugin: generatePlugin2 } = await import("./plugin-
|
|
3479
|
+
const { generatePlugin: generatePlugin2 } = await import("./plugin-KQQASBQX.js");
|
|
3445
3480
|
const pluginResult = generatePlugin2(
|
|
3446
3481
|
targetDir,
|
|
3447
3482
|
TEMPLATES_DIR2,
|
|
@@ -4407,7 +4442,7 @@ function readdirSafe(dir) {
|
|
|
4407
4442
|
|
|
4408
4443
|
// src/commands/addon.ts
|
|
4409
4444
|
import { resolve as resolve14, join as join20, dirname as dirname10 } from "path";
|
|
4410
|
-
import { existsSync as existsSync17, unlinkSync as unlinkSync3, rmSync as rmSync3
|
|
4445
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync3, rmSync as rmSync3 } from "fs";
|
|
4411
4446
|
import * as p14 from "@clack/prompts";
|
|
4412
4447
|
import chalk15 from "chalk";
|
|
4413
4448
|
function isFileBasedAddon(addonName) {
|
|
@@ -4425,8 +4460,8 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
4425
4460
|
}
|
|
4426
4461
|
const typedName = addonName;
|
|
4427
4462
|
const canonicalAction = action === "enable" ? "add" : action === "disable" ? "remove" : action;
|
|
4428
|
-
if (action === "
|
|
4429
|
-
const canonical = action === "
|
|
4463
|
+
if (action === "enable" || action === "disable") {
|
|
4464
|
+
const canonical = action === "enable" ? "add" : "remove";
|
|
4430
4465
|
p14.log.warn(
|
|
4431
4466
|
`"addon ${action}" is deprecated. Use "addon ${canonical}" instead.`
|
|
4432
4467
|
);
|
|
@@ -4473,12 +4508,12 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4473
4508
|
[
|
|
4474
4509
|
` ${chalk15.dim("Name:")} ${addon.label}`,
|
|
4475
4510
|
` ${chalk15.dim("Description:")} ${addon.description}`,
|
|
4476
|
-
` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.cyan(
|
|
4511
|
+
` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.cyan(`/${s}`)).join(", ")}`,
|
|
4477
4512
|
` ${chalk15.dim("Subagents:")} ${addon.agents.length ? addon.agents.join(", ") : chalk15.dim("\u2014")}`
|
|
4478
4513
|
].join("\n"),
|
|
4479
4514
|
"Adding addon"
|
|
4480
4515
|
);
|
|
4481
|
-
const confirmed = await p14.confirm({ message:
|
|
4516
|
+
const confirmed = await p14.confirm({ message: `Add ${addonName}?` });
|
|
4482
4517
|
if (p14.isCancel(confirmed) || !confirmed) {
|
|
4483
4518
|
p14.cancel("Addon installation cancelled.");
|
|
4484
4519
|
process.exit(0);
|
|
@@ -4514,7 +4549,7 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4514
4549
|
writeManifest(targetDir, manifest);
|
|
4515
4550
|
spinner8.stop(`${symbols.pass} ${addon.label} added`);
|
|
4516
4551
|
p14.note(
|
|
4517
|
-
addon.skills.map((s) => ` ${chalk15.cyan(
|
|
4552
|
+
addon.skills.map((s) => ` ${chalk15.cyan(`/${s}`)}`).join("\n"),
|
|
4518
4553
|
"New skills available"
|
|
4519
4554
|
);
|
|
4520
4555
|
p14.outro("Done. Restart Claude Code to load the new skills.");
|
|
@@ -4531,12 +4566,12 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4531
4566
|
[
|
|
4532
4567
|
` ${chalk15.dim("Name:")} ${addon.label}`,
|
|
4533
4568
|
` ${chalk15.dim("Description:")} ${addon.description}`,
|
|
4534
|
-
` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.dim(
|
|
4569
|
+
` ${chalk15.dim("Skills:")} ${addon.skills.map((s) => chalk15.dim(`/${s}`)).join(", ")}`,
|
|
4535
4570
|
` ${chalk15.dim("Subagents:")} ${addon.agents.length ? addon.agents.join(", ") : chalk15.dim("\u2014")}`
|
|
4536
4571
|
].join("\n"),
|
|
4537
4572
|
"Removing addon"
|
|
4538
4573
|
);
|
|
4539
|
-
const confirmed = await p14.confirm({ message:
|
|
4574
|
+
const confirmed = await p14.confirm({ message: `Remove ${addonName}?` });
|
|
4540
4575
|
if (p14.isCancel(confirmed) || !confirmed) {
|
|
4541
4576
|
p14.cancel("Addon removal cancelled.");
|
|
4542
4577
|
process.exit(0);
|
|
@@ -4593,7 +4628,7 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4593
4628
|
writeManifest(targetDir, manifest);
|
|
4594
4629
|
spinner8.stop(`${symbols.pass} ${addon.label} removed`);
|
|
4595
4630
|
p14.note(
|
|
4596
|
-
addon.skills.map((s) => ` ${chalk15.dim(
|
|
4631
|
+
addon.skills.map((s) => ` ${chalk15.dim(`/${s}`)}`).join("\n"),
|
|
4597
4632
|
"Skills removed"
|
|
4598
4633
|
);
|
|
4599
4634
|
p14.outro("Done. Restart Claude Code to apply the changes.");
|
|
@@ -4615,7 +4650,7 @@ async function addFileBasedAddon(targetDir, addonName, _options) {
|
|
|
4615
4650
|
].join("\n"),
|
|
4616
4651
|
"Adding addon"
|
|
4617
4652
|
);
|
|
4618
|
-
const confirmed = await p14.confirm({ message:
|
|
4653
|
+
const confirmed = await p14.confirm({ message: `Add ${addonName}?` });
|
|
4619
4654
|
if (p14.isCancel(confirmed) || !confirmed) {
|
|
4620
4655
|
p14.cancel("Addon installation cancelled.");
|
|
4621
4656
|
process.exit(0);
|
|
@@ -4624,16 +4659,14 @@ async function addFileBasedAddon(targetDir, addonName, _options) {
|
|
|
4624
4659
|
spinner8.start(`Adding ${addon.label}...`);
|
|
4625
4660
|
const addonSourceDir = getAddonSourceDir(addonName);
|
|
4626
4661
|
const result = generateAddonFiles(targetDir, addonSourceDir, config.agents);
|
|
4627
|
-
const
|
|
4628
|
-
readFileSync6(join20(addonSourceDir, "manifest.json"), "utf-8")
|
|
4629
|
-
);
|
|
4662
|
+
const addonMeta = getAddonManifest(addonName);
|
|
4630
4663
|
const fileList = [
|
|
4631
|
-
...(
|
|
4632
|
-
...(
|
|
4633
|
-
...(
|
|
4664
|
+
...(addonMeta.files.skills ?? []).map((s) => `skills/${s}`),
|
|
4665
|
+
...(addonMeta.files.agents ?? []).map((a) => `agents/${a}.md`),
|
|
4666
|
+
...(addonMeta.files.rules ?? []).map((r) => `rules/${r}`)
|
|
4634
4667
|
];
|
|
4635
4668
|
writeAddonToConfig(targetDir, addonName, {
|
|
4636
|
-
version:
|
|
4669
|
+
version: addonMeta.version,
|
|
4637
4670
|
files: fileList,
|
|
4638
4671
|
checksums: result.checksums ?? {}
|
|
4639
4672
|
});
|
|
@@ -4706,7 +4739,24 @@ async function addonSyncCommand(options) {
|
|
|
4706
4739
|
const targetDir = resolve14(options.path || ".");
|
|
4707
4740
|
p14.intro(introTitle("Addon Sync"));
|
|
4708
4741
|
const config = readAddonConfig(targetDir);
|
|
4709
|
-
const
|
|
4742
|
+
const manifest = readManifest(targetDir);
|
|
4743
|
+
const manifestAddons = manifest?.projectConfig?.enabledAddons ?? [];
|
|
4744
|
+
for (const name of manifestAddons) {
|
|
4745
|
+
if (!config.installed[name] && isFileBasedAddon(name)) {
|
|
4746
|
+
const addonMeta = getAddonManifest(name);
|
|
4747
|
+
const fileList = [
|
|
4748
|
+
...(addonMeta.files.skills ?? []).map((s) => `skills/${s}`),
|
|
4749
|
+
...(addonMeta.files.agents ?? []).map((a) => `agents/${a}.md`),
|
|
4750
|
+
...(addonMeta.files.rules ?? []).map((r) => `rules/${r}`)
|
|
4751
|
+
];
|
|
4752
|
+
writeAddonToConfig(targetDir, name, {
|
|
4753
|
+
version: addonMeta.version,
|
|
4754
|
+
files: fileList
|
|
4755
|
+
});
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4758
|
+
const freshConfig = readAddonConfig(targetDir);
|
|
4759
|
+
const installedNames = Object.keys(freshConfig.installed);
|
|
4710
4760
|
if (installedNames.length === 0) {
|
|
4711
4761
|
p14.log.info("No addons installed. Nothing to sync.");
|
|
4712
4762
|
p14.outro("");
|
|
@@ -4717,8 +4767,9 @@ async function addonSyncCommand(options) {
|
|
|
4717
4767
|
let totalWritten = 0;
|
|
4718
4768
|
let totalConflicts = [];
|
|
4719
4769
|
for (const name of installedNames) {
|
|
4770
|
+
if (!isFileBasedAddon(name)) continue;
|
|
4720
4771
|
const addonSourceDir = getAddonSourceDir(name);
|
|
4721
|
-
const result = syncAddonFiles(targetDir, addonSourceDir,
|
|
4772
|
+
const result = syncAddonFiles(targetDir, addonSourceDir, freshConfig.agents);
|
|
4722
4773
|
totalWritten += result.written + (result.updated ?? 0);
|
|
4723
4774
|
totalConflicts = totalConflicts.concat(result.conflicts);
|
|
4724
4775
|
}
|
|
@@ -4850,7 +4901,7 @@ program.command("uninstall").description("Remove devtronic from your project").o
|
|
|
4850
4901
|
});
|
|
4851
4902
|
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) => {
|
|
4852
4903
|
if (!["afk", "hitl", "show"].includes(mode)) {
|
|
4853
|
-
|
|
4904
|
+
p16.cancel(`Invalid mode: "${mode}". Valid values: afk, hitl, show`);
|
|
4854
4905
|
process.exit(1);
|
|
4855
4906
|
}
|
|
4856
4907
|
await modeCommand(mode, { path: options.path });
|
package/package.json
CHANGED