devtronic 1.2.3 → 1.2.5
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 +7 -7
- package/dist/index.js +206 -174
- package/package.json +1 -1
- package/templates/addons/auto-devtronic/agents/failure-analyst.md +156 -0
- package/templates/addons/auto-devtronic/agents/issue-parser.md +145 -0
- package/templates/addons/auto-devtronic/agents/quality-runner.md +85 -0
- package/templates/addons/auto-devtronic/manifest.json +16 -0
- package/templates/addons/auto-devtronic/skills/auto-devtronic/SKILL.md +611 -0
- package/templates/addons/design-best-practices/manifest.json +28 -0
- package/templates/addons/design-best-practices/reference/color-and-contrast.md +146 -0
- package/templates/addons/design-best-practices/reference/interaction-design.md +208 -0
- package/templates/addons/design-best-practices/reference/motion-design.md +167 -0
- package/templates/addons/design-best-practices/reference/responsive-design.md +180 -0
- package/templates/addons/design-best-practices/reference/spatial-design.md +161 -0
- package/templates/addons/design-best-practices/reference/typography.md +136 -0
- package/templates/addons/design-best-practices/reference/ux-writing.md +190 -0
- package/templates/addons/design-best-practices/rules/design-quality.md +53 -0
- package/templates/addons/design-best-practices/skills/design-harden/SKILL.md +142 -0
- package/templates/addons/design-best-practices/skills/design-init/SKILL.md +95 -0
- package/templates/addons/design-best-practices/skills/design-refine/SKILL.md +124 -0
- package/templates/addons/design-best-practices/skills/design-review/SKILL.md +107 -0
- package/templates/addons/design-best-practices/skills/design-system/SKILL.md +125 -0
package/dist/index.js
CHANGED
|
@@ -1469,7 +1469,7 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
1469
1469
|
p3.log.warn(`Template not found: ${templateName}`);
|
|
1470
1470
|
continue;
|
|
1471
1471
|
}
|
|
1472
|
-
const resolution = conflictResolutions.get(ide) || "
|
|
1472
|
+
const resolution = conflictResolutions.get(ide) || "replace";
|
|
1473
1473
|
const files = getAllFilesRecursive(templateDir);
|
|
1474
1474
|
const dynamicFiles = DYNAMIC_RULE_FILES[ide] || [];
|
|
1475
1475
|
for (const file of files) {
|
|
@@ -1507,7 +1507,7 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
1507
1507
|
const rulePath = dynamicFiles[0];
|
|
1508
1508
|
if (rulePath) {
|
|
1509
1509
|
const destPath = join7(targetDir, rulePath);
|
|
1510
|
-
const resolution2 = conflictResolutions.get(ide) || "
|
|
1510
|
+
const resolution2 = conflictResolutions.get(ide) || "replace";
|
|
1511
1511
|
if (fileExists(destPath) && resolution2 === "keep") {
|
|
1512
1512
|
skippedFiles.push(rulePath);
|
|
1513
1513
|
} else if (fileExists(destPath) && resolution2 === "merge") {
|
|
@@ -1717,8 +1717,8 @@ function buildProjectConfigFromPreset(presetConfig, analysis) {
|
|
|
1717
1717
|
}
|
|
1718
1718
|
|
|
1719
1719
|
// src/commands/update.ts
|
|
1720
|
-
import { resolve as
|
|
1721
|
-
import { existsSync as
|
|
1720
|
+
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";
|
|
1722
1722
|
import * as p4 from "@clack/prompts";
|
|
1723
1723
|
import chalk5 from "chalk";
|
|
1724
1724
|
|
|
@@ -1747,15 +1747,20 @@ var REMOVED_FILES = {
|
|
|
1747
1747
|
};
|
|
1748
1748
|
|
|
1749
1749
|
// src/utils/addonConfig.ts
|
|
1750
|
-
import { existsSync as
|
|
1751
|
-
import { join as join9, dirname as
|
|
1750
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync, mkdirSync, renameSync } from "fs";
|
|
1751
|
+
import { join as join9, dirname as dirname4 } from "path";
|
|
1752
1752
|
|
|
1753
1753
|
// src/addons/registry.ts
|
|
1754
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
1755
|
-
import { join as join8 } from "path";
|
|
1754
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
|
|
1755
|
+
import { join as join8, dirname as dirname3, resolve as resolve3 } from "path";
|
|
1756
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1757
|
+
function getAddonsDir() {
|
|
1758
|
+
const __dirname3 = dirname3(fileURLToPath3(import.meta.url));
|
|
1759
|
+
const templatesDir = existsSync6(resolve3(__dirname3, "../templates")) ? resolve3(__dirname3, "../templates") : resolve3(__dirname3, "../../templates");
|
|
1760
|
+
return join8(templatesDir, "addons");
|
|
1761
|
+
}
|
|
1756
1762
|
function getAddonSourceDir(name) {
|
|
1757
|
-
|
|
1758
|
-
return join8(addonsDir, name);
|
|
1763
|
+
return join8(getAddonsDir(), name);
|
|
1759
1764
|
}
|
|
1760
1765
|
function getAddonManifest(name) {
|
|
1761
1766
|
const sourceDir = getAddonSourceDir(name);
|
|
@@ -1783,25 +1788,29 @@ function getLegacyConfigPath(targetDir) {
|
|
|
1783
1788
|
function readAddonConfig(targetDir) {
|
|
1784
1789
|
const configPath = getConfigPath(targetDir);
|
|
1785
1790
|
const legacyPath = getLegacyConfigPath(targetDir);
|
|
1786
|
-
if (!
|
|
1787
|
-
mkdirSync(
|
|
1791
|
+
if (!existsSync7(configPath) && existsSync7(legacyPath)) {
|
|
1792
|
+
mkdirSync(dirname4(configPath), { recursive: true });
|
|
1788
1793
|
renameSync(legacyPath, configPath);
|
|
1789
1794
|
}
|
|
1790
|
-
if (!
|
|
1795
|
+
if (!existsSync7(configPath)) {
|
|
1796
|
+
return { agents: ["claude"], installed: {} };
|
|
1797
|
+
}
|
|
1798
|
+
try {
|
|
1799
|
+
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1800
|
+
const data = raw.addons ?? raw;
|
|
1801
|
+
return {
|
|
1802
|
+
version: 1,
|
|
1803
|
+
mode: data.mode,
|
|
1804
|
+
agents: data.agents ?? ["claude"],
|
|
1805
|
+
installed: data.installed ?? {}
|
|
1806
|
+
};
|
|
1807
|
+
} catch {
|
|
1791
1808
|
return { agents: ["claude"], installed: {} };
|
|
1792
1809
|
}
|
|
1793
|
-
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1794
|
-
const data = raw.addons ?? raw;
|
|
1795
|
-
return {
|
|
1796
|
-
version: 1,
|
|
1797
|
-
mode: data.mode,
|
|
1798
|
-
agents: data.agents ?? ["claude"],
|
|
1799
|
-
installed: data.installed ?? {}
|
|
1800
|
-
};
|
|
1801
1810
|
}
|
|
1802
1811
|
function writeAddonConfig(targetDir, config) {
|
|
1803
1812
|
const configPath = getConfigPath(targetDir);
|
|
1804
|
-
mkdirSync(
|
|
1813
|
+
mkdirSync(dirname4(configPath), { recursive: true });
|
|
1805
1814
|
const payload = { version: 1, ...config };
|
|
1806
1815
|
writeFileSync(configPath, JSON.stringify(payload, null, 2) + "\n");
|
|
1807
1816
|
}
|
|
@@ -1830,7 +1839,7 @@ function detectOrphanedAddonFiles(targetDir) {
|
|
|
1830
1839
|
for (const addon of getAvailableAddons()) {
|
|
1831
1840
|
if (config.installed[addon.name]) continue;
|
|
1832
1841
|
const skillDir = join9(targetDir, ".claude", "skills", addon.name);
|
|
1833
|
-
if (
|
|
1842
|
+
if (existsSync7(skillDir)) {
|
|
1834
1843
|
orphaned.push(addon.name);
|
|
1835
1844
|
}
|
|
1836
1845
|
}
|
|
@@ -1851,7 +1860,7 @@ function registerAddonInConfig(targetDir, addonName) {
|
|
|
1851
1860
|
|
|
1852
1861
|
// src/generators/addonFiles.ts
|
|
1853
1862
|
import {
|
|
1854
|
-
existsSync as
|
|
1863
|
+
existsSync as existsSync8,
|
|
1855
1864
|
readFileSync as readFileSync4,
|
|
1856
1865
|
writeFileSync as writeFileSync2,
|
|
1857
1866
|
mkdirSync as mkdirSync2,
|
|
@@ -1860,7 +1869,7 @@ import {
|
|
|
1860
1869
|
readdirSync,
|
|
1861
1870
|
rmdirSync
|
|
1862
1871
|
} from "fs";
|
|
1863
|
-
import { join as join10, dirname as
|
|
1872
|
+
import { join as join10, dirname as dirname5 } from "path";
|
|
1864
1873
|
import { createHash } from "crypto";
|
|
1865
1874
|
var AGENT_PATHS = {
|
|
1866
1875
|
claude: ".claude",
|
|
@@ -1917,7 +1926,7 @@ function checksum(content) {
|
|
|
1917
1926
|
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
1918
1927
|
}
|
|
1919
1928
|
function ensureDir2(dir) {
|
|
1920
|
-
if (!
|
|
1929
|
+
if (!existsSync8(dir)) mkdirSync2(dir, { recursive: true });
|
|
1921
1930
|
}
|
|
1922
1931
|
function readManifest2(addonSourceDir) {
|
|
1923
1932
|
const manifestPath = join10(addonSourceDir, "manifest.json");
|
|
@@ -1928,27 +1937,27 @@ function buildFileMap(addonSourceDir) {
|
|
|
1928
1937
|
const files = /* @__PURE__ */ new Map();
|
|
1929
1938
|
for (const skill of manifest.files.skills ?? []) {
|
|
1930
1939
|
const skillDir = join10(addonSourceDir, "skills", skill);
|
|
1931
|
-
if (!
|
|
1940
|
+
if (!existsSync8(skillDir)) continue;
|
|
1932
1941
|
const skillFile = join10(skillDir, "SKILL.md");
|
|
1933
|
-
if (
|
|
1942
|
+
if (existsSync8(skillFile)) {
|
|
1934
1943
|
files.set(`skills/${skill}/SKILL.md`, readFileSync4(skillFile, "utf-8"));
|
|
1935
1944
|
}
|
|
1936
1945
|
}
|
|
1937
1946
|
for (const agent of manifest.files.agents ?? []) {
|
|
1938
1947
|
const agentFile = join10(addonSourceDir, "agents", `${agent}.md`);
|
|
1939
|
-
if (
|
|
1948
|
+
if (existsSync8(agentFile)) {
|
|
1940
1949
|
files.set(`agents/${agent}.md`, readFileSync4(agentFile, "utf-8"));
|
|
1941
1950
|
}
|
|
1942
1951
|
}
|
|
1943
1952
|
for (const ref of manifest.files.reference ?? []) {
|
|
1944
1953
|
const refPath = join10(addonSourceDir, "reference", ref);
|
|
1945
|
-
if (
|
|
1954
|
+
if (existsSync8(refPath)) {
|
|
1946
1955
|
files.set(`skills/design-harden/reference/${ref}`, readFileSync4(refPath, "utf-8"));
|
|
1947
1956
|
}
|
|
1948
1957
|
}
|
|
1949
1958
|
for (const rule of manifest.files.rules ?? []) {
|
|
1950
1959
|
const rulePath = join10(addonSourceDir, "rules", rule);
|
|
1951
|
-
if (
|
|
1960
|
+
if (existsSync8(rulePath)) {
|
|
1952
1961
|
files.set(`rules/${rule}`, readFileSync4(rulePath, "utf-8"));
|
|
1953
1962
|
}
|
|
1954
1963
|
}
|
|
@@ -1964,11 +1973,11 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
1964
1973
|
const basePath = AGENT_PATHS[agent] ?? `.${agent}`;
|
|
1965
1974
|
for (const [relPath, content] of fileMap) {
|
|
1966
1975
|
const destPath = join10(projectDir, basePath, relPath);
|
|
1967
|
-
if (
|
|
1976
|
+
if (existsSync8(destPath)) {
|
|
1968
1977
|
result.skipped++;
|
|
1969
1978
|
continue;
|
|
1970
1979
|
}
|
|
1971
|
-
ensureDir2(
|
|
1980
|
+
ensureDir2(dirname5(destPath));
|
|
1972
1981
|
writeFileSync2(destPath, content);
|
|
1973
1982
|
result.written++;
|
|
1974
1983
|
result.checksums[relPath] = checksum(content);
|
|
@@ -1977,31 +1986,31 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
1977
1986
|
}
|
|
1978
1987
|
for (const skillName of manifest.files.skills ?? []) {
|
|
1979
1988
|
const skillFile = join10(addonSourceDir, "skills", skillName, "SKILL.md");
|
|
1980
|
-
if (!
|
|
1989
|
+
if (!existsSync8(skillFile)) continue;
|
|
1981
1990
|
const rawContent = readFileSync4(skillFile, "utf-8");
|
|
1982
1991
|
const { relPath, content } = spec.skillAdapter(skillName, rawContent);
|
|
1983
1992
|
const destPath = join10(projectDir, spec.baseDir, relPath);
|
|
1984
|
-
if (
|
|
1993
|
+
if (existsSync8(destPath)) {
|
|
1985
1994
|
const existing = readFileSync4(destPath, "utf-8");
|
|
1986
1995
|
if (existing === content) {
|
|
1987
1996
|
result.skipped++;
|
|
1988
|
-
|
|
1997
|
+
} else {
|
|
1998
|
+
result.conflicts.push(relPath);
|
|
1989
1999
|
}
|
|
1990
|
-
result.skipped++;
|
|
1991
2000
|
continue;
|
|
1992
2001
|
}
|
|
1993
|
-
ensureDir2(
|
|
2002
|
+
ensureDir2(dirname5(destPath));
|
|
1994
2003
|
writeFileSync2(destPath, content);
|
|
1995
2004
|
result.written++;
|
|
1996
2005
|
result.checksums[relPath] = checksum(content);
|
|
1997
2006
|
}
|
|
1998
2007
|
for (const agentName of manifest.files.agents ?? []) {
|
|
1999
2008
|
const agentFile = join10(addonSourceDir, "agents", `${agentName}.md`);
|
|
2000
|
-
if (!
|
|
2009
|
+
if (!existsSync8(agentFile)) continue;
|
|
2001
2010
|
const content = readFileSync4(agentFile, "utf-8");
|
|
2002
2011
|
const destPath = join10(projectDir, spec.baseDir, "agents", `${agentName}.md`);
|
|
2003
|
-
if (!
|
|
2004
|
-
ensureDir2(
|
|
2012
|
+
if (!existsSync8(destPath)) {
|
|
2013
|
+
ensureDir2(dirname5(destPath));
|
|
2005
2014
|
writeFileSync2(destPath, content);
|
|
2006
2015
|
result.written++;
|
|
2007
2016
|
result.checksums[`agents/${agentName}.md`] = checksum(content);
|
|
@@ -2012,11 +2021,11 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2012
2021
|
if (spec.rulesDir) {
|
|
2013
2022
|
for (const rule of manifest.files.rules ?? []) {
|
|
2014
2023
|
const ruleSrcPath = join10(addonSourceDir, "rules", rule);
|
|
2015
|
-
if (!
|
|
2024
|
+
if (!existsSync8(ruleSrcPath)) continue;
|
|
2016
2025
|
const content = readFileSync4(ruleSrcPath, "utf-8");
|
|
2017
2026
|
const destPath = join10(projectDir, spec.baseDir, spec.rulesDir, rule);
|
|
2018
|
-
if (!
|
|
2019
|
-
ensureDir2(
|
|
2027
|
+
if (!existsSync8(destPath)) {
|
|
2028
|
+
ensureDir2(dirname5(destPath));
|
|
2020
2029
|
writeFileSync2(destPath, content);
|
|
2021
2030
|
result.written++;
|
|
2022
2031
|
result.checksums[`rules/${rule}`] = checksum(content);
|
|
@@ -2027,12 +2036,12 @@ function generateAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2027
2036
|
}
|
|
2028
2037
|
for (const ref of manifest.files.reference ?? []) {
|
|
2029
2038
|
const refSrcPath = join10(addonSourceDir, "reference", ref);
|
|
2030
|
-
if (!
|
|
2039
|
+
if (!existsSync8(refSrcPath)) continue;
|
|
2031
2040
|
const content = readFileSync4(refSrcPath, "utf-8");
|
|
2032
2041
|
const relPath = `skills/design-harden/reference/${ref}`;
|
|
2033
2042
|
const destPath = join10(projectDir, spec.baseDir, relPath);
|
|
2034
|
-
if (!
|
|
2035
|
-
ensureDir2(
|
|
2043
|
+
if (!existsSync8(destPath)) {
|
|
2044
|
+
ensureDir2(dirname5(destPath));
|
|
2036
2045
|
writeFileSync2(destPath, content);
|
|
2037
2046
|
result.written++;
|
|
2038
2047
|
result.checksums[relPath] = checksum(content);
|
|
@@ -2071,20 +2080,20 @@ function removeAddonFiles(projectDir, addonName, agents, addonSourceDir) {
|
|
|
2071
2080
|
const basePath = AGENT_PATHS[agent] ?? `.${agent}`;
|
|
2072
2081
|
for (const skill of knownSkills) {
|
|
2073
2082
|
const skillDir = join10(projectDir, basePath, "skills", skill);
|
|
2074
|
-
if (
|
|
2083
|
+
if (existsSync8(skillDir)) rmSync(skillDir, { recursive: true, force: true });
|
|
2075
2084
|
}
|
|
2076
2085
|
for (const agentName of knownAgents) {
|
|
2077
2086
|
const agentPath = join10(projectDir, basePath, "agents", `${agentName}.md`);
|
|
2078
|
-
if (
|
|
2087
|
+
if (existsSync8(agentPath)) unlinkSync(agentPath);
|
|
2079
2088
|
}
|
|
2080
2089
|
continue;
|
|
2081
2090
|
}
|
|
2082
2091
|
for (const skillName of knownSkills) {
|
|
2083
2092
|
const { relPath } = spec.skillAdapter(skillName, "");
|
|
2084
2093
|
const destPath = join10(projectDir, spec.baseDir, relPath);
|
|
2085
|
-
if (
|
|
2086
|
-
const parentDir =
|
|
2087
|
-
if (
|
|
2094
|
+
if (existsSync8(destPath)) unlinkSync(destPath);
|
|
2095
|
+
const parentDir = dirname5(destPath);
|
|
2096
|
+
if (existsSync8(parentDir)) {
|
|
2088
2097
|
try {
|
|
2089
2098
|
const entries = readdirSync(parentDir);
|
|
2090
2099
|
if (entries.length === 0) rmdirSync(parentDir);
|
|
@@ -2094,20 +2103,20 @@ function removeAddonFiles(projectDir, addonName, agents, addonSourceDir) {
|
|
|
2094
2103
|
}
|
|
2095
2104
|
for (const agentName of knownAgents) {
|
|
2096
2105
|
const agentPath = join10(projectDir, spec.baseDir, "agents", `${agentName}.md`);
|
|
2097
|
-
if (
|
|
2106
|
+
if (existsSync8(agentPath)) unlinkSync(agentPath);
|
|
2098
2107
|
}
|
|
2099
2108
|
if (spec.rulesDir) {
|
|
2100
2109
|
for (const rule of manifest.files.rules ?? []) {
|
|
2101
2110
|
const rulePath = join10(projectDir, spec.baseDir, spec.rulesDir, rule);
|
|
2102
|
-
if (
|
|
2111
|
+
if (existsSync8(rulePath)) unlinkSync(rulePath);
|
|
2103
2112
|
}
|
|
2104
2113
|
}
|
|
2105
2114
|
for (const ref of manifest.files.reference ?? []) {
|
|
2106
2115
|
const refPath = join10(projectDir, spec.baseDir, "skills", "design-harden", "reference", ref);
|
|
2107
|
-
if (
|
|
2116
|
+
if (existsSync8(refPath)) unlinkSync(refPath);
|
|
2108
2117
|
}
|
|
2109
2118
|
const refDir = join10(projectDir, spec.baseDir, "skills", "design-harden", "reference");
|
|
2110
|
-
if (
|
|
2119
|
+
if (existsSync8(refDir)) {
|
|
2111
2120
|
try {
|
|
2112
2121
|
const entries = readdirSync(refDir);
|
|
2113
2122
|
if (entries.length === 0) rmdirSync(refDir);
|
|
@@ -2115,8 +2124,10 @@ function removeAddonFiles(projectDir, addonName, agents, addonSourceDir) {
|
|
|
2115
2124
|
}
|
|
2116
2125
|
}
|
|
2117
2126
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2127
|
+
if (manifest.attribution) {
|
|
2128
|
+
const noticePath = join10(projectDir, "NOTICE.md");
|
|
2129
|
+
if (existsSync8(noticePath)) unlinkSync(noticePath);
|
|
2130
|
+
}
|
|
2120
2131
|
}
|
|
2121
2132
|
function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
2122
2133
|
const fileMap = buildFileMap(addonSourceDir);
|
|
@@ -2138,8 +2149,8 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2138
2149
|
const basePath = AGENT_PATHS[agent] ?? `.${agent}`;
|
|
2139
2150
|
for (const [relPath, newContent] of fileMap) {
|
|
2140
2151
|
const destPath = join10(projectDir, basePath, relPath);
|
|
2141
|
-
if (!
|
|
2142
|
-
ensureDir2(
|
|
2152
|
+
if (!existsSync8(destPath)) {
|
|
2153
|
+
ensureDir2(dirname5(destPath));
|
|
2143
2154
|
writeFileSync2(destPath, newContent);
|
|
2144
2155
|
result.written++;
|
|
2145
2156
|
continue;
|
|
@@ -2162,12 +2173,12 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2162
2173
|
}
|
|
2163
2174
|
for (const skillName of manifest.files.skills ?? []) {
|
|
2164
2175
|
const skillFile = join10(addonSourceDir, "skills", skillName, "SKILL.md");
|
|
2165
|
-
if (!
|
|
2176
|
+
if (!existsSync8(skillFile)) continue;
|
|
2166
2177
|
const rawContent = readFileSync4(skillFile, "utf-8");
|
|
2167
2178
|
const { relPath, content: newContent } = spec.skillAdapter(skillName, rawContent);
|
|
2168
2179
|
const destPath = join10(projectDir, spec.baseDir, relPath);
|
|
2169
|
-
if (!
|
|
2170
|
-
ensureDir2(
|
|
2180
|
+
if (!existsSync8(destPath)) {
|
|
2181
|
+
ensureDir2(dirname5(destPath));
|
|
2171
2182
|
writeFileSync2(destPath, newContent);
|
|
2172
2183
|
result.written++;
|
|
2173
2184
|
continue;
|
|
@@ -2188,12 +2199,12 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2188
2199
|
}
|
|
2189
2200
|
for (const agentName of manifest.files.agents ?? []) {
|
|
2190
2201
|
const agentFile = join10(addonSourceDir, "agents", `${agentName}.md`);
|
|
2191
|
-
if (!
|
|
2202
|
+
if (!existsSync8(agentFile)) continue;
|
|
2192
2203
|
const newContent = readFileSync4(agentFile, "utf-8");
|
|
2193
2204
|
const destPath = join10(projectDir, spec.baseDir, "agents", `${agentName}.md`);
|
|
2194
2205
|
const relPath = `agents/${agentName}.md`;
|
|
2195
|
-
if (!
|
|
2196
|
-
ensureDir2(
|
|
2206
|
+
if (!existsSync8(destPath)) {
|
|
2207
|
+
ensureDir2(dirname5(destPath));
|
|
2197
2208
|
writeFileSync2(destPath, newContent);
|
|
2198
2209
|
result.written++;
|
|
2199
2210
|
continue;
|
|
@@ -2215,12 +2226,12 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2215
2226
|
if (spec.rulesDir) {
|
|
2216
2227
|
for (const rule of manifest.files.rules ?? []) {
|
|
2217
2228
|
const ruleSrcPath = join10(addonSourceDir, "rules", rule);
|
|
2218
|
-
if (!
|
|
2229
|
+
if (!existsSync8(ruleSrcPath)) continue;
|
|
2219
2230
|
const newContent = readFileSync4(ruleSrcPath, "utf-8");
|
|
2220
2231
|
const destPath = join10(projectDir, spec.baseDir, spec.rulesDir, rule);
|
|
2221
2232
|
const relPath = `rules/${rule}`;
|
|
2222
|
-
if (!
|
|
2223
|
-
ensureDir2(
|
|
2233
|
+
if (!existsSync8(destPath)) {
|
|
2234
|
+
ensureDir2(dirname5(destPath));
|
|
2224
2235
|
writeFileSync2(destPath, newContent);
|
|
2225
2236
|
result.written++;
|
|
2226
2237
|
continue;
|
|
@@ -2242,12 +2253,12 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2242
2253
|
}
|
|
2243
2254
|
for (const ref of manifest.files.reference ?? []) {
|
|
2244
2255
|
const refSrcPath = join10(addonSourceDir, "reference", ref);
|
|
2245
|
-
if (!
|
|
2256
|
+
if (!existsSync8(refSrcPath)) continue;
|
|
2246
2257
|
const newContent = readFileSync4(refSrcPath, "utf-8");
|
|
2247
2258
|
const relPath = `skills/design-harden/reference/${ref}`;
|
|
2248
2259
|
const destPath = join10(projectDir, spec.baseDir, relPath);
|
|
2249
|
-
if (!
|
|
2250
|
-
ensureDir2(
|
|
2260
|
+
if (!existsSync8(destPath)) {
|
|
2261
|
+
ensureDir2(dirname5(destPath));
|
|
2251
2262
|
writeFileSync2(destPath, newContent);
|
|
2252
2263
|
result.written++;
|
|
2253
2264
|
continue;
|
|
@@ -2271,19 +2282,23 @@ function syncAddonFiles(projectDir, addonSourceDir, agents) {
|
|
|
2271
2282
|
}
|
|
2272
2283
|
function detectModifiedAddonFiles(projectDir, addonName) {
|
|
2273
2284
|
let installedChecksums = {};
|
|
2285
|
+
let agents = ["claude"];
|
|
2274
2286
|
try {
|
|
2275
2287
|
const config = readAddonConfig(projectDir);
|
|
2276
2288
|
const installed = config.installed?.[addonName];
|
|
2277
2289
|
if (!installed?.checksums) return [];
|
|
2278
2290
|
installedChecksums = installed.checksums;
|
|
2291
|
+
agents = config.agents ?? ["claude"];
|
|
2279
2292
|
} catch {
|
|
2280
2293
|
return [];
|
|
2281
2294
|
}
|
|
2282
2295
|
const modified = [];
|
|
2283
|
-
for (const
|
|
2296
|
+
for (const agent of agents) {
|
|
2297
|
+
const spec = RUNTIME_SPECS[agent];
|
|
2298
|
+
const baseDir = spec?.baseDir ?? AGENT_PATHS[agent] ?? `.${agent}`;
|
|
2284
2299
|
for (const [relPath, originalHash] of Object.entries(installedChecksums)) {
|
|
2285
|
-
const absPath = join10(projectDir,
|
|
2286
|
-
if (!
|
|
2300
|
+
const absPath = join10(projectDir, baseDir, relPath);
|
|
2301
|
+
if (!existsSync8(absPath)) continue;
|
|
2287
2302
|
const current = checksum(readFileSync4(absPath, "utf-8"));
|
|
2288
2303
|
if (current !== originalHash) {
|
|
2289
2304
|
modified.push(relPath);
|
|
@@ -2298,7 +2313,7 @@ async function updateCommand(options) {
|
|
|
2298
2313
|
if (!options.check && !options.dryRun) {
|
|
2299
2314
|
ensureInteractive("update");
|
|
2300
2315
|
}
|
|
2301
|
-
const targetDir =
|
|
2316
|
+
const targetDir = resolve4(options.path || ".");
|
|
2302
2317
|
p4.intro(introTitle("Update"));
|
|
2303
2318
|
const manifest = readManifest(targetDir);
|
|
2304
2319
|
if (!manifest) {
|
|
@@ -2371,7 +2386,7 @@ async function updateCommand(options) {
|
|
|
2371
2386
|
const newFiles = [];
|
|
2372
2387
|
for (const ide of manifest.selectedIDEs) {
|
|
2373
2388
|
const templateDir = join11(TEMPLATES_DIR, IDE_TEMPLATE_MAP[ide]);
|
|
2374
|
-
if (!
|
|
2389
|
+
if (!existsSync9(templateDir)) continue;
|
|
2375
2390
|
const files = getAllFilesRecursive(templateDir);
|
|
2376
2391
|
for (const file of files) {
|
|
2377
2392
|
const templatePath = join11(templateDir, file);
|
|
@@ -2394,7 +2409,7 @@ async function updateCommand(options) {
|
|
|
2394
2409
|
let foundInAnyTemplate = false;
|
|
2395
2410
|
for (const ide of manifest.selectedIDEs) {
|
|
2396
2411
|
const templateDir = join11(TEMPLATES_DIR, IDE_TEMPLATE_MAP[ide]);
|
|
2397
|
-
if (
|
|
2412
|
+
if (existsSync9(join11(templateDir, relativePath))) {
|
|
2398
2413
|
foundInAnyTemplate = true;
|
|
2399
2414
|
break;
|
|
2400
2415
|
}
|
|
@@ -2529,7 +2544,7 @@ async function updateCommand(options) {
|
|
|
2529
2544
|
};
|
|
2530
2545
|
for (const ide of manifest.selectedIDEs) {
|
|
2531
2546
|
const templateDir = join11(TEMPLATES_DIR, IDE_TEMPLATE_MAP[ide]);
|
|
2532
|
-
if (!
|
|
2547
|
+
if (!existsSync9(templateDir)) continue;
|
|
2533
2548
|
const isPluginMode = ide === "claude-code" && manifest.installMode === "plugin";
|
|
2534
2549
|
const files = getAllFilesRecursive(templateDir);
|
|
2535
2550
|
for (const file of files) {
|
|
@@ -2542,7 +2557,7 @@ async function updateCommand(options) {
|
|
|
2542
2557
|
const templatePath = join11(templateDir, file);
|
|
2543
2558
|
const destPath = join11(targetDir, file);
|
|
2544
2559
|
const templateContent = readFile(templatePath);
|
|
2545
|
-
ensureDir(
|
|
2560
|
+
ensureDir(dirname6(destPath));
|
|
2546
2561
|
writeFile(destPath, templateContent);
|
|
2547
2562
|
updatedManifest.files[file] = createManifestEntry(templateContent);
|
|
2548
2563
|
}
|
|
@@ -2568,7 +2583,7 @@ async function updateCommand(options) {
|
|
|
2568
2583
|
);
|
|
2569
2584
|
for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
|
|
2570
2585
|
const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
|
|
2571
|
-
if (
|
|
2586
|
+
if (existsSync9(scriptPath)) {
|
|
2572
2587
|
chmodSync2(scriptPath, 493);
|
|
2573
2588
|
}
|
|
2574
2589
|
}
|
|
@@ -2624,6 +2639,7 @@ async function updateCommand(options) {
|
|
|
2624
2639
|
syncSpinner.start("Updating enabled addon files...");
|
|
2625
2640
|
let totalUpdated = 0;
|
|
2626
2641
|
for (const name of enabledAddons) {
|
|
2642
|
+
if (name === "orchestration") continue;
|
|
2627
2643
|
const addonSourceDir = getAddonSourceDir(name);
|
|
2628
2644
|
const result = syncAddonFiles(targetDir, addonSourceDir, addonConfig.agents);
|
|
2629
2645
|
totalUpdated += (result.updated ?? 0) + result.written;
|
|
@@ -2765,7 +2781,7 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
|
|
|
2765
2781
|
const rulePath = DYNAMIC_RULE_FILES[ide]?.[0];
|
|
2766
2782
|
if (ruleContent && rulePath) {
|
|
2767
2783
|
const destPath = join11(targetDir, rulePath);
|
|
2768
|
-
ensureDir(
|
|
2784
|
+
ensureDir(dirname6(destPath));
|
|
2769
2785
|
writeFile(destPath, ruleContent);
|
|
2770
2786
|
regeneratedFiles.push(rulePath);
|
|
2771
2787
|
manifest.files[rulePath] = createManifestEntry(ruleContent);
|
|
@@ -2792,7 +2808,7 @@ async function regenerateWithNewStack(targetDir, manifest, analysis, dryRun) {
|
|
|
2792
2808
|
);
|
|
2793
2809
|
for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
|
|
2794
2810
|
const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
|
|
2795
|
-
if (
|
|
2811
|
+
if (existsSync9(scriptPath)) {
|
|
2796
2812
|
chmodSync2(scriptPath, 493);
|
|
2797
2813
|
}
|
|
2798
2814
|
}
|
|
@@ -2841,7 +2857,7 @@ async function migrateToPlugin(targetDir, manifest, analysis, dryRun) {
|
|
|
2841
2857
|
);
|
|
2842
2858
|
for (const script of ["checkpoint.sh", "stop-guard.sh"]) {
|
|
2843
2859
|
const scriptPath = join11(targetDir, pluginResult.pluginPath, "scripts", script);
|
|
2844
|
-
if (
|
|
2860
|
+
if (existsSync9(scriptPath)) {
|
|
2845
2861
|
chmodSync2(scriptPath, 493);
|
|
2846
2862
|
}
|
|
2847
2863
|
}
|
|
@@ -2911,11 +2927,11 @@ function buildDefaultConfig(analysis) {
|
|
|
2911
2927
|
};
|
|
2912
2928
|
}
|
|
2913
2929
|
function cleanEmptyDirs(dirPath) {
|
|
2914
|
-
if (!
|
|
2930
|
+
if (!existsSync9(dirPath)) return;
|
|
2915
2931
|
const entries = readdirSync2(dirPath);
|
|
2916
2932
|
for (const entry of entries) {
|
|
2917
2933
|
const fullPath = join11(dirPath, entry);
|
|
2918
|
-
if (
|
|
2934
|
+
if (existsSync9(fullPath) && lstatSync(fullPath).isDirectory()) {
|
|
2919
2935
|
cleanEmptyDirs(fullPath);
|
|
2920
2936
|
}
|
|
2921
2937
|
}
|
|
@@ -2926,11 +2942,11 @@ function cleanEmptyDirs(dirPath) {
|
|
|
2926
2942
|
}
|
|
2927
2943
|
|
|
2928
2944
|
// src/commands/status.ts
|
|
2929
|
-
import { resolve as
|
|
2945
|
+
import { resolve as resolve5, join as join12 } from "path";
|
|
2930
2946
|
import * as p5 from "@clack/prompts";
|
|
2931
2947
|
import chalk6 from "chalk";
|
|
2932
2948
|
async function statusCommand(options = {}) {
|
|
2933
|
-
const targetDir =
|
|
2949
|
+
const targetDir = resolve5(options.path || ".");
|
|
2934
2950
|
p5.intro(introTitle("Status"));
|
|
2935
2951
|
const manifest = readManifest(targetDir);
|
|
2936
2952
|
const latestPromise = getLatestVersion("devtronic");
|
|
@@ -3012,12 +3028,12 @@ async function statusCommand(options = {}) {
|
|
|
3012
3028
|
}
|
|
3013
3029
|
|
|
3014
3030
|
// src/commands/diff.ts
|
|
3015
|
-
import { resolve as
|
|
3016
|
-
import { existsSync as
|
|
3031
|
+
import { resolve as resolve6, join as join13 } from "path";
|
|
3032
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3017
3033
|
import * as p6 from "@clack/prompts";
|
|
3018
3034
|
import chalk7 from "chalk";
|
|
3019
3035
|
async function diffCommand(options = {}) {
|
|
3020
|
-
const targetDir =
|
|
3036
|
+
const targetDir = resolve6(options.path || ".");
|
|
3021
3037
|
p6.intro(introTitle("Diff"));
|
|
3022
3038
|
const manifest = readManifest(targetDir);
|
|
3023
3039
|
if (!manifest) {
|
|
@@ -3029,7 +3045,7 @@ async function diffCommand(options = {}) {
|
|
|
3029
3045
|
const diffs = [];
|
|
3030
3046
|
for (const ide of manifest.selectedIDEs) {
|
|
3031
3047
|
const templateDir = join13(TEMPLATES_DIR, IDE_TEMPLATE_MAP[ide]);
|
|
3032
|
-
if (!
|
|
3048
|
+
if (!existsSync10(templateDir)) continue;
|
|
3033
3049
|
const templateFiles = getAllFilesRecursive(templateDir);
|
|
3034
3050
|
for (const file of templateFiles) {
|
|
3035
3051
|
const templatePath = join13(templateDir, file);
|
|
@@ -3086,8 +3102,8 @@ async function diffCommand(options = {}) {
|
|
|
3086
3102
|
}
|
|
3087
3103
|
|
|
3088
3104
|
// src/commands/add.ts
|
|
3089
|
-
import { existsSync as
|
|
3090
|
-
import { resolve as
|
|
3105
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3106
|
+
import { resolve as resolve7, join as join14, dirname as dirname7 } from "path";
|
|
3091
3107
|
import * as p7 from "@clack/prompts";
|
|
3092
3108
|
import chalk8 from "chalk";
|
|
3093
3109
|
var ALL_IDES = [
|
|
@@ -3101,7 +3117,7 @@ async function addCommand(ide, options) {
|
|
|
3101
3117
|
if (!options.yes) {
|
|
3102
3118
|
ensureInteractive("add");
|
|
3103
3119
|
}
|
|
3104
|
-
const targetDir =
|
|
3120
|
+
const targetDir = resolve7(options.path || ".");
|
|
3105
3121
|
p7.intro(introTitle("Add IDE"));
|
|
3106
3122
|
const manifest = readManifest(targetDir);
|
|
3107
3123
|
if (!manifest) {
|
|
@@ -3182,7 +3198,7 @@ Valid options: ${ALL_IDES.map((i) => i.value).join(", ")}`
|
|
|
3182
3198
|
const generatedRules = generateArchitectureRules(manifest.projectConfig);
|
|
3183
3199
|
const templateName = IDE_TEMPLATE_MAP[selectedIDE];
|
|
3184
3200
|
const templateDir = join14(TEMPLATES_DIR, templateName);
|
|
3185
|
-
if (!
|
|
3201
|
+
if (!existsSync11(templateDir)) {
|
|
3186
3202
|
spinner8.stop("Error");
|
|
3187
3203
|
p7.cancel(`Template not found: ${templateName}`);
|
|
3188
3204
|
process.exit(1);
|
|
@@ -3211,7 +3227,7 @@ Valid options: ${ALL_IDES.map((i) => i.value).join(", ")}`
|
|
|
3211
3227
|
continue;
|
|
3212
3228
|
}
|
|
3213
3229
|
}
|
|
3214
|
-
ensureDir(
|
|
3230
|
+
ensureDir(dirname7(destPath));
|
|
3215
3231
|
writeFile(destPath, sourceContent);
|
|
3216
3232
|
appliedFiles.push(file);
|
|
3217
3233
|
manifest.files[file] = createManifestEntry(sourceContent);
|
|
@@ -3231,7 +3247,7 @@ Valid options: ${ALL_IDES.map((i) => i.value).join(", ")}`
|
|
|
3231
3247
|
mergedFiles.push(rulePath);
|
|
3232
3248
|
manifest.files[rulePath] = createManifestEntry(mergedContent);
|
|
3233
3249
|
} else {
|
|
3234
|
-
ensureDir(
|
|
3250
|
+
ensureDir(dirname7(destPath));
|
|
3235
3251
|
writeFile(destPath, ruleContent);
|
|
3236
3252
|
generatedFiles.push(`${rulePath} (personalized)`);
|
|
3237
3253
|
manifest.files[rulePath] = createManifestEntry(ruleContent);
|
|
@@ -3272,17 +3288,17 @@ Valid options: ${ALL_IDES.map((i) => i.value).join(", ")}`
|
|
|
3272
3288
|
}
|
|
3273
3289
|
|
|
3274
3290
|
// src/commands/regenerate.ts
|
|
3275
|
-
import { existsSync as
|
|
3276
|
-
import { resolve as
|
|
3277
|
-
import { fileURLToPath as
|
|
3291
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3292
|
+
import { resolve as resolve8, join as join15, dirname as dirname8 } from "path";
|
|
3293
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3278
3294
|
import * as p8 from "@clack/prompts";
|
|
3279
3295
|
import chalk9 from "chalk";
|
|
3280
|
-
var __filename =
|
|
3281
|
-
var __regen_dirname =
|
|
3282
|
-
var TEMPLATES_DIR2 =
|
|
3296
|
+
var __filename = fileURLToPath4(import.meta.url);
|
|
3297
|
+
var __regen_dirname = dirname8(__filename);
|
|
3298
|
+
var TEMPLATES_DIR2 = existsSync12(resolve8(__regen_dirname, "../templates")) ? resolve8(__regen_dirname, "../templates") : resolve8(__regen_dirname, "../../templates");
|
|
3283
3299
|
async function regenerateCommand(target, options) {
|
|
3284
3300
|
ensureInteractive("regenerate");
|
|
3285
|
-
const targetDir =
|
|
3301
|
+
const targetDir = resolve8(options.path || ".");
|
|
3286
3302
|
p8.intro(introTitle("Regenerate"));
|
|
3287
3303
|
const manifest = readManifest(targetDir);
|
|
3288
3304
|
if (!manifest) {
|
|
@@ -3422,7 +3438,7 @@ Valid options:
|
|
|
3422
3438
|
const rulePath = DYNAMIC_RULE_FILES[ide]?.[0];
|
|
3423
3439
|
if (ruleContent && rulePath) {
|
|
3424
3440
|
const destPath = join15(targetDir, rulePath);
|
|
3425
|
-
ensureDir(
|
|
3441
|
+
ensureDir(dirname8(destPath));
|
|
3426
3442
|
writeFile(destPath, ruleContent);
|
|
3427
3443
|
regeneratedFiles.push(rulePath);
|
|
3428
3444
|
manifest.files[rulePath] = createManifestEntry(ruleContent);
|
|
@@ -3471,12 +3487,12 @@ Valid options:
|
|
|
3471
3487
|
}
|
|
3472
3488
|
|
|
3473
3489
|
// src/commands/info.ts
|
|
3474
|
-
import { resolve as
|
|
3475
|
-
import { existsSync as
|
|
3490
|
+
import { resolve as resolve9, join as join16 } from "path";
|
|
3491
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3 } from "fs";
|
|
3476
3492
|
import * as p9 from "@clack/prompts";
|
|
3477
3493
|
import chalk10 from "chalk";
|
|
3478
3494
|
async function infoCommand() {
|
|
3479
|
-
const targetDir =
|
|
3495
|
+
const targetDir = resolve9(".");
|
|
3480
3496
|
p9.intro(introTitle("Info"));
|
|
3481
3497
|
const manifest = readManifest(targetDir);
|
|
3482
3498
|
const currentVersion = getCliVersion();
|
|
@@ -3486,15 +3502,15 @@ async function infoCommand() {
|
|
|
3486
3502
|
let agentCount = 0;
|
|
3487
3503
|
if (manifest) {
|
|
3488
3504
|
const pluginDir = manifest.pluginPath ? join16(targetDir, manifest.pluginPath) : null;
|
|
3489
|
-
if (pluginDir &&
|
|
3505
|
+
if (pluginDir && existsSync13(pluginDir)) {
|
|
3490
3506
|
const skillsDir = join16(pluginDir, "skills");
|
|
3491
3507
|
const agentsDir = join16(pluginDir, "agents");
|
|
3492
|
-
if (
|
|
3508
|
+
if (existsSync13(skillsDir)) {
|
|
3493
3509
|
skillCount = readdirSync3(skillsDir, { withFileTypes: true }).filter(
|
|
3494
3510
|
(e) => e.isDirectory() || e.isFile() && e.name.endsWith(".md")
|
|
3495
3511
|
).length;
|
|
3496
3512
|
}
|
|
3497
|
-
if (
|
|
3513
|
+
if (existsSync13(agentsDir)) {
|
|
3498
3514
|
agentCount = readdirSync3(agentsDir, { withFileTypes: true }).filter(
|
|
3499
3515
|
(e) => e.isFile() && e.name.endsWith(".md")
|
|
3500
3516
|
).length;
|
|
@@ -3502,7 +3518,7 @@ async function infoCommand() {
|
|
|
3502
3518
|
}
|
|
3503
3519
|
if (skillCount === 0) {
|
|
3504
3520
|
const claudeSkills = join16(targetDir, ".claude", "skills");
|
|
3505
|
-
if (
|
|
3521
|
+
if (existsSync13(claudeSkills)) {
|
|
3506
3522
|
skillCount = readdirSync3(claudeSkills, { withFileTypes: true }).filter(
|
|
3507
3523
|
(e) => e.isDirectory() || e.isFile() && e.name.endsWith(".md")
|
|
3508
3524
|
).length;
|
|
@@ -3510,7 +3526,7 @@ async function infoCommand() {
|
|
|
3510
3526
|
}
|
|
3511
3527
|
if (agentCount === 0) {
|
|
3512
3528
|
const claudeAgents = join16(targetDir, ".claude", "agents");
|
|
3513
|
-
if (
|
|
3529
|
+
if (existsSync13(claudeAgents)) {
|
|
3514
3530
|
agentCount = readdirSync3(claudeAgents, { withFileTypes: true }).filter(
|
|
3515
3531
|
(e) => e.isFile() && e.name.endsWith(".md")
|
|
3516
3532
|
).length;
|
|
@@ -3554,12 +3570,12 @@ async function infoCommand() {
|
|
|
3554
3570
|
}
|
|
3555
3571
|
|
|
3556
3572
|
// src/commands/list.ts
|
|
3557
|
-
import { resolve as
|
|
3558
|
-
import { existsSync as
|
|
3573
|
+
import { resolve as resolve10, join as join17 } from "path";
|
|
3574
|
+
import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync5 } from "fs";
|
|
3559
3575
|
import * as p10 from "@clack/prompts";
|
|
3560
3576
|
import chalk11 from "chalk";
|
|
3561
3577
|
async function listCommand(filter, options) {
|
|
3562
|
-
const targetDir =
|
|
3578
|
+
const targetDir = resolve10(options.path || ".");
|
|
3563
3579
|
p10.intro(introTitle("List"));
|
|
3564
3580
|
const manifest = readManifest(targetDir);
|
|
3565
3581
|
const showSkills = !filter || filter === "skills";
|
|
@@ -3573,29 +3589,29 @@ Valid options: skills, agents`);
|
|
|
3573
3589
|
const skills = [];
|
|
3574
3590
|
const agents = [];
|
|
3575
3591
|
const pluginDir = manifest?.pluginPath ? join17(targetDir, manifest.pluginPath) : null;
|
|
3576
|
-
if (pluginDir &&
|
|
3592
|
+
if (pluginDir && existsSync14(pluginDir)) {
|
|
3577
3593
|
if (showSkills) {
|
|
3578
3594
|
const skillsDir = join17(pluginDir, "skills");
|
|
3579
|
-
if (
|
|
3595
|
+
if (existsSync14(skillsDir)) {
|
|
3580
3596
|
skills.push(...discoverSkills(skillsDir));
|
|
3581
3597
|
}
|
|
3582
3598
|
}
|
|
3583
3599
|
if (showAgents) {
|
|
3584
3600
|
const agentsDir = join17(pluginDir, "agents");
|
|
3585
|
-
if (
|
|
3601
|
+
if (existsSync14(agentsDir)) {
|
|
3586
3602
|
agents.push(...discoverAgents(agentsDir));
|
|
3587
3603
|
}
|
|
3588
3604
|
}
|
|
3589
3605
|
}
|
|
3590
3606
|
if (showSkills && skills.length === 0) {
|
|
3591
3607
|
const claudeSkills = join17(targetDir, ".claude", "skills");
|
|
3592
|
-
if (
|
|
3608
|
+
if (existsSync14(claudeSkills)) {
|
|
3593
3609
|
skills.push(...discoverSkills(claudeSkills));
|
|
3594
3610
|
}
|
|
3595
3611
|
}
|
|
3596
3612
|
if (showAgents && agents.length === 0) {
|
|
3597
3613
|
const claudeAgents = join17(targetDir, ".claude", "agents");
|
|
3598
|
-
if (
|
|
3614
|
+
if (existsSync14(claudeAgents)) {
|
|
3599
3615
|
agents.push(...discoverAgents(claudeAgents));
|
|
3600
3616
|
}
|
|
3601
3617
|
}
|
|
@@ -3623,7 +3639,7 @@ function discoverSkills(skillsDir) {
|
|
|
3623
3639
|
for (const entry of entries) {
|
|
3624
3640
|
if (entry.isDirectory()) {
|
|
3625
3641
|
const skillMd = join17(skillsDir, entry.name, "SKILL.md");
|
|
3626
|
-
const description =
|
|
3642
|
+
const description = existsSync14(skillMd) ? extractDescription(skillMd) : "";
|
|
3627
3643
|
items.push({ name: entry.name, description });
|
|
3628
3644
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
3629
3645
|
const name = entry.name.replace(/\.md$/, "");
|
|
@@ -3668,7 +3684,7 @@ function extractDescription(filePath) {
|
|
|
3668
3684
|
}
|
|
3669
3685
|
|
|
3670
3686
|
// src/commands/config.ts
|
|
3671
|
-
import { resolve as
|
|
3687
|
+
import { resolve as resolve11 } from "path";
|
|
3672
3688
|
import * as p11 from "@clack/prompts";
|
|
3673
3689
|
import chalk12 from "chalk";
|
|
3674
3690
|
var ARRAY_KEYS = [
|
|
@@ -3688,7 +3704,7 @@ var VALID_KEYS = [
|
|
|
3688
3704
|
...ARRAY_KEYS
|
|
3689
3705
|
];
|
|
3690
3706
|
async function configCommand(options) {
|
|
3691
|
-
const targetDir =
|
|
3707
|
+
const targetDir = resolve11(options.path || ".");
|
|
3692
3708
|
p11.intro(introTitle("Config"));
|
|
3693
3709
|
const manifest = readManifest(targetDir);
|
|
3694
3710
|
if (!manifest) {
|
|
@@ -3720,7 +3736,7 @@ async function configCommand(options) {
|
|
|
3720
3736
|
p11.outro("");
|
|
3721
3737
|
}
|
|
3722
3738
|
async function configSetCommand(key, value, options) {
|
|
3723
|
-
const targetDir =
|
|
3739
|
+
const targetDir = resolve11(options.path || ".");
|
|
3724
3740
|
p11.intro(introTitle("Config Set"));
|
|
3725
3741
|
const manifest = readManifest(targetDir);
|
|
3726
3742
|
if (!manifest || !manifest.projectConfig) {
|
|
@@ -3775,7 +3791,7 @@ Valid addons: ${validAddons.join(", ")}`
|
|
|
3775
3791
|
p11.outro(chalk12.green("Configuration updated"));
|
|
3776
3792
|
}
|
|
3777
3793
|
async function configResetCommand(options) {
|
|
3778
|
-
const targetDir =
|
|
3794
|
+
const targetDir = resolve11(options.path || ".");
|
|
3779
3795
|
p11.intro(introTitle("Config Reset"));
|
|
3780
3796
|
const manifest = readManifest(targetDir);
|
|
3781
3797
|
if (!manifest) {
|
|
@@ -3826,13 +3842,13 @@ async function configResetCommand(options) {
|
|
|
3826
3842
|
}
|
|
3827
3843
|
|
|
3828
3844
|
// src/commands/doctor.ts
|
|
3829
|
-
import { resolve as
|
|
3830
|
-
import { existsSync as
|
|
3845
|
+
import { resolve as resolve12, join as join18 } from "path";
|
|
3846
|
+
import { existsSync as existsSync15, readdirSync as readdirSync5, statSync, chmodSync as chmodSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
3831
3847
|
import { execSync } from "child_process";
|
|
3832
3848
|
import * as p12 from "@clack/prompts";
|
|
3833
3849
|
import chalk13 from "chalk";
|
|
3834
3850
|
async function doctorCommand(options) {
|
|
3835
|
-
const targetDir =
|
|
3851
|
+
const targetDir = resolve12(options.path || ".");
|
|
3836
3852
|
p12.intro(introTitle("Doctor"));
|
|
3837
3853
|
const manifest = readManifest(targetDir);
|
|
3838
3854
|
if (!manifest) {
|
|
@@ -3950,9 +3966,9 @@ function checkManifestFiles(targetDir, manifest) {
|
|
|
3950
3966
|
function checkScriptPermissions(targetDir, manifest) {
|
|
3951
3967
|
const pluginDir = manifest.pluginPath ? join18(targetDir, manifest.pluginPath) : null;
|
|
3952
3968
|
const shFiles = [];
|
|
3953
|
-
if (pluginDir &&
|
|
3969
|
+
if (pluginDir && existsSync15(pluginDir)) {
|
|
3954
3970
|
const scriptsDir = join18(pluginDir, "scripts");
|
|
3955
|
-
if (
|
|
3971
|
+
if (existsSync15(scriptsDir)) {
|
|
3956
3972
|
const entries = readdirSync5(scriptsDir);
|
|
3957
3973
|
for (const entry of entries) {
|
|
3958
3974
|
if (entry.endsWith(".sh")) {
|
|
@@ -4020,7 +4036,7 @@ function checkPluginRegistered(targetDir) {
|
|
|
4020
4036
|
}
|
|
4021
4037
|
function checkHookScripts(targetDir, manifest) {
|
|
4022
4038
|
const pluginDir = manifest.pluginPath ? join18(targetDir, manifest.pluginPath) : null;
|
|
4023
|
-
if (!pluginDir || !
|
|
4039
|
+
if (!pluginDir || !existsSync15(pluginDir)) {
|
|
4024
4040
|
return {
|
|
4025
4041
|
name: "hooks",
|
|
4026
4042
|
status: "pass",
|
|
@@ -4028,7 +4044,7 @@ function checkHookScripts(targetDir, manifest) {
|
|
|
4028
4044
|
};
|
|
4029
4045
|
}
|
|
4030
4046
|
const hooksDir = join18(pluginDir, "hooks");
|
|
4031
|
-
if (!
|
|
4047
|
+
if (!existsSync15(hooksDir)) {
|
|
4032
4048
|
return {
|
|
4033
4049
|
name: "hooks",
|
|
4034
4050
|
status: "pass",
|
|
@@ -4055,7 +4071,7 @@ function checkHookScripts(targetDir, manifest) {
|
|
|
4055
4071
|
""
|
|
4056
4072
|
);
|
|
4057
4073
|
const scriptPath = join18(pluginDir, resolved);
|
|
4058
|
-
if (
|
|
4074
|
+
if (existsSync15(scriptPath)) {
|
|
4059
4075
|
valid++;
|
|
4060
4076
|
}
|
|
4061
4077
|
} else {
|
|
@@ -4106,7 +4122,7 @@ function checkQualityScripts(targetDir) {
|
|
|
4106
4122
|
}
|
|
4107
4123
|
function checkThoughtsDir(targetDir) {
|
|
4108
4124
|
const thoughtsDir = join18(targetDir, "thoughts");
|
|
4109
|
-
if (
|
|
4125
|
+
if (existsSync15(thoughtsDir)) {
|
|
4110
4126
|
return {
|
|
4111
4127
|
name: "thoughts",
|
|
4112
4128
|
status: "pass",
|
|
@@ -4138,7 +4154,7 @@ function checkThoughtsDir(targetDir) {
|
|
|
4138
4154
|
function checkEslint(targetDir) {
|
|
4139
4155
|
try {
|
|
4140
4156
|
const eslintLocal = join18(targetDir, "node_modules", ".bin", "eslint");
|
|
4141
|
-
if (
|
|
4157
|
+
if (existsSync15(eslintLocal)) {
|
|
4142
4158
|
return { name: "eslint", status: "pass", message: "eslint is available" };
|
|
4143
4159
|
}
|
|
4144
4160
|
execSync("which eslint", { stdio: "pipe" });
|
|
@@ -4153,14 +4169,14 @@ function checkEslint(targetDir) {
|
|
|
4153
4169
|
}
|
|
4154
4170
|
|
|
4155
4171
|
// src/commands/uninstall.ts
|
|
4156
|
-
import { existsSync as
|
|
4157
|
-
import { resolve as
|
|
4172
|
+
import { existsSync as existsSync16, rmSync as rmSync2, readdirSync as readdirSync6 } from "fs";
|
|
4173
|
+
import { resolve as resolve13, join as join19, dirname as dirname9 } from "path";
|
|
4158
4174
|
import * as p13 from "@clack/prompts";
|
|
4159
4175
|
import chalk14 from "chalk";
|
|
4160
4176
|
var DEVTRONIC_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
4161
4177
|
async function uninstallCommand(options) {
|
|
4162
4178
|
ensureInteractive("uninstall");
|
|
4163
|
-
const targetDir =
|
|
4179
|
+
const targetDir = resolve13(options.path || ".");
|
|
4164
4180
|
p13.intro(introTitle("Uninstall"));
|
|
4165
4181
|
const manifest = readManifest(targetDir);
|
|
4166
4182
|
if (!manifest) {
|
|
@@ -4174,8 +4190,8 @@ async function uninstallCommand(options) {
|
|
|
4174
4190
|
const managedFiles = Object.keys(manifest.files);
|
|
4175
4191
|
const existingFiles = managedFiles.filter((f) => fileExists(join19(targetDir, f)));
|
|
4176
4192
|
const missingFiles = managedFiles.filter((f) => !fileExists(join19(targetDir, f)));
|
|
4177
|
-
const hasPlugin = manifest.installMode === "plugin" &&
|
|
4178
|
-
const hasThoughts =
|
|
4193
|
+
const hasPlugin = manifest.installMode === "plugin" && existsSync16(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME));
|
|
4194
|
+
const hasThoughts = existsSync16(join19(targetDir, "thoughts"));
|
|
4179
4195
|
const hasClaudeMd = fileExists(join19(targetDir, "CLAUDE.md"));
|
|
4180
4196
|
const hasAgentsMd = fileExists(join19(targetDir, "AGENTS.md"));
|
|
4181
4197
|
p13.log.info(`Installation found: v${manifest.version} (${manifest.implantedAt})`);
|
|
@@ -4266,12 +4282,12 @@ async function uninstallCommand(options) {
|
|
|
4266
4282
|
rmSync2(join19(targetDir, PLUGIN_DIR, PLUGIN_NAME), { recursive: true, force: true });
|
|
4267
4283
|
removed.push(`${PLUGIN_DIR}/${PLUGIN_NAME}/`);
|
|
4268
4284
|
const marketplaceDescDir = join19(targetDir, PLUGIN_DIR, ".claude-plugin");
|
|
4269
|
-
if (
|
|
4285
|
+
if (existsSync16(marketplaceDescDir)) {
|
|
4270
4286
|
rmSync2(marketplaceDescDir, { recursive: true, force: true });
|
|
4271
4287
|
removed.push(`${PLUGIN_DIR}/.claude-plugin/`);
|
|
4272
4288
|
}
|
|
4273
4289
|
const pluginsDir = join19(targetDir, PLUGIN_DIR);
|
|
4274
|
-
if (
|
|
4290
|
+
if (existsSync16(pluginsDir)) {
|
|
4275
4291
|
const remaining = readdirSafe(pluginsDir);
|
|
4276
4292
|
if (remaining.length === 0) {
|
|
4277
4293
|
rmSync2(pluginsDir, { recursive: true, force: true });
|
|
@@ -4288,10 +4304,10 @@ async function uninstallCommand(options) {
|
|
|
4288
4304
|
if (file.startsWith(PLUGIN_DIR + "/")) continue;
|
|
4289
4305
|
try {
|
|
4290
4306
|
const filePath = join19(targetDir, file);
|
|
4291
|
-
if (
|
|
4307
|
+
if (existsSync16(filePath)) {
|
|
4292
4308
|
rmSync2(filePath, { force: true });
|
|
4293
4309
|
removed.push(file);
|
|
4294
|
-
cleanEmptyParents(targetDir,
|
|
4310
|
+
cleanEmptyParents(targetDir, dirname9(file));
|
|
4295
4311
|
}
|
|
4296
4312
|
} catch (err) {
|
|
4297
4313
|
errors.push(`Failed to remove ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -4335,7 +4351,7 @@ async function uninstallCommand(options) {
|
|
|
4335
4351
|
}
|
|
4336
4352
|
try {
|
|
4337
4353
|
const manifestDir = join19(targetDir, MANIFEST_DIR);
|
|
4338
|
-
if (
|
|
4354
|
+
if (existsSync16(manifestDir)) {
|
|
4339
4355
|
rmSync2(manifestDir, { recursive: true, force: true });
|
|
4340
4356
|
removed.push(`${MANIFEST_DIR}/`);
|
|
4341
4357
|
}
|
|
@@ -4382,12 +4398,12 @@ async function uninstallCommand(options) {
|
|
|
4382
4398
|
function cleanEmptyParents(targetDir, relDir) {
|
|
4383
4399
|
if (!relDir || relDir === ".") return;
|
|
4384
4400
|
const absDir = join19(targetDir, relDir);
|
|
4385
|
-
if (!
|
|
4401
|
+
if (!existsSync16(absDir)) return;
|
|
4386
4402
|
const entries = readdirSafe(absDir);
|
|
4387
4403
|
if (entries.length === 0) {
|
|
4388
4404
|
try {
|
|
4389
4405
|
rmSync2(absDir, { recursive: true, force: true });
|
|
4390
|
-
cleanEmptyParents(targetDir,
|
|
4406
|
+
cleanEmptyParents(targetDir, dirname9(relDir));
|
|
4391
4407
|
} catch {
|
|
4392
4408
|
}
|
|
4393
4409
|
}
|
|
@@ -4401,15 +4417,15 @@ function readdirSafe(dir) {
|
|
|
4401
4417
|
}
|
|
4402
4418
|
|
|
4403
4419
|
// src/commands/addon.ts
|
|
4404
|
-
import { resolve as
|
|
4405
|
-
import { existsSync as
|
|
4420
|
+
import { resolve as resolve14, join as join20, dirname as dirname10 } from "path";
|
|
4421
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync3, rmSync as rmSync3 } from "fs";
|
|
4406
4422
|
import * as p14 from "@clack/prompts";
|
|
4407
4423
|
import chalk15 from "chalk";
|
|
4408
4424
|
function isFileBasedAddon(addonName) {
|
|
4409
4425
|
return addonName !== "orchestration";
|
|
4410
4426
|
}
|
|
4411
4427
|
async function addonCommand(action, addonName, options) {
|
|
4412
|
-
const targetDir =
|
|
4428
|
+
const targetDir = resolve14(options.path || ".");
|
|
4413
4429
|
p14.intro(introTitle(`Addon ${action}`));
|
|
4414
4430
|
const validAddons = Object.keys(ADDONS);
|
|
4415
4431
|
if (!validAddons.includes(addonName)) {
|
|
@@ -4420,8 +4436,8 @@ Valid addons: ${validAddons.join(", ")}`);
|
|
|
4420
4436
|
}
|
|
4421
4437
|
const typedName = addonName;
|
|
4422
4438
|
const canonicalAction = action === "enable" ? "add" : action === "disable" ? "remove" : action;
|
|
4423
|
-
if (action === "
|
|
4424
|
-
const canonical = action === "
|
|
4439
|
+
if (action === "enable" || action === "disable") {
|
|
4440
|
+
const canonical = action === "enable" ? "add" : "remove";
|
|
4425
4441
|
p14.log.warn(
|
|
4426
4442
|
`"addon ${action}" is deprecated. Use "addon ${canonical}" instead.`
|
|
4427
4443
|
);
|
|
@@ -4483,7 +4499,7 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4483
4499
|
const addedFiles = [];
|
|
4484
4500
|
for (const skillDir of addon.skills) {
|
|
4485
4501
|
const sourceDir = join20(skillsSourceDir, skillDir);
|
|
4486
|
-
if (!
|
|
4502
|
+
if (!existsSync17(sourceDir)) {
|
|
4487
4503
|
spinner8.stop(`Template not found for skill: ${skillDir}`);
|
|
4488
4504
|
p14.log.warn(`Skipping ${skillDir} \u2014 template not found.`);
|
|
4489
4505
|
continue;
|
|
@@ -4493,7 +4509,7 @@ async function addAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4493
4509
|
const content = readFile(join20(sourceDir, file));
|
|
4494
4510
|
const destRelPath = join20(pluginRoot, "skills", skillDir, file);
|
|
4495
4511
|
const destAbsPath = join20(targetDir, destRelPath);
|
|
4496
|
-
ensureDir(
|
|
4512
|
+
ensureDir(dirname10(destAbsPath));
|
|
4497
4513
|
writeFile(destAbsPath, content);
|
|
4498
4514
|
manifest.files[destRelPath] = createManifestEntry(content);
|
|
4499
4515
|
addedFiles.push(destRelPath);
|
|
@@ -4542,7 +4558,7 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4542
4558
|
for (const [filePath, fileInfo] of Object.entries(manifest.files)) {
|
|
4543
4559
|
if (!filePath.startsWith(skillRelBase)) continue;
|
|
4544
4560
|
const absPath = join20(targetDir, filePath);
|
|
4545
|
-
if (!
|
|
4561
|
+
if (!existsSync17(absPath)) continue;
|
|
4546
4562
|
const current = calculateChecksum(readFile(absPath));
|
|
4547
4563
|
if (current !== fileInfo.originalChecksum) {
|
|
4548
4564
|
modifiedFiles.push(filePath);
|
|
@@ -4570,11 +4586,11 @@ async function removeAddon(targetDir, manifest, addonName, currentAddons) {
|
|
|
4570
4586
|
for (const filePath of Object.keys(manifest.files)) {
|
|
4571
4587
|
if (filePath.startsWith(skillRelBase)) {
|
|
4572
4588
|
const absPath = join20(targetDir, filePath);
|
|
4573
|
-
if (
|
|
4589
|
+
if (existsSync17(absPath)) unlinkSync3(absPath);
|
|
4574
4590
|
delete manifest.files[filePath];
|
|
4575
4591
|
}
|
|
4576
4592
|
}
|
|
4577
|
-
if (
|
|
4593
|
+
if (existsSync17(skillAbsDir)) {
|
|
4578
4594
|
try {
|
|
4579
4595
|
rmSync3(skillAbsDir, { recursive: true });
|
|
4580
4596
|
} catch {
|
|
@@ -4619,16 +4635,14 @@ async function addFileBasedAddon(targetDir, addonName, _options) {
|
|
|
4619
4635
|
spinner8.start(`Adding ${addon.label}...`);
|
|
4620
4636
|
const addonSourceDir = getAddonSourceDir(addonName);
|
|
4621
4637
|
const result = generateAddonFiles(targetDir, addonSourceDir, config.agents);
|
|
4622
|
-
const
|
|
4623
|
-
readFileSync6(join20(addonSourceDir, "manifest.json"), "utf-8")
|
|
4624
|
-
);
|
|
4638
|
+
const addonMeta = getAddonManifest(addonName);
|
|
4625
4639
|
const fileList = [
|
|
4626
|
-
...(
|
|
4627
|
-
...(
|
|
4628
|
-
...(
|
|
4640
|
+
...(addonMeta.files.skills ?? []).map((s) => `skills/${s}`),
|
|
4641
|
+
...(addonMeta.files.agents ?? []).map((a) => `agents/${a}.md`),
|
|
4642
|
+
...(addonMeta.files.rules ?? []).map((r) => `rules/${r}`)
|
|
4629
4643
|
];
|
|
4630
4644
|
writeAddonToConfig(targetDir, addonName, {
|
|
4631
|
-
version:
|
|
4645
|
+
version: addonMeta.version,
|
|
4632
4646
|
files: fileList,
|
|
4633
4647
|
checksums: result.checksums ?? {}
|
|
4634
4648
|
});
|
|
@@ -4685,7 +4699,7 @@ function getAddonListInfo(targetDir) {
|
|
|
4685
4699
|
}));
|
|
4686
4700
|
}
|
|
4687
4701
|
async function addonListCommand(options) {
|
|
4688
|
-
const targetDir =
|
|
4702
|
+
const targetDir = resolve14(options.path || ".");
|
|
4689
4703
|
p14.intro(introTitle("Addon List"));
|
|
4690
4704
|
const items = getAddonListInfo(targetDir);
|
|
4691
4705
|
const lines = items.map((item) => {
|
|
@@ -4698,10 +4712,27 @@ async function addonListCommand(options) {
|
|
|
4698
4712
|
p14.outro(`Use ${chalk15.cyan("devtronic addon add <name>")} to install.`);
|
|
4699
4713
|
}
|
|
4700
4714
|
async function addonSyncCommand(options) {
|
|
4701
|
-
const targetDir =
|
|
4715
|
+
const targetDir = resolve14(options.path || ".");
|
|
4702
4716
|
p14.intro(introTitle("Addon Sync"));
|
|
4703
4717
|
const config = readAddonConfig(targetDir);
|
|
4704
|
-
const
|
|
4718
|
+
const manifest = readManifest(targetDir);
|
|
4719
|
+
const manifestAddons = manifest?.projectConfig?.enabledAddons ?? [];
|
|
4720
|
+
for (const name of manifestAddons) {
|
|
4721
|
+
if (!config.installed[name] && isFileBasedAddon(name)) {
|
|
4722
|
+
const addonMeta = getAddonManifest(name);
|
|
4723
|
+
const fileList = [
|
|
4724
|
+
...(addonMeta.files.skills ?? []).map((s) => `skills/${s}`),
|
|
4725
|
+
...(addonMeta.files.agents ?? []).map((a) => `agents/${a}.md`),
|
|
4726
|
+
...(addonMeta.files.rules ?? []).map((r) => `rules/${r}`)
|
|
4727
|
+
];
|
|
4728
|
+
writeAddonToConfig(targetDir, name, {
|
|
4729
|
+
version: addonMeta.version,
|
|
4730
|
+
files: fileList
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
const freshConfig = readAddonConfig(targetDir);
|
|
4735
|
+
const installedNames = Object.keys(freshConfig.installed);
|
|
4705
4736
|
if (installedNames.length === 0) {
|
|
4706
4737
|
p14.log.info("No addons installed. Nothing to sync.");
|
|
4707
4738
|
p14.outro("");
|
|
@@ -4712,8 +4743,9 @@ async function addonSyncCommand(options) {
|
|
|
4712
4743
|
let totalWritten = 0;
|
|
4713
4744
|
let totalConflicts = [];
|
|
4714
4745
|
for (const name of installedNames) {
|
|
4746
|
+
if (!isFileBasedAddon(name)) continue;
|
|
4715
4747
|
const addonSourceDir = getAddonSourceDir(name);
|
|
4716
|
-
const result = syncAddonFiles(targetDir, addonSourceDir,
|
|
4748
|
+
const result = syncAddonFiles(targetDir, addonSourceDir, freshConfig.agents);
|
|
4717
4749
|
totalWritten += result.written + (result.updated ?? 0);
|
|
4718
4750
|
totalConflicts = totalConflicts.concat(result.conflicts);
|
|
4719
4751
|
}
|
|
@@ -4739,16 +4771,16 @@ function updateDescriptors(targetDir, manifest, pluginRoot, addonSkillCount) {
|
|
|
4739
4771
|
}
|
|
4740
4772
|
|
|
4741
4773
|
// src/commands/mode.ts
|
|
4742
|
-
import { resolve as
|
|
4743
|
-
import { existsSync as
|
|
4774
|
+
import { resolve as resolve15, join as join21 } from "path";
|
|
4775
|
+
import { existsSync as existsSync18 } from "fs";
|
|
4744
4776
|
import * as p15 from "@clack/prompts";
|
|
4745
4777
|
import chalk16 from "chalk";
|
|
4746
4778
|
async function modeCommand(action, options) {
|
|
4747
|
-
const targetDir =
|
|
4779
|
+
const targetDir = resolve15(options.path || ".");
|
|
4748
4780
|
p15.intro(introTitle("Mode"));
|
|
4749
4781
|
if (action === "show") {
|
|
4750
4782
|
const configPath = join21(targetDir, ".claude", "devtronic.json");
|
|
4751
|
-
const hasConfig =
|
|
4783
|
+
const hasConfig = existsSync18(configPath);
|
|
4752
4784
|
const config = readAddonConfig(targetDir);
|
|
4753
4785
|
const currentMode = config.mode;
|
|
4754
4786
|
const isDefault = !hasConfig || currentMode === void 0;
|