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 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 30 rules for Claude Code — selective deploy by profile.**
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/ # 30 rules (Markdown)
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(chalk7.green(`Already up to date (v${currentVersion}).`));
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: 0,
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(chalk7.green("\nNo changes to apply."));
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);
@@ -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 **30 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.
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
 
@@ -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
- | xargs -n1 dirname 2>/dev/null | xargs -n1 basename 2>/dev/null | sort -u | wc -l | tr -d ' ')
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
- | xargs -n1 basename 2>/dev/null | sort -u | wc -l | tr -d ' ')
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
- [ "$AGENTS" -gt 0 ] 2>/dev/null && PARTS="${PARTS} | 🤖 ${AGENTS} agents"
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.8.0",
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": {
@@ -32,9 +32,13 @@ rules:
32
32
  - prototype-code
33
33
  - test-standards
34
34
  - implementation-workflow
35
+ - code-comments
35
36
  gamedev:
36
37
  []
37
38
 
39
+ output_styles:
40
+ - arcane
41
+
38
42
  agents:
39
43
  - quality
40
44
 
@@ -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