claude-code-arcane 2.6.1 → 2.7.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/CHANGELOG.md +9 -0
- package/README.md +8 -4
- package/dist/cli.js +335 -133
- package/docs/SKILLS-CATALOG.md +19 -3
- package/docs/USER-GUIDE.md +3 -3
- package/hooks/validate-commit.sh +36 -0
- package/package.json +1 -1
- package/profiles/blender.yaml +49 -0
- package/profiles/freelance.yaml +1 -0
- package/rules/blender-pipeline.md +95 -0
- package/skills/arcane-add/SKILL.md +29 -2
- package/skills/blender-animation/SKILL.md +167 -0
- package/skills/blender-context/SKILL.md +145 -0
- package/skills/blender-export/SKILL.md +143 -0
- package/skills/blender-export/scripts/validate_gltf.py +362 -0
- package/skills/blender-materials/SKILL.md +165 -0
- package/skills/blender-modeling/SKILL.md +160 -0
- package/skills/fiverr-gig/SKILL.md +147 -0
- package/skills/freelance-scan/references/platforms.md +2 -0
- package/skills/meshy-derive/SKILL.md +3 -0
package/dist/cli.js
CHANGED
|
@@ -265,6 +265,113 @@ async function runInstallWizard(profiles, detections) {
|
|
|
265
265
|
);
|
|
266
266
|
return names;
|
|
267
267
|
}
|
|
268
|
+
async function runAddWizard(input) {
|
|
269
|
+
const installedProfiles = new Set(input.installedProfiles);
|
|
270
|
+
const installedSkills = new Set(input.installedSkills);
|
|
271
|
+
const addableProfiles = input.profiles.filter(
|
|
272
|
+
(pr) => !installedProfiles.has(pr.name)
|
|
273
|
+
);
|
|
274
|
+
const addableSkills = input.skills.filter((s) => !installedSkills.has(s.name));
|
|
275
|
+
p.intro(chalk.cyan("claude-code-arcane add"));
|
|
276
|
+
const current = input.installedProfiles.filter((n) => n !== "core");
|
|
277
|
+
p.note(
|
|
278
|
+
[
|
|
279
|
+
`Profiles: ${current.length > 0 ? chalk.green(current.join(", ")) : chalk.dim("core only")}`,
|
|
280
|
+
`Skills: ${chalk.green(String(installedSkills.size))} installed, ${chalk.dim(`${addableSkills.length} available`)}`
|
|
281
|
+
].join("\n"),
|
|
282
|
+
"Current installation"
|
|
283
|
+
);
|
|
284
|
+
if (addableProfiles.length === 0 && addableSkills.length === 0) {
|
|
285
|
+
p.outro(chalk.dim("Everything in the catalog is already installed."));
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
const kind = await pickKind(addableProfiles.length, addableSkills.length);
|
|
289
|
+
if (kind === null) return null;
|
|
290
|
+
const items = kind === "profiles" ? await pickProfiles(addableProfiles) : await pickSkills(addableSkills);
|
|
291
|
+
if (items === null) return null;
|
|
292
|
+
if (items.length === 0) {
|
|
293
|
+
p.outro(
|
|
294
|
+
chalk.dim(
|
|
295
|
+
"Nothing selected. Run again, or add directly: npx claude-code-arcane add +testing docker-setup"
|
|
296
|
+
)
|
|
297
|
+
);
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
const expr = items.join(" ");
|
|
301
|
+
const ok = await p.confirm({ message: `Add ${chalk.cyan(expr)}?` });
|
|
302
|
+
if (p.isCancel(ok) || !ok) {
|
|
303
|
+
p.cancel("Add cancelled.");
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
p.outro(
|
|
307
|
+
chalk.dim(`Next time, skip the menu: npx claude-code-arcane add ${expr}`)
|
|
308
|
+
);
|
|
309
|
+
return items;
|
|
310
|
+
}
|
|
311
|
+
async function pickKind(profileCount, skillCount) {
|
|
312
|
+
if (profileCount === 0) return "skills";
|
|
313
|
+
if (skillCount === 0) return "profiles";
|
|
314
|
+
const kind = await p.select({
|
|
315
|
+
message: "What do you want to add?",
|
|
316
|
+
options: [
|
|
317
|
+
{
|
|
318
|
+
value: "profiles",
|
|
319
|
+
label: "Profiles",
|
|
320
|
+
hint: `${profileCount} not installed \u2014 brings its skills, rules and agents`
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
value: "skills",
|
|
324
|
+
label: "Individual skills",
|
|
325
|
+
hint: `${skillCount} not installed \u2014 search by name`
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
});
|
|
329
|
+
if (p.isCancel(kind)) {
|
|
330
|
+
p.cancel("Add cancelled.");
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return kind;
|
|
334
|
+
}
|
|
335
|
+
async function pickProfiles(addable) {
|
|
336
|
+
const options = {};
|
|
337
|
+
for (const group of groupByCategory(addable)) {
|
|
338
|
+
options[group.label] = group.profiles.map((pr) => ({
|
|
339
|
+
value: pr.name,
|
|
340
|
+
label: pr.name,
|
|
341
|
+
hint: hint(pr.description)
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
const selected = await p.groupMultiselect({
|
|
345
|
+
message: "Select profiles to add (space to toggle, enter to confirm)",
|
|
346
|
+
options,
|
|
347
|
+
required: false
|
|
348
|
+
});
|
|
349
|
+
if (p.isCancel(selected)) {
|
|
350
|
+
p.cancel("Add cancelled.");
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
return selected.map((name) => `+${name}`);
|
|
354
|
+
}
|
|
355
|
+
async function pickSkills(addable) {
|
|
356
|
+
const selected = await p.autocompleteMultiselect({
|
|
357
|
+
message: "Search skills to add (type to filter, space to toggle)",
|
|
358
|
+
options: addable.map((s) => ({
|
|
359
|
+
value: s.name,
|
|
360
|
+
label: s.name,
|
|
361
|
+
hint: hint(s.description)
|
|
362
|
+
})),
|
|
363
|
+
required: false
|
|
364
|
+
});
|
|
365
|
+
if (p.isCancel(selected)) {
|
|
366
|
+
p.cancel("Add cancelled.");
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
return selected;
|
|
370
|
+
}
|
|
371
|
+
function hint(description, max = 90) {
|
|
372
|
+
const flat = description.replace(/\s+/g, " ").trim();
|
|
373
|
+
return flat.length > max ? `${flat.slice(0, max - 3)}...` : flat;
|
|
374
|
+
}
|
|
268
375
|
|
|
269
376
|
// src/installer.ts
|
|
270
377
|
import fs6 from "fs";
|
|
@@ -1528,10 +1635,53 @@ Installing profile: ${chalk2.cyan(profileExpr)}`)
|
|
|
1528
1635
|
}
|
|
1529
1636
|
|
|
1530
1637
|
// src/commands/add.ts
|
|
1638
|
+
import fs10 from "fs";
|
|
1639
|
+
import path13 from "path";
|
|
1640
|
+
import chalk3 from "chalk";
|
|
1641
|
+
|
|
1642
|
+
// src/skills-catalog.ts
|
|
1531
1643
|
import fs9 from "fs";
|
|
1532
1644
|
import path12 from "path";
|
|
1533
|
-
|
|
1534
|
-
|
|
1645
|
+
var FRONTMATTER_BYTES = 4096;
|
|
1646
|
+
function listSkills(skillsDir) {
|
|
1647
|
+
if (!fs9.existsSync(skillsDir)) return [];
|
|
1648
|
+
return fs9.readdirSync(skillsDir, { withFileTypes: true }).filter(
|
|
1649
|
+
(d) => d.isDirectory() && !d.name.startsWith("_") && fs9.existsSync(path12.join(skillsDir, d.name, "SKILL.md"))
|
|
1650
|
+
).map((d) => ({
|
|
1651
|
+
name: d.name,
|
|
1652
|
+
description: readDescription(path12.join(skillsDir, d.name, "SKILL.md"))
|
|
1653
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
1654
|
+
}
|
|
1655
|
+
function readDescription(skillFile) {
|
|
1656
|
+
const head = readHead(skillFile);
|
|
1657
|
+
const lines = head.split(/\r?\n/);
|
|
1658
|
+
const idx = lines.findIndex((l) => /^description:/.test(l));
|
|
1659
|
+
if (idx === -1) return "";
|
|
1660
|
+
const inline = lines[idx].slice("description:".length).trim();
|
|
1661
|
+
if (inline === "|" || inline === ">" || /^[|>][-+]?$/.test(inline)) {
|
|
1662
|
+
const block = [];
|
|
1663
|
+
for (const line of lines.slice(idx + 1)) {
|
|
1664
|
+
if (line.trim() === "") continue;
|
|
1665
|
+
if (!/^\s/.test(line)) break;
|
|
1666
|
+
block.push(line.trim());
|
|
1667
|
+
}
|
|
1668
|
+
return block.join(" ");
|
|
1669
|
+
}
|
|
1670
|
+
return inline.replace(/^["']|["']$/g, "").trim();
|
|
1671
|
+
}
|
|
1672
|
+
function readHead(file) {
|
|
1673
|
+
const fd = fs9.openSync(file, "r");
|
|
1674
|
+
try {
|
|
1675
|
+
const buf = Buffer.alloc(FRONTMATTER_BYTES);
|
|
1676
|
+
const read = fs9.readSync(fd, buf, 0, FRONTMATTER_BYTES, 0);
|
|
1677
|
+
return buf.subarray(0, read).toString("utf-8");
|
|
1678
|
+
} finally {
|
|
1679
|
+
fs9.closeSync(fd);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
// src/commands/add.ts
|
|
1684
|
+
async function addCommand(items = []) {
|
|
1535
1685
|
const target = process.cwd();
|
|
1536
1686
|
const source = await resolveContentSource({ quiet: true });
|
|
1537
1687
|
const root = await source.getContentRoot();
|
|
@@ -1544,7 +1694,21 @@ async function addCommand(items) {
|
|
|
1544
1694
|
);
|
|
1545
1695
|
process.exit(1);
|
|
1546
1696
|
}
|
|
1547
|
-
|
|
1697
|
+
if (items.length === 0) {
|
|
1698
|
+
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
1699
|
+
printCatalog(root, manifest.profiles, manifest.installed_skills);
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
const selected = await runAddWizard({
|
|
1703
|
+
profiles: listProfiles(path13.join(root, "profiles")),
|
|
1704
|
+
skills: listSkills(path13.join(root, "skills")),
|
|
1705
|
+
installedProfiles: manifest.profiles,
|
|
1706
|
+
installedSkills: manifest.installed_skills
|
|
1707
|
+
});
|
|
1708
|
+
if (!selected) return;
|
|
1709
|
+
items = selected;
|
|
1710
|
+
}
|
|
1711
|
+
const claudeDir = path13.join(target, ".claude");
|
|
1548
1712
|
const added = [];
|
|
1549
1713
|
const skipped = [];
|
|
1550
1714
|
const notFound = [];
|
|
@@ -1554,8 +1718,8 @@ async function addCommand(items) {
|
|
|
1554
1718
|
for (const item of items) {
|
|
1555
1719
|
if (item.startsWith("+")) {
|
|
1556
1720
|
const profileName = item.slice(1);
|
|
1557
|
-
const profilePath =
|
|
1558
|
-
if (!
|
|
1721
|
+
const profilePath = path13.join(root, "profiles", `${profileName}.yaml`);
|
|
1722
|
+
if (!fs10.existsSync(profilePath)) {
|
|
1559
1723
|
console.error(chalk3.red(` Profile '${profileName}' not found`));
|
|
1560
1724
|
continue;
|
|
1561
1725
|
}
|
|
@@ -1568,10 +1732,10 @@ async function addCommand(items) {
|
|
|
1568
1732
|
}
|
|
1569
1733
|
for (const rule of profile.rules.universal) {
|
|
1570
1734
|
if (!manifest.installed_rules.includes(rule)) {
|
|
1571
|
-
const src =
|
|
1572
|
-
if (
|
|
1573
|
-
ensureDir(
|
|
1574
|
-
|
|
1735
|
+
const src = path13.join(root, "rules", `${rule}.md`);
|
|
1736
|
+
if (fs10.existsSync(src)) {
|
|
1737
|
+
ensureDir(path13.join(claudeDir, "rules"));
|
|
1738
|
+
fs10.copyFileSync(src, path13.join(claudeDir, "rules", `${rule}.md`));
|
|
1575
1739
|
manifest.installed_rules.push(rule);
|
|
1576
1740
|
addedRules.push(rule);
|
|
1577
1741
|
}
|
|
@@ -1579,10 +1743,10 @@ async function addCommand(items) {
|
|
|
1579
1743
|
}
|
|
1580
1744
|
for (const rule of profile.rules.gamedev) {
|
|
1581
1745
|
if (!manifest.installed_rules.includes(rule)) {
|
|
1582
|
-
const src =
|
|
1583
|
-
if (
|
|
1584
|
-
ensureDir(
|
|
1585
|
-
|
|
1746
|
+
const src = path13.join(root, "rules", "gamedev", `${rule}.md`);
|
|
1747
|
+
if (fs10.existsSync(src)) {
|
|
1748
|
+
ensureDir(path13.join(claudeDir, "rules"));
|
|
1749
|
+
fs10.copyFileSync(src, path13.join(claudeDir, "rules", `${rule}.md`));
|
|
1586
1750
|
manifest.installed_rules.push(rule);
|
|
1587
1751
|
addedRules.push(rule);
|
|
1588
1752
|
}
|
|
@@ -1590,9 +1754,9 @@ async function addCommand(items) {
|
|
|
1590
1754
|
}
|
|
1591
1755
|
for (const agentDir of profile.agents) {
|
|
1592
1756
|
if (!manifest.installed_agents.includes(agentDir)) {
|
|
1593
|
-
const src =
|
|
1594
|
-
if (
|
|
1595
|
-
const dst =
|
|
1757
|
+
const src = path13.join(root, "agents", agentDir);
|
|
1758
|
+
if (fs10.existsSync(src)) {
|
|
1759
|
+
const dst = path13.join(claudeDir, "agents", agentDir);
|
|
1596
1760
|
copyDirSync(src, dst);
|
|
1597
1761
|
manifest.installed_agents.push(agentDir);
|
|
1598
1762
|
addedAgents.push(agentDir);
|
|
@@ -1600,9 +1764,9 @@ async function addCommand(items) {
|
|
|
1600
1764
|
}
|
|
1601
1765
|
}
|
|
1602
1766
|
if (profileName === "statusline") {
|
|
1603
|
-
const statuslineSrc =
|
|
1604
|
-
if (
|
|
1605
|
-
|
|
1767
|
+
const statuslineSrc = path13.join(root, "hooks", "statusline.sh");
|
|
1768
|
+
if (fs10.existsSync(statuslineSrc)) {
|
|
1769
|
+
fs10.copyFileSync(statuslineSrc, path13.join(claudeDir, "statusline.sh"));
|
|
1606
1770
|
statuslineAdded = true;
|
|
1607
1771
|
}
|
|
1608
1772
|
}
|
|
@@ -1636,8 +1800,12 @@ async function addCommand(items) {
|
|
|
1636
1800
|
};
|
|
1637
1801
|
writeManifest(target, merged, manifest.profile_command, root);
|
|
1638
1802
|
const totalAdded = added.length + addedRules.length + addedAgents.length + (statuslineAdded ? 1 : 0);
|
|
1639
|
-
console.log(
|
|
1640
|
-
|
|
1803
|
+
console.log(
|
|
1804
|
+
chalk3.bold(
|
|
1805
|
+
totalAdded === 0 ? "\nNothing added." : `
|
|
1806
|
+
Added ${totalAdded} items:`
|
|
1807
|
+
)
|
|
1808
|
+
);
|
|
1641
1809
|
for (const s of added) console.log(chalk3.green(` [ok] skill: ${s}`));
|
|
1642
1810
|
for (const r of addedRules) console.log(chalk3.green(` [ok] rule: ${r}`));
|
|
1643
1811
|
for (const a of addedAgents) console.log(chalk3.green(` [ok] agents: ${a}/`));
|
|
@@ -1646,18 +1814,54 @@ Added ${totalAdded} items:`));
|
|
|
1646
1814
|
console.log(chalk3.dim(` [skip] ${s} (already installed)`));
|
|
1647
1815
|
for (const s of notFound)
|
|
1648
1816
|
console.log(chalk3.red(` [miss] ${s} (not found in source)`));
|
|
1817
|
+
if (totalAdded > 0) {
|
|
1818
|
+
console.log(
|
|
1819
|
+
chalk3.dim(
|
|
1820
|
+
`
|
|
1821
|
+
Now installed: ${manifest.installed_skills.length} skills, ${manifest.installed_rules.length} rules, ${manifest.installed_agents.length} agents.`
|
|
1822
|
+
)
|
|
1823
|
+
);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
function printCatalog(root, installedProfiles, installedSkills) {
|
|
1827
|
+
const profiles = listProfiles(path13.join(root, "profiles")).filter(
|
|
1828
|
+
(pr) => !installedProfiles.includes(pr.name)
|
|
1829
|
+
);
|
|
1830
|
+
const skills = listSkills(path13.join(root, "skills")).filter(
|
|
1831
|
+
(sk) => !installedSkills.includes(sk.name)
|
|
1832
|
+
);
|
|
1833
|
+
if (profiles.length === 0 && skills.length === 0) {
|
|
1834
|
+
console.log(chalk3.dim("\nEverything in the catalog is already installed.\n"));
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
console.log(chalk3.bold("\nProfiles you can add:\n"));
|
|
1838
|
+
for (const group of groupByCategory(profiles)) {
|
|
1839
|
+
console.log(chalk3.cyan(` ${group.label}:`));
|
|
1840
|
+
for (const pr of group.profiles) {
|
|
1841
|
+
console.log(
|
|
1842
|
+
` ${chalk3.green(`+${pr.name}`.padEnd(20))} ${pr.description}`
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
console.log();
|
|
1846
|
+
}
|
|
1847
|
+
console.log(
|
|
1848
|
+
chalk3.dim(` Plus ${skills.length} individual skills \u2014 see \`arcane list\`.`)
|
|
1849
|
+
);
|
|
1850
|
+
console.log(
|
|
1851
|
+
chalk3.dim(" Usage: npx claude-code-arcane add +testing docker-setup\n")
|
|
1852
|
+
);
|
|
1649
1853
|
}
|
|
1650
1854
|
function addSkill(root, target, skill, installed) {
|
|
1651
1855
|
if (installed.includes(skill)) return "skipped";
|
|
1652
|
-
const src =
|
|
1653
|
-
if (!
|
|
1654
|
-
const dst =
|
|
1856
|
+
const src = path13.join(root, "skills", skill);
|
|
1857
|
+
if (!fs10.existsSync(src)) return "not-found";
|
|
1858
|
+
const dst = path13.join(target, ".claude", "skills", skill);
|
|
1655
1859
|
copyDirSync(src, dst);
|
|
1656
1860
|
return "added";
|
|
1657
1861
|
}
|
|
1658
1862
|
function mergePermissions(claudeDir, newPerms) {
|
|
1659
|
-
const settingsPath =
|
|
1660
|
-
if (!
|
|
1863
|
+
const settingsPath = path13.join(claudeDir, "settings.json");
|
|
1864
|
+
if (!fs10.existsSync(settingsPath)) return;
|
|
1661
1865
|
const settings = readJsonSync(settingsPath);
|
|
1662
1866
|
const perms = settings.permissions ?? { allow: [], deny: [] };
|
|
1663
1867
|
const allowSet = new Set(perms.allow);
|
|
@@ -1670,8 +1874,8 @@ function mergePermissions(claudeDir, newPerms) {
|
|
|
1670
1874
|
writeJsonSync(settingsPath, settings);
|
|
1671
1875
|
}
|
|
1672
1876
|
function addStatuslineToSettings(claudeDir) {
|
|
1673
|
-
const settingsPath =
|
|
1674
|
-
if (!
|
|
1877
|
+
const settingsPath = path13.join(claudeDir, "settings.json");
|
|
1878
|
+
if (!fs10.existsSync(settingsPath)) return;
|
|
1675
1879
|
const settings = readJsonSync(settingsPath);
|
|
1676
1880
|
if (settings.statusLine) return;
|
|
1677
1881
|
settings.statusLine = {
|
|
@@ -1682,8 +1886,8 @@ function addStatuslineToSettings(claudeDir) {
|
|
|
1682
1886
|
}
|
|
1683
1887
|
|
|
1684
1888
|
// src/commands/remove.ts
|
|
1685
|
-
import
|
|
1686
|
-
import
|
|
1889
|
+
import fs11 from "fs";
|
|
1890
|
+
import path14 from "path";
|
|
1687
1891
|
import chalk4 from "chalk";
|
|
1688
1892
|
var CORE_SKILLS = [
|
|
1689
1893
|
"commit",
|
|
@@ -1748,7 +1952,7 @@ async function removeCommand(items, opts = {}) {
|
|
|
1748
1952
|
}
|
|
1749
1953
|
manifest.total_skills = manifest.installed_skills.length;
|
|
1750
1954
|
manifest.total_rules = manifest.installed_rules.length;
|
|
1751
|
-
const mp =
|
|
1955
|
+
const mp = path14.join(target, ".claude", "arcane-manifest.json");
|
|
1752
1956
|
writeJsonSync(mp, manifest);
|
|
1753
1957
|
const totalRemoved = removedSkills.length + removedAgents.length + removedProfiles.length;
|
|
1754
1958
|
console.log(chalk4.bold(`
|
|
@@ -1769,9 +1973,9 @@ function removeSkill(target, skill, manifest) {
|
|
|
1769
1973
|
);
|
|
1770
1974
|
return "skipped";
|
|
1771
1975
|
}
|
|
1772
|
-
const skillDir =
|
|
1773
|
-
if (!
|
|
1774
|
-
|
|
1976
|
+
const skillDir = path14.join(target, ".claude", "skills", skill);
|
|
1977
|
+
if (!fs11.existsSync(skillDir)) return "skipped";
|
|
1978
|
+
fs11.rmSync(skillDir, { recursive: true, force: true });
|
|
1775
1979
|
manifest.installed_skills = manifest.installed_skills.filter(
|
|
1776
1980
|
(s) => s !== skill
|
|
1777
1981
|
);
|
|
@@ -1790,8 +1994,8 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
1790
1994
|
);
|
|
1791
1995
|
return { removed: false, skills: [], agents: [] };
|
|
1792
1996
|
}
|
|
1793
|
-
const profilePath =
|
|
1794
|
-
if (!
|
|
1997
|
+
const profilePath = path14.join(root, "profiles", `${profileName}.yaml`);
|
|
1998
|
+
if (!fs11.existsSync(profilePath)) {
|
|
1795
1999
|
console.warn(
|
|
1796
2000
|
chalk4.yellow(` WARNING: Profile '${profileName}.yaml' not found.`)
|
|
1797
2001
|
);
|
|
@@ -1805,8 +2009,8 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
1805
2009
|
const sharedAgents = /* @__PURE__ */ new Set();
|
|
1806
2010
|
const sharedRules = /* @__PURE__ */ new Set();
|
|
1807
2011
|
for (const rp of remainingProfiles) {
|
|
1808
|
-
const rpPath =
|
|
1809
|
-
if (!
|
|
2012
|
+
const rpPath = path14.join(root, "profiles", `${rp}.yaml`);
|
|
2013
|
+
if (!fs11.existsSync(rpPath)) continue;
|
|
1810
2014
|
const rpDef = parseProfile(rpPath);
|
|
1811
2015
|
rpDef.skills.forEach((s) => sharedSkills.add(s));
|
|
1812
2016
|
rpDef.agents.forEach((a) => sharedAgents.add(a));
|
|
@@ -1817,9 +2021,9 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
1817
2021
|
for (const skill of profile.skills) {
|
|
1818
2022
|
if (sharedSkills.has(skill)) continue;
|
|
1819
2023
|
if (CORE_SKILLS.includes(skill)) continue;
|
|
1820
|
-
const skillDir =
|
|
1821
|
-
if (
|
|
1822
|
-
|
|
2024
|
+
const skillDir = path14.join(target, ".claude", "skills", skill);
|
|
2025
|
+
if (fs11.existsSync(skillDir)) {
|
|
2026
|
+
fs11.rmSync(skillDir, { recursive: true, force: true });
|
|
1823
2027
|
removedSkills.push(skill);
|
|
1824
2028
|
}
|
|
1825
2029
|
manifest.installed_skills = manifest.installed_skills.filter(
|
|
@@ -1829,9 +2033,9 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
1829
2033
|
const removedAgents = [];
|
|
1830
2034
|
for (const agent of profile.agents) {
|
|
1831
2035
|
if (sharedAgents.has(agent)) continue;
|
|
1832
|
-
const agentDir =
|
|
1833
|
-
if (
|
|
1834
|
-
|
|
2036
|
+
const agentDir = path14.join(target, ".claude", "agents", agent);
|
|
2037
|
+
if (fs11.existsSync(agentDir)) {
|
|
2038
|
+
fs11.rmSync(agentDir, { recursive: true, force: true });
|
|
1835
2039
|
removedAgents.push(agent);
|
|
1836
2040
|
}
|
|
1837
2041
|
manifest.installed_agents = manifest.installed_agents.filter(
|
|
@@ -1843,44 +2047,44 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
1843
2047
|
...profile.rules.gamedev
|
|
1844
2048
|
]) {
|
|
1845
2049
|
if (sharedRules.has(rule)) continue;
|
|
1846
|
-
const ruleFile =
|
|
1847
|
-
if (
|
|
1848
|
-
|
|
2050
|
+
const ruleFile = path14.join(target, ".claude", "rules", `${rule}.md`);
|
|
2051
|
+
if (fs11.existsSync(ruleFile)) {
|
|
2052
|
+
fs11.rmSync(ruleFile);
|
|
1849
2053
|
}
|
|
1850
2054
|
manifest.installed_rules = manifest.installed_rules.filter(
|
|
1851
2055
|
(r) => r !== rule
|
|
1852
2056
|
);
|
|
1853
2057
|
}
|
|
1854
2058
|
if (profileName === "statusline") {
|
|
1855
|
-
const statuslineFile =
|
|
1856
|
-
if (
|
|
1857
|
-
|
|
2059
|
+
const statuslineFile = path14.join(target, ".claude", "statusline.sh");
|
|
2060
|
+
if (fs11.existsSync(statuslineFile)) {
|
|
2061
|
+
fs11.rmSync(statuslineFile);
|
|
1858
2062
|
}
|
|
1859
|
-
removeStatuslineFromSettings(
|
|
2063
|
+
removeStatuslineFromSettings(path14.join(target, ".claude"));
|
|
1860
2064
|
}
|
|
1861
2065
|
manifest.profiles = remainingProfiles;
|
|
1862
2066
|
manifest.profile_command = remainingProfiles.filter((p2) => p2 !== "core").join("+");
|
|
1863
2067
|
return { removed: true, skills: removedSkills, agents: removedAgents };
|
|
1864
2068
|
}
|
|
1865
2069
|
function removeStatuslineFromSettings(claudeDir) {
|
|
1866
|
-
const settingsPath =
|
|
1867
|
-
if (!
|
|
1868
|
-
const settings = JSON.parse(
|
|
2070
|
+
const settingsPath = path14.join(claudeDir, "settings.json");
|
|
2071
|
+
if (!fs11.existsSync(settingsPath)) return;
|
|
2072
|
+
const settings = JSON.parse(fs11.readFileSync(settingsPath, "utf-8"));
|
|
1869
2073
|
if (settings.statusLine) {
|
|
1870
2074
|
delete settings.statusLine;
|
|
1871
|
-
|
|
2075
|
+
fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
1872
2076
|
}
|
|
1873
2077
|
}
|
|
1874
2078
|
|
|
1875
2079
|
// src/commands/list.ts
|
|
1876
|
-
import
|
|
1877
|
-
import
|
|
2080
|
+
import fs12 from "fs";
|
|
2081
|
+
import path15 from "path";
|
|
1878
2082
|
import chalk5 from "chalk";
|
|
1879
2083
|
async function listCommand() {
|
|
1880
2084
|
const source = await resolveContentSource({ quiet: true });
|
|
1881
2085
|
const root = await source.getContentRoot();
|
|
1882
|
-
const profilesDir =
|
|
1883
|
-
const skillsDir =
|
|
2086
|
+
const profilesDir = path15.join(root, "profiles");
|
|
2087
|
+
const skillsDir = path15.join(root, "skills");
|
|
1884
2088
|
const target = process.cwd();
|
|
1885
2089
|
const manifest = readManifest(target);
|
|
1886
2090
|
const profiles = listProfiles(profilesDir);
|
|
@@ -1897,10 +2101,8 @@ async function listCommand() {
|
|
|
1897
2101
|
}
|
|
1898
2102
|
console.log();
|
|
1899
2103
|
}
|
|
1900
|
-
if (
|
|
1901
|
-
const allSkills =
|
|
1902
|
-
(d) => d.isDirectory() && !d.name.startsWith("_") && fs11.existsSync(path14.join(skillsDir, d.name, "SKILL.md"))
|
|
1903
|
-
).map((d) => d.name).sort();
|
|
2104
|
+
if (fs12.existsSync(skillsDir)) {
|
|
2105
|
+
const allSkills = listSkills(skillsDir).map((s) => s.name);
|
|
1904
2106
|
console.log(chalk5.bold(`
|
|
1905
2107
|
=== Skills (${allSkills.length}) ===
|
|
1906
2108
|
`));
|
|
@@ -1966,8 +2168,8 @@ async function statusCommand() {
|
|
|
1966
2168
|
}
|
|
1967
2169
|
|
|
1968
2170
|
// src/commands/update.ts
|
|
1969
|
-
import
|
|
1970
|
-
import
|
|
2171
|
+
import fs13 from "fs";
|
|
2172
|
+
import path16 from "path";
|
|
1971
2173
|
import os4 from "os";
|
|
1972
2174
|
import chalk7 from "chalk";
|
|
1973
2175
|
|
|
@@ -2039,12 +2241,12 @@ async function updateCommand(opts) {
|
|
|
2039
2241
|
}
|
|
2040
2242
|
}
|
|
2041
2243
|
const cwd = process.cwd();
|
|
2042
|
-
if (
|
|
2244
|
+
if (fs13.existsSync(manifestPath(cwd))) {
|
|
2043
2245
|
registerInstallation(cwd);
|
|
2044
2246
|
}
|
|
2045
2247
|
const targets = pruneRegistry().map((e) => e.path);
|
|
2046
2248
|
const globalTarget = os4.homedir();
|
|
2047
|
-
if (!process.env.VITEST &&
|
|
2249
|
+
if (!process.env.VITEST && fs13.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
|
|
2048
2250
|
targets.push(globalTarget);
|
|
2049
2251
|
}
|
|
2050
2252
|
if (targets.length === 0) {
|
|
@@ -2120,11 +2322,11 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
|
|
|
2120
2322
|
);
|
|
2121
2323
|
}
|
|
2122
2324
|
const profileNames = manifest.profile_command.split("+").filter(Boolean);
|
|
2123
|
-
const profilesDir =
|
|
2325
|
+
const profilesDir = path16.join(root, "profiles");
|
|
2124
2326
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
2125
2327
|
const allRules = [...merged.rules.universal, ...merged.rules.gamedev];
|
|
2126
2328
|
const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
|
|
2127
|
-
const claudeDir =
|
|
2329
|
+
const claudeDir = path16.join(target, ".claude");
|
|
2128
2330
|
const installedHashes = computeContentHashes(claudeDir);
|
|
2129
2331
|
const items = computeUpdatePlan(
|
|
2130
2332
|
manifest.content_hashes ?? null,
|
|
@@ -2317,48 +2519,48 @@ function applyUpdates(items, root, claudeDir, merged, force) {
|
|
|
2317
2519
|
function applyItem(item, root, claudeDir) {
|
|
2318
2520
|
const src = getSourcePath(item, root);
|
|
2319
2521
|
const dst = getInstalledPath(item, claudeDir);
|
|
2320
|
-
if (!src || !
|
|
2522
|
+
if (!src || !fs13.existsSync(src)) return;
|
|
2321
2523
|
if (item.type === "skill" || item.type === "agent") {
|
|
2322
|
-
if (
|
|
2323
|
-
|
|
2524
|
+
if (fs13.existsSync(dst)) {
|
|
2525
|
+
fs13.rmSync(dst, { recursive: true, force: true });
|
|
2324
2526
|
}
|
|
2325
2527
|
copyDirSync(src, dst);
|
|
2326
2528
|
} else {
|
|
2327
|
-
const dir =
|
|
2328
|
-
if (!
|
|
2329
|
-
|
|
2529
|
+
const dir = path16.dirname(dst);
|
|
2530
|
+
if (!fs13.existsSync(dir)) {
|
|
2531
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
2330
2532
|
}
|
|
2331
|
-
|
|
2533
|
+
fs13.copyFileSync(src, dst);
|
|
2332
2534
|
}
|
|
2333
2535
|
}
|
|
2334
2536
|
function backupItem(item, claudeDir) {
|
|
2335
2537
|
const installed = getInstalledPath(item, claudeDir);
|
|
2336
|
-
if (!
|
|
2538
|
+
if (!fs13.existsSync(installed)) return;
|
|
2337
2539
|
const backupPath = installed + ".bak";
|
|
2338
|
-
if (
|
|
2339
|
-
|
|
2540
|
+
if (fs13.existsSync(backupPath)) {
|
|
2541
|
+
fs13.rmSync(backupPath, { recursive: true, force: true });
|
|
2340
2542
|
}
|
|
2341
|
-
|
|
2543
|
+
fs13.renameSync(installed, backupPath);
|
|
2342
2544
|
}
|
|
2343
2545
|
function removeItem(item, claudeDir) {
|
|
2344
2546
|
const installed = getInstalledPath(item, claudeDir);
|
|
2345
|
-
if (
|
|
2346
|
-
|
|
2547
|
+
if (fs13.existsSync(installed)) {
|
|
2548
|
+
fs13.rmSync(installed, { recursive: true, force: true });
|
|
2347
2549
|
}
|
|
2348
2550
|
}
|
|
2349
2551
|
function getSourcePath(item, root) {
|
|
2350
2552
|
switch (item.type) {
|
|
2351
2553
|
case "skill":
|
|
2352
|
-
return
|
|
2554
|
+
return path16.join(root, "skills", item.name);
|
|
2353
2555
|
case "rule": {
|
|
2354
|
-
const direct =
|
|
2355
|
-
if (
|
|
2356
|
-
return
|
|
2556
|
+
const direct = path16.join(root, "rules", item.name);
|
|
2557
|
+
if (fs13.existsSync(direct)) return direct;
|
|
2558
|
+
return path16.join(root, "rules", "gamedev", item.name);
|
|
2357
2559
|
}
|
|
2358
2560
|
case "agent":
|
|
2359
|
-
return
|
|
2561
|
+
return path16.join(root, "agents", item.name);
|
|
2360
2562
|
case "hook":
|
|
2361
|
-
return
|
|
2563
|
+
return path16.join(root, "hooks", item.name);
|
|
2362
2564
|
default:
|
|
2363
2565
|
return null;
|
|
2364
2566
|
}
|
|
@@ -2366,13 +2568,13 @@ function getSourcePath(item, root) {
|
|
|
2366
2568
|
function getInstalledPath(item, claudeDir) {
|
|
2367
2569
|
switch (item.type) {
|
|
2368
2570
|
case "skill":
|
|
2369
|
-
return
|
|
2571
|
+
return path16.join(claudeDir, "skills", item.name);
|
|
2370
2572
|
case "rule":
|
|
2371
|
-
return
|
|
2573
|
+
return path16.join(claudeDir, "rules", item.name);
|
|
2372
2574
|
case "agent":
|
|
2373
|
-
return
|
|
2575
|
+
return path16.join(claudeDir, "agents", item.name);
|
|
2374
2576
|
case "hook":
|
|
2375
|
-
return
|
|
2577
|
+
return path16.join(claudeDir, "hooks", item.name);
|
|
2376
2578
|
}
|
|
2377
2579
|
}
|
|
2378
2580
|
|
|
@@ -2407,8 +2609,8 @@ async function cleanCommand(opts) {
|
|
|
2407
2609
|
}
|
|
2408
2610
|
|
|
2409
2611
|
// src/commands/worktree.ts
|
|
2410
|
-
import
|
|
2411
|
-
import
|
|
2612
|
+
import fs14 from "fs";
|
|
2613
|
+
import path17 from "path";
|
|
2412
2614
|
import { execSync as execSync2 } from "child_process";
|
|
2413
2615
|
import chalk9 from "chalk";
|
|
2414
2616
|
async function worktreeCommand(branch, opts) {
|
|
@@ -2419,8 +2621,8 @@ async function worktreeCommand(branch, opts) {
|
|
|
2419
2621
|
process.exit(1);
|
|
2420
2622
|
}
|
|
2421
2623
|
const repoRoot = wtInfo.isWorktree ? wtInfo.mainWorktreePath : cwd;
|
|
2422
|
-
const worktreePath = opts.path ?
|
|
2423
|
-
if (
|
|
2624
|
+
const worktreePath = opts.path ? path17.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
|
|
2625
|
+
if (fs14.existsSync(worktreePath)) {
|
|
2424
2626
|
console.error(
|
|
2425
2627
|
chalk9.red(`Path already exists: ${worktreePath}`)
|
|
2426
2628
|
);
|
|
@@ -2467,7 +2669,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
2467
2669
|
}
|
|
2468
2670
|
console.log(" Installing Arcane...");
|
|
2469
2671
|
const root = getPackageRoot();
|
|
2470
|
-
const profilesDir =
|
|
2672
|
+
const profilesDir = path17.join(root, "profiles");
|
|
2471
2673
|
const profileNames = profileExpr.split("+").filter(Boolean);
|
|
2472
2674
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
2473
2675
|
const shareFrom = opts.share !== false ? cwd : void 0;
|
|
@@ -2503,13 +2705,13 @@ async function worktreeCommand(branch, opts) {
|
|
|
2503
2705
|
}
|
|
2504
2706
|
}
|
|
2505
2707
|
if (opts.isolate) {
|
|
2506
|
-
const isolateScript =
|
|
2708
|
+
const isolateScript = path17.join(
|
|
2507
2709
|
worktreePath,
|
|
2508
2710
|
".claude",
|
|
2509
2711
|
"skills",
|
|
2510
2712
|
"worktree-isolation"
|
|
2511
2713
|
);
|
|
2512
|
-
if (
|
|
2714
|
+
if (fs14.existsSync(isolateScript)) {
|
|
2513
2715
|
console.log(" Applying worktree isolation...");
|
|
2514
2716
|
try {
|
|
2515
2717
|
execSync2(
|
|
@@ -2544,13 +2746,13 @@ async function worktreeCommand(branch, opts) {
|
|
|
2544
2746
|
}
|
|
2545
2747
|
|
|
2546
2748
|
// src/commands/global.ts
|
|
2547
|
-
import
|
|
2548
|
-
import
|
|
2749
|
+
import fs15 from "fs";
|
|
2750
|
+
import path18 from "path";
|
|
2549
2751
|
import os5 from "os";
|
|
2550
2752
|
import chalk10 from "chalk";
|
|
2551
|
-
var CLAUDE_HOME =
|
|
2552
|
-
var SCRIPTS_DIR =
|
|
2553
|
-
var SETTINGS_PATH =
|
|
2753
|
+
var CLAUDE_HOME = path18.join(os5.homedir(), ".claude");
|
|
2754
|
+
var SCRIPTS_DIR = path18.join(CLAUDE_HOME, "scripts", "worktree-isolation");
|
|
2755
|
+
var SETTINGS_PATH = path18.join(CLAUDE_HOME, "settings.json");
|
|
2554
2756
|
async function globalCommand(opts) {
|
|
2555
2757
|
if (opts.status) {
|
|
2556
2758
|
showStatus();
|
|
@@ -2564,8 +2766,8 @@ async function globalCommand(opts) {
|
|
|
2564
2766
|
}
|
|
2565
2767
|
function showStatus() {
|
|
2566
2768
|
console.log(chalk10.bold("\n=== Arcane Global Status ===\n"));
|
|
2567
|
-
const hasScripts =
|
|
2568
|
-
|
|
2769
|
+
const hasScripts = fs15.existsSync(
|
|
2770
|
+
path18.join(SCRIPTS_DIR, "apply.py")
|
|
2569
2771
|
);
|
|
2570
2772
|
console.log(
|
|
2571
2773
|
` Worktree scripts: ${hasScripts ? chalk10.green("installed") : chalk10.dim("not installed")}`
|
|
@@ -2583,32 +2785,32 @@ function showStatus() {
|
|
|
2583
2785
|
function installGlobal() {
|
|
2584
2786
|
console.log(chalk10.bold("\nInstalling Arcane global hooks...\n"));
|
|
2585
2787
|
const root = getPackageRoot();
|
|
2586
|
-
const scriptsSrc =
|
|
2788
|
+
const scriptsSrc = path18.join(
|
|
2587
2789
|
root,
|
|
2588
2790
|
"skills",
|
|
2589
2791
|
"worktree-isolation",
|
|
2590
2792
|
"scripts"
|
|
2591
2793
|
);
|
|
2592
|
-
if (!
|
|
2794
|
+
if (!fs15.existsSync(scriptsSrc)) {
|
|
2593
2795
|
console.error(
|
|
2594
2796
|
chalk10.red(" worktree-isolation scripts not found in package.")
|
|
2595
2797
|
);
|
|
2596
2798
|
process.exit(1);
|
|
2597
2799
|
}
|
|
2598
|
-
|
|
2800
|
+
fs15.mkdirSync(SCRIPTS_DIR, { recursive: true });
|
|
2599
2801
|
for (const file of ["apply.py", "lib.py"]) {
|
|
2600
|
-
const src =
|
|
2601
|
-
const dst =
|
|
2602
|
-
if (!
|
|
2802
|
+
const src = path18.join(scriptsSrc, file);
|
|
2803
|
+
const dst = path18.join(SCRIPTS_DIR, file);
|
|
2804
|
+
if (!fs15.existsSync(src)) {
|
|
2603
2805
|
console.warn(chalk10.yellow(` WARN: ${file} not found, skipping`));
|
|
2604
2806
|
continue;
|
|
2605
2807
|
}
|
|
2606
|
-
if (
|
|
2808
|
+
if (fs15.existsSync(dst) && fs15.readFileSync(src).equals(fs15.readFileSync(dst))) {
|
|
2607
2809
|
console.log(chalk10.dim(` [skip] ${file} (already up-to-date)`));
|
|
2608
2810
|
} else {
|
|
2609
|
-
|
|
2811
|
+
fs15.copyFileSync(src, dst);
|
|
2610
2812
|
if (process.platform !== "win32") {
|
|
2611
|
-
|
|
2813
|
+
fs15.chmodSync(dst, 493);
|
|
2612
2814
|
}
|
|
2613
2815
|
console.log(chalk10.green(` [ok] ${file} -> ${SCRIPTS_DIR}`));
|
|
2614
2816
|
}
|
|
@@ -2632,8 +2834,8 @@ function installGlobal() {
|
|
|
2632
2834
|
}
|
|
2633
2835
|
function removeGlobal() {
|
|
2634
2836
|
console.log(chalk10.bold("\nRemoving Arcane global hooks...\n"));
|
|
2635
|
-
if (
|
|
2636
|
-
|
|
2837
|
+
if (fs15.existsSync(SCRIPTS_DIR)) {
|
|
2838
|
+
fs15.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
|
|
2637
2839
|
console.log(chalk10.green(" [ok] Removed worktree-isolation scripts"));
|
|
2638
2840
|
} else {
|
|
2639
2841
|
console.log(chalk10.dim(" [skip] Scripts not found"));
|
|
@@ -2647,16 +2849,16 @@ function removeGlobal() {
|
|
|
2647
2849
|
console.log(chalk10.bold.green("\n Global hooks removed.\n"));
|
|
2648
2850
|
}
|
|
2649
2851
|
function readSettings() {
|
|
2650
|
-
if (!
|
|
2852
|
+
if (!fs15.existsSync(SETTINGS_PATH)) return {};
|
|
2651
2853
|
try {
|
|
2652
|
-
return JSON.parse(
|
|
2854
|
+
return JSON.parse(fs15.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
2653
2855
|
} catch {
|
|
2654
2856
|
return {};
|
|
2655
2857
|
}
|
|
2656
2858
|
}
|
|
2657
2859
|
function writeSettings(settings) {
|
|
2658
|
-
|
|
2659
|
-
|
|
2860
|
+
fs15.mkdirSync(path18.dirname(SETTINGS_PATH), { recursive: true });
|
|
2861
|
+
fs15.writeFileSync(
|
|
2660
2862
|
SETTINGS_PATH,
|
|
2661
2863
|
JSON.stringify(settings, null, 2) + "\n",
|
|
2662
2864
|
"utf-8"
|
|
@@ -2713,11 +2915,11 @@ function removeHookFromSettings() {
|
|
|
2713
2915
|
}
|
|
2714
2916
|
|
|
2715
2917
|
// src/update-check.ts
|
|
2716
|
-
import
|
|
2717
|
-
import
|
|
2918
|
+
import fs16 from "fs";
|
|
2919
|
+
import path19 from "path";
|
|
2718
2920
|
import os6 from "os";
|
|
2719
2921
|
import chalk11 from "chalk";
|
|
2720
|
-
var CHECK_FILE =
|
|
2922
|
+
var CHECK_FILE = path19.join(os6.homedir(), ".arcane", "last-check.json");
|
|
2721
2923
|
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
2722
2924
|
var GITHUB_OWNER2 = "SebastianLuser";
|
|
2723
2925
|
var GITHUB_REPO2 = "Claude-Code-Arcane";
|
|
@@ -2842,19 +3044,19 @@ async function getLatestNpmVersion() {
|
|
|
2842
3044
|
}
|
|
2843
3045
|
function readCachedCheck() {
|
|
2844
3046
|
try {
|
|
2845
|
-
if (!
|
|
2846
|
-
return JSON.parse(
|
|
3047
|
+
if (!fs16.existsSync(CHECK_FILE)) return null;
|
|
3048
|
+
return JSON.parse(fs16.readFileSync(CHECK_FILE, "utf-8"));
|
|
2847
3049
|
} catch {
|
|
2848
3050
|
return null;
|
|
2849
3051
|
}
|
|
2850
3052
|
}
|
|
2851
3053
|
function writeCachedCheck(result) {
|
|
2852
3054
|
try {
|
|
2853
|
-
const dir =
|
|
2854
|
-
if (!
|
|
2855
|
-
|
|
3055
|
+
const dir = path19.dirname(CHECK_FILE);
|
|
3056
|
+
if (!fs16.existsSync(dir)) {
|
|
3057
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
2856
3058
|
}
|
|
2857
|
-
|
|
3059
|
+
fs16.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
|
|
2858
3060
|
} catch {
|
|
2859
3061
|
}
|
|
2860
3062
|
}
|
|
@@ -2872,9 +3074,9 @@ program.command("install [profile]").description(
|
|
|
2872
3074
|
).option("--source <source>", "Content source: auto, github, or bundled", "auto").action(async (profile, opts) => {
|
|
2873
3075
|
await installCommand(profile, opts);
|
|
2874
3076
|
});
|
|
2875
|
-
program.command("add
|
|
2876
|
-
"Add skills or profiles. Use +profileName for profiles, skillName for individual skills."
|
|
2877
|
-
).action(async (items) => {
|
|
3077
|
+
program.command("add [items...]").description(
|
|
3078
|
+
"Add skills or profiles. Use +profileName for profiles, skillName for individual skills. Without args, opens an interactive picker."
|
|
3079
|
+
).action(async (items = []) => {
|
|
2878
3080
|
await addCommand(items);
|
|
2879
3081
|
});
|
|
2880
3082
|
program.command("remove <items...>").description(
|