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.
- package/README.md +3 -3
- package/dist/{chunk-V4QEAL7Y.js → chunk-YBT2XOOQ.js} +38 -38
- package/dist/index.js +352 -120
- package/dist/{plugin-SGSFVXPA.js → plugin-XSNKI5VI.js} +5 -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/templates/marketplace/auto-lint.sh +26 -0
- package/templates/marketplace/checkpoint.sh +39 -0
- package/templates/marketplace/hooks.json +75 -0
- 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-
|
|
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
|
|
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
|
-
|
|
1491
|
+
registerGitHubPlugin(
|
|
1444
1492
|
targetDir,
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
analysis.packageManager
|
|
1493
|
+
PLUGIN_NAME,
|
|
1494
|
+
GITHUB_MARKETPLACE_NAME,
|
|
1495
|
+
GITHUB_MARKETPLACE_REPO
|
|
1449
1496
|
);
|
|
1450
|
-
|
|
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
|
|
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
|
-
`
|
|
1591
|
-
`
|
|
1592
|
-
`
|
|
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
|
-
"
|
|
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,
|
|
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))
|
|
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] =
|
|
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] =
|
|
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`] =
|
|
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}`] =
|
|
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] =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
2850
|
-
|
|
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
|
-
|
|
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
|
-
`
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
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
|
|
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) &&
|
|
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 (
|
|
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-
|
|
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
|
|
3521
|
-
if (existsSync13(
|
|
3522
|
-
skillCount = readdirSync3(
|
|
3523
|
-
(e) => e.isDirectory()
|
|
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
|
-
|
|
3528
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
4173
|
-
import { resolve as resolve13, join as join19, dirname as
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4509
|
+
rmSync3(filePath, { force: true });
|
|
4309
4510
|
removed.push(file);
|
|
4310
|
-
cleanEmptyParents(targetDir,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4406
|
-
cleanEmptyParents(targetDir,
|
|
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
|
|
4421
|
-
import { existsSync as existsSync17, unlinkSync as unlinkSync3, rmSync as
|
|
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"
|
|
4461
|
-
p14.log.warn("
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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 });
|