claude-code-arcane 2.8.0 → 2.9.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 +27 -0
- package/README.md +2 -2
- package/agents/game/gameplay-programmer.md +0 -1
- package/dist/cli.js +121 -6
- package/docs/USER-GUIDE.md +1 -1
- package/hooks/statusline.sh +17 -4
- package/output-styles/arcane.md +53 -0
- package/package.json +2 -1
- package/profiles/core.yaml +4 -0
- package/rules/code-comments.md +63 -0
- package/rules/gamedev/audio-code.md +0 -1
- package/rules/gamedev/gameplay-code.md +0 -1
- package/rules/gamedev/shader-code.md +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
## [2.9.1](https://github.com/SebastianLuser/claude-code-arcane/compare/v2.9.0...v2.9.1) (2026-09-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **deps:** patch the two vulnerabilities that reach users ([ec0e25c](https://github.com/SebastianLuser/claude-code-arcane/commit/ec0e25cfd57ce15bf13896bee91ca22d6c3a58cf))
|
|
7
|
+
|
|
8
|
+
# [2.9.0](https://github.com/SebastianLuser/claude-code-arcane/compare/v2.8.0...v2.9.0) (2026-09-04)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **settings:** point this repo's status line at its own hooks/statusline.sh ([3b180da](https://github.com/SebastianLuser/claude-code-arcane/commit/3b180daed519095f246b1db3d0038698efa28b80))
|
|
14
|
+
* **tests:** pin ARCANE_HOME so npm test cannot write to real installs ([a3836cc](https://github.com/SebastianLuser/claude-code-arcane/commit/a3836cce4820b5651a92df9ad38d00510a74ec38))
|
|
15
|
+
* **update:** refresh statusline.sh and output-styles, and honor --force ([9ebdb20](https://github.com/SebastianLuser/claude-code-arcane/commit/9ebdb2070501837607d7998fe426cd6578c1cbcd))
|
|
16
|
+
* **update:** sync unhashed files past the version gate and activate the style ([0a998be](https://github.com/SebastianLuser/claude-code-arcane/commit/0a998be8ab6cc8848862c83ed547377ab0dd3a8d))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **core:** ship the arcane output style and a code-comments rule ([2a981bf](https://github.com/SebastianLuser/claude-code-arcane/commit/2a981bf847d8c41143da1353dd230f98ca7f7e11))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Performance Improvements
|
|
25
|
+
|
|
26
|
+
* **statusline:** cut ~2s of process spawns from the status line ([108afe4](https://github.com/SebastianLuser/claude-code-arcane/commit/108afe409f3a51e54e22587317bbebeba04f3e07))
|
|
27
|
+
|
|
1
28
|
# [2.8.0](https://github.com/SebastianLuser/claude-code-arcane/compare/v2.7.1...v2.8.0) (2026-08-27)
|
|
2
29
|
|
|
3
30
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Claude Code Arcane
|
|
2
2
|
|
|
3
|
-
> **437 skills, 115 agents, 17 hooks and
|
|
3
|
+
> **437 skills, 115 agents, 17 hooks and 31 rules for Claude Code — selective deploy by profile.**
|
|
4
4
|
|
|
5
5
|
A configuration harness installable via `npx`. Pick a **profile** that matches your stack and only the relevant tools get installed into your project's `.claude/` directory.
|
|
6
6
|
|
|
@@ -439,7 +439,7 @@ claude-code-arcane/
|
|
|
439
439
|
├── profiles/ # 38 profiles (YAML)
|
|
440
440
|
├── agents/ # 17 dirs, 109 agents (Markdown)
|
|
441
441
|
├── hooks/ # 17 lifecycle hooks (Bash)
|
|
442
|
-
├── rules/ #
|
|
442
|
+
├── rules/ # 31 rules (Markdown)
|
|
443
443
|
├── templates/ # Gamedev templates
|
|
444
444
|
├── docs/ # Documentation
|
|
445
445
|
└── skills-selftest/ # QA framework
|
|
@@ -87,7 +87,6 @@ If an ADR exists for this system:
|
|
|
87
87
|
- State machines must have explicit transition tables
|
|
88
88
|
- No direct references to UI code (use events/signals)
|
|
89
89
|
- Frame-rate independent logic (delta time everywhere)
|
|
90
|
-
- Document the design doc each feature implements in code comments
|
|
91
90
|
|
|
92
91
|
### What This Agent Must NOT Do
|
|
93
92
|
|
package/dist/cli.js
CHANGED
|
@@ -26,6 +26,7 @@ function parseProfile(filePath) {
|
|
|
26
26
|
},
|
|
27
27
|
agents: raw.agents ?? [],
|
|
28
28
|
hooks: raw.hooks ?? [],
|
|
29
|
+
output_styles: raw.output_styles ?? [],
|
|
29
30
|
permissions: {
|
|
30
31
|
allow: perms.allow ?? [],
|
|
31
32
|
deny: perms.deny ?? []
|
|
@@ -67,6 +68,7 @@ function mergeProfiles(profilesDir, profileNames) {
|
|
|
67
68
|
const seenRulesGamedev = /* @__PURE__ */ new Set();
|
|
68
69
|
const seenAgents = /* @__PURE__ */ new Set();
|
|
69
70
|
const seenHooks = /* @__PURE__ */ new Set();
|
|
71
|
+
const seenOutputStyles = /* @__PURE__ */ new Set();
|
|
70
72
|
const allPermAllow = [];
|
|
71
73
|
const allPermDeny = [];
|
|
72
74
|
const skills = [];
|
|
@@ -74,6 +76,7 @@ function mergeProfiles(profilesDir, profileNames) {
|
|
|
74
76
|
const rulesGamedev = [];
|
|
75
77
|
const agents = [];
|
|
76
78
|
const hooks = [];
|
|
79
|
+
const outputStyles = [];
|
|
77
80
|
for (const name of namesToLoad) {
|
|
78
81
|
const filePath = path.join(profilesDir, `${name}.yaml`);
|
|
79
82
|
if (!fs.existsSync(filePath)) {
|
|
@@ -112,6 +115,12 @@ function mergeProfiles(profilesDir, profileNames) {
|
|
|
112
115
|
hooks.push(h);
|
|
113
116
|
}
|
|
114
117
|
}
|
|
118
|
+
for (const o of profile.output_styles) {
|
|
119
|
+
if (!seenOutputStyles.has(o)) {
|
|
120
|
+
seenOutputStyles.add(o);
|
|
121
|
+
outputStyles.push(o);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
115
124
|
allPermAllow.push(...profile.permissions.allow);
|
|
116
125
|
allPermDeny.push(...profile.permissions.deny);
|
|
117
126
|
}
|
|
@@ -121,6 +130,7 @@ function mergeProfiles(profilesDir, profileNames) {
|
|
|
121
130
|
rules: { universal: rulesUniv, gamedev: rulesGamedev },
|
|
122
131
|
agents,
|
|
123
132
|
hooks,
|
|
133
|
+
output_styles: outputStyles,
|
|
124
134
|
permissions: {
|
|
125
135
|
allow: [...new Set(allPermAllow)],
|
|
126
136
|
deny: [...new Set(allPermDeny)]
|
|
@@ -805,6 +815,7 @@ function computeSourceHashes(packageRoot, skills, rules, agents) {
|
|
|
805
815
|
const hooksDir = path7.join(packageRoot, "hooks");
|
|
806
816
|
if (fs6.existsSync(hooksDir)) {
|
|
807
817
|
for (const entry of fs6.readdirSync(hooksDir, { withFileTypes: true })) {
|
|
818
|
+
if (entry.name === "statusline.sh") continue;
|
|
808
819
|
if (!entry.isDirectory()) {
|
|
809
820
|
hashes.hooks[entry.name] = hashFile(path7.join(hooksDir, entry.name));
|
|
810
821
|
}
|
|
@@ -990,6 +1001,7 @@ var Installer = class {
|
|
|
990
1001
|
this.copyHooks();
|
|
991
1002
|
this.copySkills();
|
|
992
1003
|
this.copyRules();
|
|
1004
|
+
this.copyOutputStyles();
|
|
993
1005
|
this.copyAgents();
|
|
994
1006
|
this.copyDocs();
|
|
995
1007
|
this.generateSettings();
|
|
@@ -1024,6 +1036,9 @@ var Installer = class {
|
|
|
1024
1036
|
for (const d of ["skills", "rules", "agents", "docs"]) {
|
|
1025
1037
|
ensureDir(path8.join(this.claudeDir, d));
|
|
1026
1038
|
}
|
|
1039
|
+
if (this.merged.output_styles.length > 0) {
|
|
1040
|
+
ensureDir(path8.join(this.claudeDir, "output-styles"));
|
|
1041
|
+
}
|
|
1027
1042
|
}
|
|
1028
1043
|
get hasStatusline() {
|
|
1029
1044
|
return this.merged.loaded.includes("statusline");
|
|
@@ -1147,6 +1162,26 @@ var Installer = class {
|
|
|
1147
1162
|
}
|
|
1148
1163
|
}
|
|
1149
1164
|
}
|
|
1165
|
+
copyOutputStyles() {
|
|
1166
|
+
if (this.merged.output_styles.length === 0) return;
|
|
1167
|
+
this.log("\nOutput styles:");
|
|
1168
|
+
for (const style of this.merged.output_styles) {
|
|
1169
|
+
const src = path8.join(this.root, "output-styles", `${style}.md`);
|
|
1170
|
+
if (!fs7.existsSync(src)) {
|
|
1171
|
+
this.log(` WARN: Output style '${style}.md' not found`);
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
if (this.dryRun) {
|
|
1175
|
+
this.log(` [dry-run] ${style}.md`);
|
|
1176
|
+
} else {
|
|
1177
|
+
fs7.copyFileSync(
|
|
1178
|
+
src,
|
|
1179
|
+
path8.join(this.claudeDir, "output-styles", `${style}.md`)
|
|
1180
|
+
);
|
|
1181
|
+
this.log(` [ok] ${style}.md`);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1150
1185
|
copyAgents() {
|
|
1151
1186
|
if (this.merged.agents.length === 0) return;
|
|
1152
1187
|
this.log("\nAgents:");
|
|
@@ -1204,6 +1239,10 @@ var Installer = class {
|
|
|
1204
1239
|
command: "bash .claude/statusline.sh"
|
|
1205
1240
|
};
|
|
1206
1241
|
}
|
|
1242
|
+
const activeStyle = this.merged.output_styles[0];
|
|
1243
|
+
if (activeStyle) {
|
|
1244
|
+
settings.outputStyle = activeStyle;
|
|
1245
|
+
}
|
|
1207
1246
|
if (this.dryRun) {
|
|
1208
1247
|
this.log(
|
|
1209
1248
|
` [dry-run] settings.json (${this.merged.permissions.allow.length} allow, ${this.merged.permissions.deny.length} deny)`
|
|
@@ -1290,7 +1329,7 @@ function storeInCache(version, contentDir) {
|
|
|
1290
1329
|
fs8.rmSync(versionDir, { recursive: true, force: true });
|
|
1291
1330
|
}
|
|
1292
1331
|
fs8.mkdirSync(versionDir, { recursive: true });
|
|
1293
|
-
for (const dir of ["skills", "rules", "agents", "hooks", "profiles", "templates", "docs"]) {
|
|
1332
|
+
for (const dir of ["skills", "rules", "agents", "hooks", "profiles", "templates", "docs", "output-styles"]) {
|
|
1294
1333
|
const src = path9.join(contentDir, dir);
|
|
1295
1334
|
if (fs8.existsSync(src)) {
|
|
1296
1335
|
copyDirRecursive(src, path9.join(versionDir, dir));
|
|
@@ -1847,6 +1886,7 @@ async function addCommand(items = []) {
|
|
|
1847
1886
|
rules: { universal: manifest.installed_rules, gamedev: [] },
|
|
1848
1887
|
agents: manifest.installed_agents,
|
|
1849
1888
|
hooks: [],
|
|
1889
|
+
output_styles: [],
|
|
1850
1890
|
permissions: { allow: [], deny: [] }
|
|
1851
1891
|
};
|
|
1852
1892
|
writeManifest(target, merged, manifest.profile_command, root);
|
|
@@ -2355,16 +2395,34 @@ async function updateTarget(target, opts) {
|
|
|
2355
2395
|
const root = await contentSource.getContentRoot();
|
|
2356
2396
|
const currentVersion = await contentSource.getVersion();
|
|
2357
2397
|
const installedVersion = manifest.source_version ?? manifest.arcane_version;
|
|
2398
|
+
const profileNamesForSync = manifest.profile_command.split("+").filter(Boolean);
|
|
2399
|
+
const claudeDirForSync = path17.join(target, ".claude");
|
|
2400
|
+
const earlySync = syncUnhashedFiles(
|
|
2401
|
+
root,
|
|
2402
|
+
claudeDirForSync,
|
|
2403
|
+
mergeProfiles(path17.join(root, "profiles"), profileNamesForSync),
|
|
2404
|
+
opts.dryRun ?? false
|
|
2405
|
+
);
|
|
2406
|
+
if (earlySync.length > 0 && !opts.quiet) {
|
|
2407
|
+
const prefix = opts.dryRun ? "[dry-run] " : "";
|
|
2408
|
+
console.log(`
|
|
2409
|
+
${prefix}${chalk7.cyan("Refreshed:")}`);
|
|
2410
|
+
for (const label of earlySync) {
|
|
2411
|
+
console.log(` ~ ${label}`);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2358
2414
|
if (installedVersion === currentVersion && !opts.force) {
|
|
2359
2415
|
if (!opts.quiet) {
|
|
2360
|
-
console.log(
|
|
2416
|
+
console.log(
|
|
2417
|
+
earlySync.length > 0 ? chalk7.green(`Refreshed ${earlySync.length} file(s); otherwise up to date (v${currentVersion}).`) : chalk7.green(`Already up to date (v${currentVersion}).`)
|
|
2418
|
+
);
|
|
2361
2419
|
}
|
|
2362
2420
|
return {
|
|
2363
2421
|
target,
|
|
2364
|
-
status: "up-to-date",
|
|
2422
|
+
status: earlySync.length > 0 ? "updated" : "up-to-date",
|
|
2365
2423
|
fromVersion: installedVersion,
|
|
2366
2424
|
toVersion: currentVersion,
|
|
2367
|
-
updated:
|
|
2425
|
+
updated: earlySync.length,
|
|
2368
2426
|
skipped: 0,
|
|
2369
2427
|
removed: 0
|
|
2370
2428
|
};
|
|
@@ -2382,6 +2440,7 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
|
|
|
2382
2440
|
const sourceHashes = computeSourceHashes(root, merged.skills, allRules, merged.agents);
|
|
2383
2441
|
const claudeDir = path17.join(target, ".claude");
|
|
2384
2442
|
const installedHashes = computeContentHashes(claudeDir);
|
|
2443
|
+
const unhashed = earlySync;
|
|
2385
2444
|
const items = computeUpdatePlan(
|
|
2386
2445
|
manifest.content_hashes ?? null,
|
|
2387
2446
|
installedHashes,
|
|
@@ -2390,10 +2449,11 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
|
|
|
2390
2449
|
const updates = items.filter((i) => i.action === "update" || i.action === "add" || i.action === "conflict");
|
|
2391
2450
|
const skipped = items.filter((i) => i.action === "skip-customized" || i.action === "skip-unchanged");
|
|
2392
2451
|
const removed = items.filter((i) => i.action === "remove");
|
|
2452
|
+
const forcible = opts.force ? items.filter((i) => i.action === "skip-customized") : [];
|
|
2393
2453
|
if (!opts.quiet) {
|
|
2394
2454
|
printUpdateSummary(items, opts.dryRun ?? false);
|
|
2395
2455
|
}
|
|
2396
|
-
if (updates.length === 0 && removed.length === 0) {
|
|
2456
|
+
if (updates.length === 0 && removed.length === 0 && forcible.length === 0) {
|
|
2397
2457
|
if (!opts.dryRun) {
|
|
2398
2458
|
updateManifestFields(target, {
|
|
2399
2459
|
arcane_version: currentVersion,
|
|
@@ -2408,7 +2468,10 @@ Update available: ${chalk7.red(installedVersion)} -> ${chalk7.green(currentVersi
|
|
|
2408
2468
|
});
|
|
2409
2469
|
}
|
|
2410
2470
|
if (!opts.quiet) {
|
|
2411
|
-
console.log(
|
|
2471
|
+
console.log(
|
|
2472
|
+
unhashed.length > 0 ? chalk7.green(`
|
|
2473
|
+
Refreshed ${unhashed.length} file(s); no other changes to apply.`) : chalk7.green("\nNo changes to apply.")
|
|
2474
|
+
);
|
|
2412
2475
|
}
|
|
2413
2476
|
return {
|
|
2414
2477
|
target,
|
|
@@ -2570,6 +2633,58 @@ function applyUpdates(items, root, claudeDir, merged, force) {
|
|
|
2570
2633
|
}
|
|
2571
2634
|
}
|
|
2572
2635
|
}
|
|
2636
|
+
function syncUnhashedFiles(root, claudeDir, merged, dryRun) {
|
|
2637
|
+
const targets = [];
|
|
2638
|
+
const statuslineDst = path17.join(claudeDir, "statusline.sh");
|
|
2639
|
+
if (merged.loaded.includes("statusline") || fs14.existsSync(statuslineDst)) {
|
|
2640
|
+
targets.push({
|
|
2641
|
+
src: path17.join(root, "hooks", "statusline.sh"),
|
|
2642
|
+
dst: statuslineDst,
|
|
2643
|
+
label: "statusline.sh"
|
|
2644
|
+
});
|
|
2645
|
+
}
|
|
2646
|
+
for (const style of merged.output_styles) {
|
|
2647
|
+
targets.push({
|
|
2648
|
+
src: path17.join(root, "output-styles", `${style}.md`),
|
|
2649
|
+
dst: path17.join(claudeDir, "output-styles", `${style}.md`),
|
|
2650
|
+
label: `output-styles/${style}.md`
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
const changed = [];
|
|
2654
|
+
for (const t of targets) {
|
|
2655
|
+
if (!fs14.existsSync(t.src)) continue;
|
|
2656
|
+
const src = fs14.readFileSync(t.src);
|
|
2657
|
+
if (fs14.existsSync(t.dst) && src.equals(fs14.readFileSync(t.dst))) continue;
|
|
2658
|
+
changed.push(t.label);
|
|
2659
|
+
if (dryRun) continue;
|
|
2660
|
+
if (fs14.existsSync(t.dst)) {
|
|
2661
|
+
const bak = `${t.dst}.bak`;
|
|
2662
|
+
if (fs14.existsSync(bak)) fs14.rmSync(bak, { force: true });
|
|
2663
|
+
fs14.renameSync(t.dst, bak);
|
|
2664
|
+
}
|
|
2665
|
+
fs14.mkdirSync(path17.dirname(t.dst), { recursive: true });
|
|
2666
|
+
fs14.copyFileSync(t.src, t.dst);
|
|
2667
|
+
}
|
|
2668
|
+
const activeStyle = merged.output_styles[0];
|
|
2669
|
+
if (activeStyle) {
|
|
2670
|
+
const settingsPath = path17.join(claudeDir, "settings.json");
|
|
2671
|
+
if (fs14.existsSync(settingsPath)) {
|
|
2672
|
+
try {
|
|
2673
|
+
const settings = JSON.parse(fs14.readFileSync(settingsPath, "utf-8"));
|
|
2674
|
+
if (settings.outputStyle !== activeStyle) {
|
|
2675
|
+
changed.push(`settings.json (outputStyle: ${activeStyle})`);
|
|
2676
|
+
if (!dryRun) {
|
|
2677
|
+
settings.outputStyle = activeStyle;
|
|
2678
|
+
fs14.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
2679
|
+
`, "utf-8");
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
} catch {
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
return changed;
|
|
2687
|
+
}
|
|
2573
2688
|
function applyItem(item, root, claudeDir) {
|
|
2574
2689
|
const src = getSourcePath(item, root);
|
|
2575
2690
|
const dst = getInstalledPath(item, claudeDir);
|
package/docs/USER-GUIDE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What is Arcane?
|
|
4
4
|
|
|
5
|
-
Arcane is a configuration harness for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). It provides a curated library of **437 skills**, **115 agents**, **17 hooks**, and **
|
|
5
|
+
Arcane is a configuration harness for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). It provides a curated library of **437 skills**, **115 agents**, **17 hooks**, and **31 rules** that you selectively deploy into any project. Instead of shipping everything to every project (wasting tokens and degrading performance), you pick a **profile** that matches your stack and only the relevant tools get installed.
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
package/hooks/statusline.sh
CHANGED
|
@@ -15,12 +15,17 @@ fi
|
|
|
15
15
|
# Skills/agents Claude can actually invoke = global (~/.claude) + project (.claude),
|
|
16
16
|
# deduped by name (project overrides global). Follow -L so .claude/skills junctions count.
|
|
17
17
|
# Top-level only (mindepth/maxdepth 2) so bundled sub-skills don't inflate the total.
|
|
18
|
+
#
|
|
19
|
+
# Names are extracted with a single awk, never `xargs -n1 dirname|basename`: that spawned one
|
|
20
|
+
# process per file (~160 here), which cost ~1.7s of the ~2.5s total on Windows and made the
|
|
21
|
+
# status line miss its execution window under load — Claude Code drops the output on a
|
|
22
|
+
# non-zero exit or an abort, so the bar vanished at random.
|
|
18
23
|
SKILLS=$( { find -L "$HOME/.claude/skills" -mindepth 2 -maxdepth 2 -name "SKILL.md" 2>/dev/null;
|
|
19
24
|
find -L .claude/skills -mindepth 2 -maxdepth 2 -name "SKILL.md" 2>/dev/null; } \
|
|
20
|
-
|
|
|
25
|
+
| awk -F/ 'NF>1{print $(NF-1)}' | sort -u | wc -l | tr -d ' ')
|
|
21
26
|
AGENTS=$( { find -L "$HOME/.claude/agents" -name "*.md" 2>/dev/null;
|
|
22
27
|
find -L .claude/agents -name "*.md" 2>/dev/null; } \
|
|
23
|
-
|
|
|
28
|
+
| awk -F/ '{print $NF}' | sort -u | wc -l | tr -d ' ')
|
|
24
29
|
|
|
25
30
|
PYCMD=$(command -v python 2>/dev/null || command -v python3 2>/dev/null || command -v py 2>/dev/null)
|
|
26
31
|
|
|
@@ -117,8 +122,16 @@ PREFIX="🔮 ${PROJECT}"
|
|
|
117
122
|
|
|
118
123
|
PARTS="${PREFIX}"
|
|
119
124
|
[ -n "$BRANCH" ] && PARTS="${PARTS} | 🌿 ${BRANCH}"
|
|
120
|
-
PARTS="${PARTS} | 🛠️ ${SKILLS} skills"
|
|
121
|
-
|
|
125
|
+
PARTS="${PARTS} | 🛠️ ${SKILLS:-0} skills"
|
|
126
|
+
case "$AGENTS" in
|
|
127
|
+
''|*[!0-9]*) ;;
|
|
128
|
+
0) ;;
|
|
129
|
+
*) PARTS="${PARTS} | 🤖 ${AGENTS} agents" ;;
|
|
130
|
+
esac
|
|
122
131
|
[ -n "$EXTRA" ] && PARTS="${PARTS} | ${EXTRA}"
|
|
123
132
|
|
|
124
133
|
echo "$PARTS"
|
|
134
|
+
|
|
135
|
+
# Claude Code discards the status line on a non-zero exit, so never inherit one from the
|
|
136
|
+
# last conditional above.
|
|
137
|
+
exit 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arcane
|
|
3
|
+
description: Respuestas compactas, tablas para comparar, lenguaje literal y estructura legible — pensado para lectura con TEA y dislexia.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Arcane
|
|
7
|
+
|
|
8
|
+
Respondé de forma compacta, literal y estructurada. Estas instrucciones son sobre **la
|
|
9
|
+
forma** de la respuesta, no sobre el alcance del trabajo: no reducen lo que hay que hacer ni
|
|
10
|
+
autorizan entregar menos.
|
|
11
|
+
|
|
12
|
+
## Largo
|
|
13
|
+
|
|
14
|
+
- **La respuesta primero.** Primera línea: qué pasó o qué contestás. El detalle va después,
|
|
15
|
+
para quien lo quiera.
|
|
16
|
+
- **Sin preámbulo.** No repitas la pregunta, no anuncies lo que vas a hacer, no cierres
|
|
17
|
+
resumiendo lo que acabás de decir.
|
|
18
|
+
- **Un tema por párrafo, tres líneas como máximo.** Si un párrafo crece, partilo o pasalo a
|
|
19
|
+
lista.
|
|
20
|
+
- **Cortá cuando terminaste.** Nada de relleno, cierres corteses ni ofertas genéricas de
|
|
21
|
+
ayuda.
|
|
22
|
+
- **No enumeres lo que descartaste.** Si evaluaste opciones y elegiste, decí la elegida y el
|
|
23
|
+
porqué en una línea.
|
|
24
|
+
|
|
25
|
+
## Estructura
|
|
26
|
+
|
|
27
|
+
- **Tabla siempre que compares.** Dos o más cosas con dos o más atributos (opciones,
|
|
28
|
+
versiones, proyectos, trade-offs) van en tabla, no en prosa ni en bullets paralelos. Una
|
|
29
|
+
columna por atributo, una fila por cosa.
|
|
30
|
+
- **Listas para pasos y hallazgos.** Prosa solo para el razonamiento que une las cosas.
|
|
31
|
+
- **Títulos cuando hay más de un tema.** Que se pueda saltar a la parte que importa sin
|
|
32
|
+
leer todo.
|
|
33
|
+
- **`backticks` en todo identificador** — archivos, comandos, ramas, funciones, flags.
|
|
34
|
+
- **Bold para lo importante**, no italics: es más fácil de decodificar. Nunca MAYÚSCULAS
|
|
35
|
+
para enfatizar, deforman la silueta de la palabra.
|
|
36
|
+
|
|
37
|
+
## Lenguaje
|
|
38
|
+
|
|
39
|
+
- **Literal.** Sin ironía, sarcasmo, modismos, metáforas ni preguntas retóricas. Si algo
|
|
40
|
+
está mal, decilo derecho.
|
|
41
|
+
- **Un término por concepto, siempre el mismo.** No alternes sinónimos para la misma cosa:
|
|
42
|
+
si es `worktree`, es `worktree` en toda la respuesta.
|
|
43
|
+
- **Oraciones cortas, una idea cada una.** Preferí punto sobre subordinada.
|
|
44
|
+
- **Voz activa y sujeto explícito.** Decí quién hace qué.
|
|
45
|
+
- **La incertidumbre se declara, no se insinúa.** "No sé si X" o "no lo verifiqué", en vez
|
|
46
|
+
de prosa condicional que deja al lector adivinando el nivel de confianza.
|
|
47
|
+
- **Decí explícitamente qué esperás del otro.** Si hace falta una decisión, que se vea que
|
|
48
|
+
hace falta y cuáles son las alternativas.
|
|
49
|
+
|
|
50
|
+
## Números y datos
|
|
51
|
+
|
|
52
|
+
- **El dato antes que el adjetivo.** "62 commits atrás" en vez de "bastante atrasado".
|
|
53
|
+
- **Unidad y fecha completas.** `2026-09-04`, no "ayer"; `312 KB`, no "poco".
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-arcane",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "Skills, agents, hooks and rules for Claude Code — installable via npx claude-code-arcane",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"rules/",
|
|
16
16
|
"templates/",
|
|
17
17
|
"docs/",
|
|
18
|
+
"output-styles/",
|
|
18
19
|
"CHANGELOG.md"
|
|
19
20
|
],
|
|
20
21
|
"scripts": {
|
package/profiles/core.yaml
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.cs"
|
|
4
|
+
- "**/*.ts"
|
|
5
|
+
- "**/*.tsx"
|
|
6
|
+
- "**/*.js"
|
|
7
|
+
- "**/*.jsx"
|
|
8
|
+
- "**/*.py"
|
|
9
|
+
- "**/*.go"
|
|
10
|
+
- "**/*.java"
|
|
11
|
+
- "**/*.kt"
|
|
12
|
+
- "**/*.swift"
|
|
13
|
+
- "**/*.rb"
|
|
14
|
+
- "**/*.rs"
|
|
15
|
+
- "**/*.php"
|
|
16
|
+
- "**/*.dart"
|
|
17
|
+
- "**/*.c"
|
|
18
|
+
- "**/*.cpp"
|
|
19
|
+
- "**/*.cc"
|
|
20
|
+
- "**/*.h"
|
|
21
|
+
- "**/*.hpp"
|
|
22
|
+
- "**/*.sh"
|
|
23
|
+
- "**/*.sql"
|
|
24
|
+
- "**/*.shader"
|
|
25
|
+
- "**/*.hlsl"
|
|
26
|
+
- "**/*.glsl"
|
|
27
|
+
- "**/*.gd"
|
|
28
|
+
- "**/*.lua"
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Code Comments Rule
|
|
32
|
+
|
|
33
|
+
Comments are written in **English**, whatever language the conversation is in.
|
|
34
|
+
|
|
35
|
+
## Never write
|
|
36
|
+
|
|
37
|
+
- **Session narration.** No comment records what was decided, changed, tried or removed
|
|
38
|
+
while working. Not `// removed the old validation`, not `// now uses a dictionary instead
|
|
39
|
+
of a list`, not `// moved above the null check as discussed`, not `// changed from 30 to
|
|
40
|
+
50`. The commit message carries the story; the code carries the result.
|
|
41
|
+
- **WHAT restated.** The code already says what it does. `// increment the counter` above
|
|
42
|
+
`counter++` is noise.
|
|
43
|
+
- **Ticket, task or doc references.** No `// added for JIRA-123`, no `// part of sprint04`,
|
|
44
|
+
no `// implements the boss design doc`. Traceability lives in the commit and the PR, where
|
|
45
|
+
it stays accurate and searchable.
|
|
46
|
+
- **Attribution or dates.** No `// author:`, no `// created 2026-09-04`. Git owns that and
|
|
47
|
+
never goes stale.
|
|
48
|
+
- **Multi-paragraph docstrings on simple functions.**
|
|
49
|
+
- **`// used by X`.** Tooling answers that better and this rots the first time X moves.
|
|
50
|
+
|
|
51
|
+
## Write
|
|
52
|
+
|
|
53
|
+
- **The WHY that the code cannot show.** A constant that looks arbitrary, an ordering that
|
|
54
|
+
matters, a deliberate break from the surrounding pattern.
|
|
55
|
+
- **Workarounds**, with a link to the upstream bug.
|
|
56
|
+
- **Non-obvious invariants or preconditions** a caller has to respect.
|
|
57
|
+
- **Doc comments on public APIs** consumed from outside the module. Private ones need none.
|
|
58
|
+
- **`TODO(@owner, YYYY-MM-DD): description`.** Never a bare `TODO`.
|
|
59
|
+
|
|
60
|
+
## Shape
|
|
61
|
+
|
|
62
|
+
One line, unless the reason honestly needs two. A comment that needs a paragraph to explain
|
|
63
|
+
the code beneath it is a signal to fix the code instead.
|
|
@@ -14,7 +14,6 @@ paths:
|
|
|
14
14
|
- Voice budget is a hard limit: define max simultaneous voices per category and drop by priority, never let it grow unbounded
|
|
15
15
|
- Audio must degrade silently — a missing bank or failed event NEVER throws into the gameplay frame
|
|
16
16
|
- Keep the audio update off the render-critical path; audio work budget is 5-10% of frame time
|
|
17
|
-
- Reference the audio bible section each event implements in code comments
|
|
18
17
|
|
|
19
18
|
## Examples
|
|
20
19
|
|
|
@@ -11,7 +11,6 @@ paths:
|
|
|
11
11
|
- Every gameplay system must implement a clear interface
|
|
12
12
|
- State machines must have explicit transition tables with documented states
|
|
13
13
|
- Write unit tests for all gameplay logic — separate logic from presentation
|
|
14
|
-
- Document which design doc each feature implements in code comments
|
|
15
14
|
- No static singletons for game state — use dependency injection
|
|
16
15
|
|
|
17
16
|
## Examples
|
|
@@ -20,7 +20,6 @@ visual quality, performance, and cross-platform compatibility.
|
|
|
20
20
|
- Group related parameters (Unity: `[Header]`, Unreal: Category)
|
|
21
21
|
- Comment non-obvious calculations (especially math-heavy sections)
|
|
22
22
|
- No magic numbers — use named constants or documented uniform values
|
|
23
|
-
- Include authorship and purpose comment at the top of each shader file
|
|
24
23
|
|
|
25
24
|
## Performance Requirements
|
|
26
25
|
- Document the target platform and complexity budget for each shader
|