claude-code-arcane 2.6.1 → 2.7.1
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 +16 -0
- package/README.md +8 -4
- package/dist/cli.js +349 -143
- 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
|
|
|
@@ -1994,22 +2196,26 @@ async function selfUpdateNpm(opts = {}) {
|
|
|
1994
2196
|
if (opts.dryRun) {
|
|
1995
2197
|
return { updated: false, skipped: true, reason: "dry-run", fromVersion };
|
|
1996
2198
|
}
|
|
1997
|
-
const npm =
|
|
1998
|
-
const result = spawnSync(
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
timeout: 12e4
|
|
2005
|
-
}
|
|
2006
|
-
);
|
|
2199
|
+
const npm = npmInstallCommand();
|
|
2200
|
+
const result = spawnSync(npm.command, npm.args, {
|
|
2201
|
+
stdio: opts.quiet ? "ignore" : "inherit",
|
|
2202
|
+
encoding: "utf-8",
|
|
2203
|
+
timeout: 12e4,
|
|
2204
|
+
shell: npm.shell
|
|
2205
|
+
});
|
|
2007
2206
|
if (result.status === 0) {
|
|
2008
2207
|
return { updated: true, skipped: false, fromVersion };
|
|
2009
2208
|
}
|
|
2010
2209
|
const reason = result.error ? result.error.message : `npm exited with code ${result.status ?? "unknown"}`;
|
|
2011
2210
|
return { updated: false, skipped: false, reason, fromVersion };
|
|
2012
2211
|
}
|
|
2212
|
+
function npmInstallCommand(platform = process.platform) {
|
|
2213
|
+
const args = ["install", "-g", `${PACKAGE_NAME}@latest`];
|
|
2214
|
+
if (platform === "win32") {
|
|
2215
|
+
return { command: ["npm", ...args].join(" "), args: [], shell: true };
|
|
2216
|
+
}
|
|
2217
|
+
return { command: "npm", args, shell: false };
|
|
2218
|
+
}
|
|
2013
2219
|
function safeVersion() {
|
|
2014
2220
|
try {
|
|
2015
2221
|
return getPackageVersion();
|
|
@@ -2039,12 +2245,12 @@ async function updateCommand(opts) {
|
|
|
2039
2245
|
}
|
|
2040
2246
|
}
|
|
2041
2247
|
const cwd = process.cwd();
|
|
2042
|
-
if (
|
|
2248
|
+
if (fs13.existsSync(manifestPath(cwd))) {
|
|
2043
2249
|
registerInstallation(cwd);
|
|
2044
2250
|
}
|
|
2045
2251
|
const targets = pruneRegistry().map((e) => e.path);
|
|
2046
2252
|
const globalTarget = os4.homedir();
|
|
2047
|
-
if (!process.env.VITEST &&
|
|
2253
|
+
if (!process.env.VITEST && fs13.existsSync(manifestPath(globalTarget)) && !targets.includes(globalTarget)) {
|
|
2048
2254
|
targets.push(globalTarget);
|
|
2049
2255
|
}
|
|
2050
2256
|
if (targets.length === 0) {
|
|
@@ -2120,11 +2326,11 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
|
|
|
2120
2326
|
);
|
|
2121
2327
|
}
|
|
2122
2328
|
const profileNames = manifest.profile_command.split("+").filter(Boolean);
|
|
2123
|
-
const profilesDir =
|
|
2329
|
+
const profilesDir = path16.join(root, "profiles");
|
|
2124
2330
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
2125
2331
|
const allRules = [...merged.rules.universal, ...merged.rules.gamedev];
|
|
2126
2332
|
const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
|
|
2127
|
-
const claudeDir =
|
|
2333
|
+
const claudeDir = path16.join(target, ".claude");
|
|
2128
2334
|
const installedHashes = computeContentHashes(claudeDir);
|
|
2129
2335
|
const items = computeUpdatePlan(
|
|
2130
2336
|
manifest.content_hashes ?? null,
|
|
@@ -2317,48 +2523,48 @@ function applyUpdates(items, root, claudeDir, merged, force) {
|
|
|
2317
2523
|
function applyItem(item, root, claudeDir) {
|
|
2318
2524
|
const src = getSourcePath(item, root);
|
|
2319
2525
|
const dst = getInstalledPath(item, claudeDir);
|
|
2320
|
-
if (!src || !
|
|
2526
|
+
if (!src || !fs13.existsSync(src)) return;
|
|
2321
2527
|
if (item.type === "skill" || item.type === "agent") {
|
|
2322
|
-
if (
|
|
2323
|
-
|
|
2528
|
+
if (fs13.existsSync(dst)) {
|
|
2529
|
+
fs13.rmSync(dst, { recursive: true, force: true });
|
|
2324
2530
|
}
|
|
2325
2531
|
copyDirSync(src, dst);
|
|
2326
2532
|
} else {
|
|
2327
|
-
const dir =
|
|
2328
|
-
if (!
|
|
2329
|
-
|
|
2533
|
+
const dir = path16.dirname(dst);
|
|
2534
|
+
if (!fs13.existsSync(dir)) {
|
|
2535
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
2330
2536
|
}
|
|
2331
|
-
|
|
2537
|
+
fs13.copyFileSync(src, dst);
|
|
2332
2538
|
}
|
|
2333
2539
|
}
|
|
2334
2540
|
function backupItem(item, claudeDir) {
|
|
2335
2541
|
const installed = getInstalledPath(item, claudeDir);
|
|
2336
|
-
if (!
|
|
2542
|
+
if (!fs13.existsSync(installed)) return;
|
|
2337
2543
|
const backupPath = installed + ".bak";
|
|
2338
|
-
if (
|
|
2339
|
-
|
|
2544
|
+
if (fs13.existsSync(backupPath)) {
|
|
2545
|
+
fs13.rmSync(backupPath, { recursive: true, force: true });
|
|
2340
2546
|
}
|
|
2341
|
-
|
|
2547
|
+
fs13.renameSync(installed, backupPath);
|
|
2342
2548
|
}
|
|
2343
2549
|
function removeItem(item, claudeDir) {
|
|
2344
2550
|
const installed = getInstalledPath(item, claudeDir);
|
|
2345
|
-
if (
|
|
2346
|
-
|
|
2551
|
+
if (fs13.existsSync(installed)) {
|
|
2552
|
+
fs13.rmSync(installed, { recursive: true, force: true });
|
|
2347
2553
|
}
|
|
2348
2554
|
}
|
|
2349
2555
|
function getSourcePath(item, root) {
|
|
2350
2556
|
switch (item.type) {
|
|
2351
2557
|
case "skill":
|
|
2352
|
-
return
|
|
2558
|
+
return path16.join(root, "skills", item.name);
|
|
2353
2559
|
case "rule": {
|
|
2354
|
-
const direct =
|
|
2355
|
-
if (
|
|
2356
|
-
return
|
|
2560
|
+
const direct = path16.join(root, "rules", item.name);
|
|
2561
|
+
if (fs13.existsSync(direct)) return direct;
|
|
2562
|
+
return path16.join(root, "rules", "gamedev", item.name);
|
|
2357
2563
|
}
|
|
2358
2564
|
case "agent":
|
|
2359
|
-
return
|
|
2565
|
+
return path16.join(root, "agents", item.name);
|
|
2360
2566
|
case "hook":
|
|
2361
|
-
return
|
|
2567
|
+
return path16.join(root, "hooks", item.name);
|
|
2362
2568
|
default:
|
|
2363
2569
|
return null;
|
|
2364
2570
|
}
|
|
@@ -2366,13 +2572,13 @@ function getSourcePath(item, root) {
|
|
|
2366
2572
|
function getInstalledPath(item, claudeDir) {
|
|
2367
2573
|
switch (item.type) {
|
|
2368
2574
|
case "skill":
|
|
2369
|
-
return
|
|
2575
|
+
return path16.join(claudeDir, "skills", item.name);
|
|
2370
2576
|
case "rule":
|
|
2371
|
-
return
|
|
2577
|
+
return path16.join(claudeDir, "rules", item.name);
|
|
2372
2578
|
case "agent":
|
|
2373
|
-
return
|
|
2579
|
+
return path16.join(claudeDir, "agents", item.name);
|
|
2374
2580
|
case "hook":
|
|
2375
|
-
return
|
|
2581
|
+
return path16.join(claudeDir, "hooks", item.name);
|
|
2376
2582
|
}
|
|
2377
2583
|
}
|
|
2378
2584
|
|
|
@@ -2407,8 +2613,8 @@ async function cleanCommand(opts) {
|
|
|
2407
2613
|
}
|
|
2408
2614
|
|
|
2409
2615
|
// src/commands/worktree.ts
|
|
2410
|
-
import
|
|
2411
|
-
import
|
|
2616
|
+
import fs14 from "fs";
|
|
2617
|
+
import path17 from "path";
|
|
2412
2618
|
import { execSync as execSync2 } from "child_process";
|
|
2413
2619
|
import chalk9 from "chalk";
|
|
2414
2620
|
async function worktreeCommand(branch, opts) {
|
|
@@ -2419,8 +2625,8 @@ async function worktreeCommand(branch, opts) {
|
|
|
2419
2625
|
process.exit(1);
|
|
2420
2626
|
}
|
|
2421
2627
|
const repoRoot = wtInfo.isWorktree ? wtInfo.mainWorktreePath : cwd;
|
|
2422
|
-
const worktreePath = opts.path ?
|
|
2423
|
-
if (
|
|
2628
|
+
const worktreePath = opts.path ? path17.resolve(opts.path) : defaultWorktreePath(repoRoot, branch);
|
|
2629
|
+
if (fs14.existsSync(worktreePath)) {
|
|
2424
2630
|
console.error(
|
|
2425
2631
|
chalk9.red(`Path already exists: ${worktreePath}`)
|
|
2426
2632
|
);
|
|
@@ -2467,7 +2673,7 @@ async function worktreeCommand(branch, opts) {
|
|
|
2467
2673
|
}
|
|
2468
2674
|
console.log(" Installing Arcane...");
|
|
2469
2675
|
const root = getPackageRoot();
|
|
2470
|
-
const profilesDir =
|
|
2676
|
+
const profilesDir = path17.join(root, "profiles");
|
|
2471
2677
|
const profileNames = profileExpr.split("+").filter(Boolean);
|
|
2472
2678
|
const merged = mergeProfiles(profilesDir, profileNames);
|
|
2473
2679
|
const shareFrom = opts.share !== false ? cwd : void 0;
|
|
@@ -2503,13 +2709,13 @@ async function worktreeCommand(branch, opts) {
|
|
|
2503
2709
|
}
|
|
2504
2710
|
}
|
|
2505
2711
|
if (opts.isolate) {
|
|
2506
|
-
const isolateScript =
|
|
2712
|
+
const isolateScript = path17.join(
|
|
2507
2713
|
worktreePath,
|
|
2508
2714
|
".claude",
|
|
2509
2715
|
"skills",
|
|
2510
2716
|
"worktree-isolation"
|
|
2511
2717
|
);
|
|
2512
|
-
if (
|
|
2718
|
+
if (fs14.existsSync(isolateScript)) {
|
|
2513
2719
|
console.log(" Applying worktree isolation...");
|
|
2514
2720
|
try {
|
|
2515
2721
|
execSync2(
|
|
@@ -2544,13 +2750,13 @@ async function worktreeCommand(branch, opts) {
|
|
|
2544
2750
|
}
|
|
2545
2751
|
|
|
2546
2752
|
// src/commands/global.ts
|
|
2547
|
-
import
|
|
2548
|
-
import
|
|
2753
|
+
import fs15 from "fs";
|
|
2754
|
+
import path18 from "path";
|
|
2549
2755
|
import os5 from "os";
|
|
2550
2756
|
import chalk10 from "chalk";
|
|
2551
|
-
var CLAUDE_HOME =
|
|
2552
|
-
var SCRIPTS_DIR =
|
|
2553
|
-
var SETTINGS_PATH =
|
|
2757
|
+
var CLAUDE_HOME = path18.join(os5.homedir(), ".claude");
|
|
2758
|
+
var SCRIPTS_DIR = path18.join(CLAUDE_HOME, "scripts", "worktree-isolation");
|
|
2759
|
+
var SETTINGS_PATH = path18.join(CLAUDE_HOME, "settings.json");
|
|
2554
2760
|
async function globalCommand(opts) {
|
|
2555
2761
|
if (opts.status) {
|
|
2556
2762
|
showStatus();
|
|
@@ -2564,8 +2770,8 @@ async function globalCommand(opts) {
|
|
|
2564
2770
|
}
|
|
2565
2771
|
function showStatus() {
|
|
2566
2772
|
console.log(chalk10.bold("\n=== Arcane Global Status ===\n"));
|
|
2567
|
-
const hasScripts =
|
|
2568
|
-
|
|
2773
|
+
const hasScripts = fs15.existsSync(
|
|
2774
|
+
path18.join(SCRIPTS_DIR, "apply.py")
|
|
2569
2775
|
);
|
|
2570
2776
|
console.log(
|
|
2571
2777
|
` Worktree scripts: ${hasScripts ? chalk10.green("installed") : chalk10.dim("not installed")}`
|
|
@@ -2583,32 +2789,32 @@ function showStatus() {
|
|
|
2583
2789
|
function installGlobal() {
|
|
2584
2790
|
console.log(chalk10.bold("\nInstalling Arcane global hooks...\n"));
|
|
2585
2791
|
const root = getPackageRoot();
|
|
2586
|
-
const scriptsSrc =
|
|
2792
|
+
const scriptsSrc = path18.join(
|
|
2587
2793
|
root,
|
|
2588
2794
|
"skills",
|
|
2589
2795
|
"worktree-isolation",
|
|
2590
2796
|
"scripts"
|
|
2591
2797
|
);
|
|
2592
|
-
if (!
|
|
2798
|
+
if (!fs15.existsSync(scriptsSrc)) {
|
|
2593
2799
|
console.error(
|
|
2594
2800
|
chalk10.red(" worktree-isolation scripts not found in package.")
|
|
2595
2801
|
);
|
|
2596
2802
|
process.exit(1);
|
|
2597
2803
|
}
|
|
2598
|
-
|
|
2804
|
+
fs15.mkdirSync(SCRIPTS_DIR, { recursive: true });
|
|
2599
2805
|
for (const file of ["apply.py", "lib.py"]) {
|
|
2600
|
-
const src =
|
|
2601
|
-
const dst =
|
|
2602
|
-
if (!
|
|
2806
|
+
const src = path18.join(scriptsSrc, file);
|
|
2807
|
+
const dst = path18.join(SCRIPTS_DIR, file);
|
|
2808
|
+
if (!fs15.existsSync(src)) {
|
|
2603
2809
|
console.warn(chalk10.yellow(` WARN: ${file} not found, skipping`));
|
|
2604
2810
|
continue;
|
|
2605
2811
|
}
|
|
2606
|
-
if (
|
|
2812
|
+
if (fs15.existsSync(dst) && fs15.readFileSync(src).equals(fs15.readFileSync(dst))) {
|
|
2607
2813
|
console.log(chalk10.dim(` [skip] ${file} (already up-to-date)`));
|
|
2608
2814
|
} else {
|
|
2609
|
-
|
|
2815
|
+
fs15.copyFileSync(src, dst);
|
|
2610
2816
|
if (process.platform !== "win32") {
|
|
2611
|
-
|
|
2817
|
+
fs15.chmodSync(dst, 493);
|
|
2612
2818
|
}
|
|
2613
2819
|
console.log(chalk10.green(` [ok] ${file} -> ${SCRIPTS_DIR}`));
|
|
2614
2820
|
}
|
|
@@ -2632,8 +2838,8 @@ function installGlobal() {
|
|
|
2632
2838
|
}
|
|
2633
2839
|
function removeGlobal() {
|
|
2634
2840
|
console.log(chalk10.bold("\nRemoving Arcane global hooks...\n"));
|
|
2635
|
-
if (
|
|
2636
|
-
|
|
2841
|
+
if (fs15.existsSync(SCRIPTS_DIR)) {
|
|
2842
|
+
fs15.rmSync(SCRIPTS_DIR, { recursive: true, force: true });
|
|
2637
2843
|
console.log(chalk10.green(" [ok] Removed worktree-isolation scripts"));
|
|
2638
2844
|
} else {
|
|
2639
2845
|
console.log(chalk10.dim(" [skip] Scripts not found"));
|
|
@@ -2647,16 +2853,16 @@ function removeGlobal() {
|
|
|
2647
2853
|
console.log(chalk10.bold.green("\n Global hooks removed.\n"));
|
|
2648
2854
|
}
|
|
2649
2855
|
function readSettings() {
|
|
2650
|
-
if (!
|
|
2856
|
+
if (!fs15.existsSync(SETTINGS_PATH)) return {};
|
|
2651
2857
|
try {
|
|
2652
|
-
return JSON.parse(
|
|
2858
|
+
return JSON.parse(fs15.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
2653
2859
|
} catch {
|
|
2654
2860
|
return {};
|
|
2655
2861
|
}
|
|
2656
2862
|
}
|
|
2657
2863
|
function writeSettings(settings) {
|
|
2658
|
-
|
|
2659
|
-
|
|
2864
|
+
fs15.mkdirSync(path18.dirname(SETTINGS_PATH), { recursive: true });
|
|
2865
|
+
fs15.writeFileSync(
|
|
2660
2866
|
SETTINGS_PATH,
|
|
2661
2867
|
JSON.stringify(settings, null, 2) + "\n",
|
|
2662
2868
|
"utf-8"
|
|
@@ -2713,11 +2919,11 @@ function removeHookFromSettings() {
|
|
|
2713
2919
|
}
|
|
2714
2920
|
|
|
2715
2921
|
// src/update-check.ts
|
|
2716
|
-
import
|
|
2717
|
-
import
|
|
2922
|
+
import fs16 from "fs";
|
|
2923
|
+
import path19 from "path";
|
|
2718
2924
|
import os6 from "os";
|
|
2719
2925
|
import chalk11 from "chalk";
|
|
2720
|
-
var CHECK_FILE =
|
|
2926
|
+
var CHECK_FILE = path19.join(os6.homedir(), ".arcane", "last-check.json");
|
|
2721
2927
|
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
2722
2928
|
var GITHUB_OWNER2 = "SebastianLuser";
|
|
2723
2929
|
var GITHUB_REPO2 = "Claude-Code-Arcane";
|
|
@@ -2842,19 +3048,19 @@ async function getLatestNpmVersion() {
|
|
|
2842
3048
|
}
|
|
2843
3049
|
function readCachedCheck() {
|
|
2844
3050
|
try {
|
|
2845
|
-
if (!
|
|
2846
|
-
return JSON.parse(
|
|
3051
|
+
if (!fs16.existsSync(CHECK_FILE)) return null;
|
|
3052
|
+
return JSON.parse(fs16.readFileSync(CHECK_FILE, "utf-8"));
|
|
2847
3053
|
} catch {
|
|
2848
3054
|
return null;
|
|
2849
3055
|
}
|
|
2850
3056
|
}
|
|
2851
3057
|
function writeCachedCheck(result) {
|
|
2852
3058
|
try {
|
|
2853
|
-
const dir =
|
|
2854
|
-
if (!
|
|
2855
|
-
|
|
3059
|
+
const dir = path19.dirname(CHECK_FILE);
|
|
3060
|
+
if (!fs16.existsSync(dir)) {
|
|
3061
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
2856
3062
|
}
|
|
2857
|
-
|
|
3063
|
+
fs16.writeFileSync(CHECK_FILE, JSON.stringify(result, null, 2) + "\n");
|
|
2858
3064
|
} catch {
|
|
2859
3065
|
}
|
|
2860
3066
|
}
|
|
@@ -2872,9 +3078,9 @@ program.command("install [profile]").description(
|
|
|
2872
3078
|
).option("--source <source>", "Content source: auto, github, or bundled", "auto").action(async (profile, opts) => {
|
|
2873
3079
|
await installCommand(profile, opts);
|
|
2874
3080
|
});
|
|
2875
|
-
program.command("add
|
|
2876
|
-
"Add skills or profiles. Use +profileName for profiles, skillName for individual skills."
|
|
2877
|
-
).action(async (items) => {
|
|
3081
|
+
program.command("add [items...]").description(
|
|
3082
|
+
"Add skills or profiles. Use +profileName for profiles, skillName for individual skills. Without args, opens an interactive picker."
|
|
3083
|
+
).action(async (items = []) => {
|
|
2878
3084
|
await addCommand(items);
|
|
2879
3085
|
});
|
|
2880
3086
|
program.command("remove <items...>").description(
|