@phuc1403/musketeer 0.8.0 → 0.10.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.
Files changed (236) hide show
  1. package/INSTALLATION.md +52 -52
  2. package/README.md +49 -49
  3. package/bin/musketeer.js +168 -168
  4. package/manifest.json +333 -301
  5. package/package.json +48 -48
  6. package/src/dotnet-scaffold-copier.js +79 -79
  7. package/src/provisioner/detect.js +93 -93
  8. package/src/self-update.js +77 -77
  9. package/template/.claude/agents/code-reviewer.md +182 -166
  10. package/template/.claude/agents/git-manager.md +18 -18
  11. package/template/.claude/agents/hallmark-auditor.md +78 -78
  12. package/template/.claude/agents/researcher.md +33 -33
  13. package/template/.claude/hooks/block-unsafe-adr-title.cjs +85 -85
  14. package/template/.claude/hooks/git-skill-reminder.cjs +53 -0
  15. package/template/.claude/hooks/init-adr-dir.cjs +173 -173
  16. package/template/.claude/hooks/inject-adr-flags.cjs +94 -94
  17. package/template/.claude/hooks/lib/adr/command-scan.cjs +115 -115
  18. package/template/.claude/hooks/lib/characteristics/checker.cjs +357 -357
  19. package/template/.claude/hooks/lib/colors.cjs +180 -122
  20. package/template/.claude/hooks/lib/git-info-cache.cjs +191 -191
  21. package/template/.claude/hooks/lib/transcript-parser.cjs +300 -277
  22. package/template/.claude/hooks/sync-adr-toc.cjs +146 -146
  23. package/template/.claude/hooks/{usage-context-awareness.cjs → usage-quota-cache-refresh.cjs} +166 -166
  24. package/template/.claude/hooks/validate-characteristics-hook.cjs +66 -66
  25. package/template/.claude/hooks/validate-cml-hook.js +145 -145
  26. package/template/.claude/skills/adr-writer/SKILL.md +48 -48
  27. package/template/.claude/skills/adr-writer/references/adr-example.md +35 -35
  28. package/template/.claude/skills/architecture-characteristic-writer/SKILL.md +215 -215
  29. package/template/.claude/skills/architecture-characteristic-writer/assets/worksheet-template.md +29 -29
  30. package/template/.claude/skills/architecture-characteristic-writer/references/characteristics-catalog.md +40 -40
  31. package/template/.claude/skills/architecture-characteristic-writer/scripts/ranking-table.cjs +171 -171
  32. package/template/.claude/skills/code-review/SKILL.md +201 -54
  33. package/template/.claude/skills/code-review/references/checklist-workflow.md +96 -0
  34. package/template/.claude/skills/code-review/references/checklists/api.md +52 -52
  35. package/template/.claude/skills/code-review/references/checklists/base.md +100 -100
  36. package/template/.claude/skills/code-review/references/checklists/web-app.md +54 -54
  37. package/template/.claude/skills/code-review/references/code-review-reception.md +113 -0
  38. package/template/.claude/skills/code-review/references/codebase-scan-workflow.md +30 -0
  39. package/template/.claude/skills/code-review/references/edge-case-scouting.md +119 -0
  40. package/template/.claude/skills/code-review/references/input-mode-resolution.md +135 -0
  41. package/template/.claude/skills/code-review/references/parallel-review-workflow.md +76 -0
  42. package/template/.claude/skills/code-review/references/requesting-code-review.md +116 -0
  43. package/template/.claude/skills/code-review/references/spec-compliance-review.md +43 -0
  44. package/template/.claude/skills/code-review/references/task-management-reviews.md +140 -0
  45. package/template/.claude/skills/code-review/references/verification-before-completion.md +139 -0
  46. package/template/.claude/skills/context-map/SKILL.md +80 -80
  47. package/template/.claude/skills/context-map/example.cml +106 -106
  48. package/template/.claude/skills/context-map/reference/Bounded Context/Bounded Context.md +40 -40
  49. package/template/.claude/skills/context-map/reference/Bounded Context/businessModel.md +5 -5
  50. package/template/.claude/skills/context-map/reference/Bounded Context/domainVisionStatement.md +2 -2
  51. package/template/.claude/skills/context-map/reference/Bounded Context/evolution.md +5 -5
  52. package/template/.claude/skills/context-map/reference/Bounded Context/implementationTechnology.md +1 -1
  53. package/template/.claude/skills/context-map/reference/Bounded Context/implements.md +1 -1
  54. package/template/.claude/skills/context-map/reference/Bounded Context/knowledgeLevel.md +4 -4
  55. package/template/.claude/skills/context-map/reference/Bounded Context/realizes.md +9 -9
  56. package/template/.claude/skills/context-map/reference/Bounded Context/refines.md +10 -10
  57. package/template/.claude/skills/context-map/reference/Bounded Context/responsibilities.md +26 -26
  58. package/template/.claude/skills/context-map/reference/Bounded Context/type.md +23 -23
  59. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Anticorruption Layer.md +5 -5
  60. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Bounded Context Relationship.md +12 -12
  61. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Conformist.md +5 -5
  62. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Customer-Supplier (C-S).md +22 -22
  63. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Open Host Service.md +4 -4
  64. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Partnership (P).md +13 -13
  65. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Published Language.md +4 -4
  66. package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Shared Kernel (SK).md +12 -12
  67. package/template/.claude/skills/context-map/reference/Context Map.md +62 -62
  68. package/template/.claude/skills/context-map/reference/Domain/Domain.md +30 -30
  69. package/template/.claude/skills/context-map/reference/Domain/supports.md +33 -33
  70. package/template/.claude/skills/context-map/reference/Domain/type.md +3 -3
  71. package/template/.claude/skills/context-map/reference/Semantic Rules.md +32 -32
  72. package/template/.claude/skills/git/SKILL.md +131 -115
  73. package/template/.claude/skills/git/references/branch-management.md +88 -88
  74. package/template/.claude/skills/git/references/commit-standards.md +46 -46
  75. package/template/.claude/skills/git/references/context-efficiency.md +54 -0
  76. package/template/.claude/skills/git/references/gh-cli-guide.md +109 -109
  77. package/template/.claude/skills/git/references/safety-protocols.md +69 -69
  78. package/template/.claude/skills/git/references/workflow-commit.md +58 -58
  79. package/template/.claude/skills/git/references/workflow-merge-pr.md +136 -0
  80. package/template/.claude/skills/git/references/workflow-merge.md +48 -48
  81. package/template/.claude/skills/git/references/workflow-pr.md +58 -58
  82. package/template/.claude/skills/git/references/workflow-push.md +52 -52
  83. package/template/.claude/skills/hallmark/SKILL.md +552 -552
  84. package/template/.claude/skills/hallmark/references/anti-patterns.md +412 -412
  85. package/template/.claude/skills/hallmark/references/assets.md +406 -406
  86. package/template/.claude/skills/hallmark/references/color.md +95 -95
  87. package/template/.claude/skills/hallmark/references/component-cookbook.md +256 -256
  88. package/template/.claude/skills/hallmark/references/components/c1-outlined-chip.md +12 -12
  89. package/template/.claude/skills/hallmark/references/components/c2-inline-form-as-cta.md +16 -16
  90. package/template/.claude/skills/hallmark/references/components/c3-typographic-link.md +8 -8
  91. package/template/.claude/skills/hallmark/references/components/c4-sticky-bottom-bar.md +16 -16
  92. package/template/.claude/skills/hallmark/references/components/f1-bento-grid.md +20 -20
  93. package/template/.claude/skills/hallmark/references/components/f2-sticky-scroll-stack.md +20 -20
  94. package/template/.claude/skills/hallmark/references/components/f3-tabular-spec-sheet.md +11 -11
  95. package/template/.claude/skills/hallmark/references/components/f4-step-sequence.md +11 -11
  96. package/template/.claude/skills/hallmark/references/components/f5-annotated-screenshot.md +11 -11
  97. package/template/.claude/skills/hallmark/references/components/f6-product-card-grid.md +41 -41
  98. package/template/.claude/skills/hallmark/references/components/ft1-mast-headed.md +13 -13
  99. package/template/.claude/skills/hallmark/references/components/ft2-inline-rule-single-line.md +10 -10
  100. package/template/.claude/skills/hallmark/references/components/ft3-index-style-category-list.md +12 -12
  101. package/template/.claude/skills/hallmark/references/components/ft4-dense-typographic.md +10 -10
  102. package/template/.claude/skills/hallmark/references/components/ft5-statement.md +21 -21
  103. package/template/.claude/skills/hallmark/references/components/ft6-letter-close.md +19 -19
  104. package/template/.claude/skills/hallmark/references/components/ft7-newsletter-first.md +27 -27
  105. package/template/.claude/skills/hallmark/references/components/ft8-marquee-scroll.md +25 -25
  106. package/template/.claude/skills/hallmark/references/components/h1-marquee.md +15 -15
  107. package/template/.claude/skills/hallmark/references/components/h2-split-diptych.md +15 -15
  108. package/template/.claude/skills/hallmark/references/components/h3-quote-led.md +11 -11
  109. package/template/.claude/skills/hallmark/references/components/h4-stat-led.md +14 -14
  110. package/template/.claude/skills/hallmark/references/components/h5-letter-hero.md +11 -11
  111. package/template/.claude/skills/hallmark/references/components/h6-photographic-fold.md +16 -16
  112. package/template/.claude/skills/hallmark/references/components/h7-demo-video-clipped-by-viewport-edge.md +27 -27
  113. package/template/.claude/skills/hallmark/references/components/h8-mockup-split-browser-framed.md +23 -23
  114. package/template/.claude/skills/hallmark/references/components/h9-custom-illustration-centerpiece.md +27 -27
  115. package/template/.claude/skills/hallmark/references/components/n1-wordmark-2-links.md +12 -12
  116. package/template/.claude/skills/hallmark/references/components/n10-floating-on-scroll-morph.md +19 -19
  117. package/template/.claude/skills/hallmark/references/components/n2-floating-chip.md +14 -14
  118. package/template/.claude/skills/hallmark/references/components/n3-side-rail.md +14 -14
  119. package/template/.claude/skills/hallmark/references/components/n4-hidden-behind-k.md +9 -9
  120. package/template/.claude/skills/hallmark/references/components/n5-floating-pill.md +28 -28
  121. package/template/.claude/skills/hallmark/references/components/n6-newspaper-masthead.md +24 -24
  122. package/template/.claude/skills/hallmark/references/components/n7-brutal-slab.md +22 -22
  123. package/template/.claude/skills/hallmark/references/components/n8-terminal-command.md +21 -21
  124. package/template/.claude/skills/hallmark/references/components/n9-edge-aligned-minimal.md +17 -17
  125. package/template/.claude/skills/hallmark/references/components/s1-left-margin-numbered.md +15 -15
  126. package/template/.claude/skills/hallmark/references/components/s2-hanging.md +13 -13
  127. package/template/.claude/skills/hallmark/references/components/s3-sticky-pinned.md +19 -19
  128. package/template/.claude/skills/hallmark/references/components/s4-inline-no-break.md +11 -11
  129. package/template/.claude/skills/hallmark/references/components/s5-bottom-anchored.md +13 -13
  130. package/template/.claude/skills/hallmark/references/components/t1-pull-quote-with-marginalia.md +12 -12
  131. package/template/.claude/skills/hallmark/references/components/t2-logo-wall-hairline.md +19 -19
  132. package/template/.claude/skills/hallmark/references/components/t3-single-huge-quote.md +11 -11
  133. package/template/.claude/skills/hallmark/references/components/t4-numbered-stat-strip.md +14 -14
  134. package/template/.claude/skills/hallmark/references/contract.md +24 -24
  135. package/template/.claude/skills/hallmark/references/copy.md +182 -182
  136. package/template/.claude/skills/hallmark/references/custom-craft.md +626 -626
  137. package/template/.claude/skills/hallmark/references/custom-theme.md +329 -329
  138. package/template/.claude/skills/hallmark/references/design-md.md +116 -116
  139. package/template/.claude/skills/hallmark/references/export-formats.md +328 -328
  140. package/template/.claude/skills/hallmark/references/floating-nav.md +89 -89
  141. package/template/.claude/skills/hallmark/references/genres/atmospheric.md +65 -65
  142. package/template/.claude/skills/hallmark/references/genres/editorial.md +70 -70
  143. package/template/.claude/skills/hallmark/references/genres/modern-minimal.md +67 -67
  144. package/template/.claude/skills/hallmark/references/genres/playful.md +65 -65
  145. package/template/.claude/skills/hallmark/references/hero-enrichment.md +474 -474
  146. package/template/.claude/skills/hallmark/references/imagery-kit.md +170 -170
  147. package/template/.claude/skills/hallmark/references/interaction-and-states.md +207 -207
  148. package/template/.claude/skills/hallmark/references/layout-and-space.md +111 -111
  149. package/template/.claude/skills/hallmark/references/macrostructures/01-bento-grid.md +35 -35
  150. package/template/.claude/skills/hallmark/references/macrostructures/02-long-document.md +34 -34
  151. package/template/.claude/skills/hallmark/references/macrostructures/03-marquee-hero.md +31 -31
  152. package/template/.claude/skills/hallmark/references/macrostructures/04-stat-led.md +32 -32
  153. package/template/.claude/skills/hallmark/references/macrostructures/05-workbench.md +32 -32
  154. package/template/.claude/skills/hallmark/references/macrostructures/06-conversational-faq.md +33 -33
  155. package/template/.claude/skills/hallmark/references/macrostructures/07-manifesto.md +32 -32
  156. package/template/.claude/skills/hallmark/references/macrostructures/08-photographic.md +34 -34
  157. package/template/.claude/skills/hallmark/references/macrostructures/09-quote-led.md +32 -32
  158. package/template/.claude/skills/hallmark/references/macrostructures/10-specimen.md +32 -32
  159. package/template/.claude/skills/hallmark/references/macrostructures/11-catalogue.md +23 -23
  160. package/template/.claude/skills/hallmark/references/macrostructures/12-letter.md +23 -23
  161. package/template/.claude/skills/hallmark/references/macrostructures/13-index-first.md +23 -23
  162. package/template/.claude/skills/hallmark/references/macrostructures/14-narrative-workflow.md +23 -23
  163. package/template/.claude/skills/hallmark/references/macrostructures/15-split-studio.md +23 -23
  164. package/template/.claude/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -23
  165. package/template/.claude/skills/hallmark/references/macrostructures/17-type-specimen.md +23 -23
  166. package/template/.claude/skills/hallmark/references/macrostructures/18-portfolio-grid.md +23 -23
  167. package/template/.claude/skills/hallmark/references/macrostructures/19-map-diagram.md +23 -23
  168. package/template/.claude/skills/hallmark/references/macrostructures/20-ecosystem-index.md +23 -23
  169. package/template/.claude/skills/hallmark/references/macrostructures/21-component-playground.md +23 -23
  170. package/template/.claude/skills/hallmark/references/macrostructures.md +89 -89
  171. package/template/.claude/skills/hallmark/references/microinteractions.md +260 -260
  172. package/template/.claude/skills/hallmark/references/motion.md +109 -109
  173. package/template/.claude/skills/hallmark/references/preview-examples.md +49 -49
  174. package/template/.claude/skills/hallmark/references/responsive.md +138 -138
  175. package/template/.claude/skills/hallmark/references/slop-test.md +205 -205
  176. package/template/.claude/skills/hallmark/references/structure.md +164 -164
  177. package/template/.claude/skills/hallmark/references/study.md +511 -511
  178. package/template/.claude/skills/hallmark/references/typography.md +243 -243
  179. package/template/.claude/skills/hallmark/references/verbs/audit.md +25 -25
  180. package/template/.claude/skills/hallmark/references/verbs/redesign.md +269 -269
  181. package/template/.claude/skills/hallmark-loop/SKILL.md +105 -105
  182. package/template/.claude/skills/hallmark-loop/references/auditor-call.md +60 -60
  183. package/template/.claude/skills/hallmark-loop/references/capture.md +78 -78
  184. package/template/.claude/skills/hallmark-loop/references/loop-control.md +79 -79
  185. package/template/.claude/skills/handoff/SKILL.md +15 -15
  186. package/template/.claude/skills/knowledge-crunching/SKILL.md +94 -94
  187. package/template/.claude/skills/research/SKILL.md +69 -69
  188. package/template/.claude/skills/skill-creator/LICENSE.txt +201 -201
  189. package/template/.claude/skills/skill-creator/SKILL.md +154 -149
  190. package/template/.claude/skills/skill-creator/agents/analyzer.md +274 -274
  191. package/template/.claude/skills/skill-creator/agents/comparator.md +202 -202
  192. package/template/.claude/skills/skill-creator/agents/grader.md +223 -223
  193. package/template/.claude/skills/skill-creator/assets/eval_review.html +146 -146
  194. package/template/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -471
  195. package/template/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -1325
  196. package/template/.claude/skills/skill-creator/references/benchmark-optimization-guide.md +86 -86
  197. package/template/.claude/skills/skill-creator/references/distribution-guide.md +79 -79
  198. package/template/.claude/skills/skill-creator/references/eval-infrastructure-guide.md +129 -129
  199. package/template/.claude/skills/skill-creator/references/eval-schemas.md +121 -121
  200. package/template/.claude/skills/skill-creator/references/mcp-skills-integration.md +71 -71
  201. package/template/.claude/skills/skill-creator/references/metadata-quality-criteria.md +94 -94
  202. package/template/.claude/skills/skill-creator/references/plugin-marketplace-hosting.md +104 -104
  203. package/template/.claude/skills/skill-creator/references/plugin-marketplace-overview.md +89 -89
  204. package/template/.claude/skills/skill-creator/references/plugin-marketplace-schema.md +93 -93
  205. package/template/.claude/skills/skill-creator/references/plugin-marketplace-sources.md +103 -103
  206. package/template/.claude/skills/skill-creator/references/plugin-marketplace-troubleshooting.md +76 -76
  207. package/template/.claude/skills/skill-creator/references/script-quality-criteria.md +106 -106
  208. package/template/.claude/skills/skill-creator/references/skill-anatomy-and-requirements.md +77 -77
  209. package/template/.claude/skills/skill-creator/references/skill-creation-workflow.md +152 -151
  210. package/template/.claude/skills/skill-creator/references/skill-design-patterns.md +75 -75
  211. package/template/.claude/skills/skill-creator/references/skillmark-benchmark-criteria.md +102 -102
  212. package/template/.claude/skills/skill-creator/references/structure-organization-criteria.md +114 -114
  213. package/template/.claude/skills/skill-creator/references/testing-and-iteration.md +78 -78
  214. package/template/.claude/skills/skill-creator/references/token-efficiency-criteria.md +74 -74
  215. package/template/.claude/skills/skill-creator/references/troubleshooting-guide.md +81 -81
  216. package/template/.claude/skills/skill-creator/references/validation-checklist.md +83 -83
  217. package/template/.claude/skills/skill-creator/references/writing-effective-instructions.md +88 -88
  218. package/template/.claude/skills/skill-creator/references/yaml-frontmatter-reference.md +92 -92
  219. package/template/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -401
  220. package/template/.claude/skills/skill-creator/scripts/encoding_utils.py +36 -36
  221. package/template/.claude/skills/skill-creator/scripts/generate_report.py +326 -326
  222. package/template/.claude/skills/skill-creator/scripts/improve_description.py +248 -248
  223. package/template/.claude/skills/skill-creator/scripts/init_skill.py +360 -360
  224. package/template/.claude/skills/skill-creator/scripts/package_skill.py +143 -143
  225. package/template/.claude/skills/skill-creator/scripts/quick_validate.py +110 -110
  226. package/template/.claude/skills/skill-creator/scripts/run_eval.py +310 -310
  227. package/template/.claude/skills/skill-creator/scripts/run_loop.py +332 -332
  228. package/template/.claude/skills/skill-creator/scripts/utils.py +47 -47
  229. package/template/.claude/skills/tdd/SKILL.md +142 -142
  230. package/template/.claude/skills/tdd/deep-modules.md +15 -15
  231. package/template/.claude/skills/tdd/interface-design.md +31 -31
  232. package/template/.claude/skills/tdd/mocking.md +59 -59
  233. package/template/.claude/skills/tdd/refactoring.md +10 -10
  234. package/template/.claude/skills/tdd/tests.md +61 -61
  235. package/template/.claude/statusline.cjs +0 -0
  236. package/template/.claude/skills/code-review/references/adversarial-review.md +0 -223
@@ -1,146 +1,146 @@
1
- #!/usr/bin/env node
2
- // PostToolUse hook: after any `adr` command, rebuild docs/adr/README.md from the
3
- // ADR files themselves — one row per ADR with its title and current status.
4
- //
5
- // The index is fully derived, so it can never drift: every run re-reads the
6
- // source. That is why there is no need to be clever about which adr subcommand
7
- // ran, or to protect the file from being rewritten.
8
- //
9
- // Also deletes the tool's own `decisions.md`. Every `adr new` regenerates that
10
- // file unconditionally (not just on `adr generate toc`), which would leave two
11
- // competing indexes side by side in the same directory. README.md is the richer
12
- // one — it carries a status column — so it is the one kept.
13
- //
14
- // Also runnable directly: node sync-adr-toc.cjs --write
15
- //
16
- // Fails open at exit 0 on any problem — a hook must never block a session.
17
-
18
- const fs = require('fs');
19
- const path = require('path');
20
-
21
- const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
22
- const ADR_FILE = /^\d+-.*\.md$/;
23
-
24
- // The tool reads its directory from a committed `.adr-dir` (default `doc/adr`).
25
- function adrDir() {
26
- try {
27
- return fs.readFileSync(path.join(root, '.adr-dir'), 'utf8').trim() || 'doc/adr';
28
- } catch {
29
- return 'doc/adr';
30
- }
31
- }
32
-
33
- // Was this Bash call an adr command? (`adr new -q …` counts; a hyphenated name
34
- // such as `adr-new` does not, hence the required whitespace.)
35
- function isAdrCommand() {
36
- try {
37
- const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
38
- return /\badr\s/.test(payload?.tool_input?.command || '');
39
- } catch {
40
- return false;
41
- }
42
- }
43
-
44
- // First line, minus the leading "# " and the tool's own number prefix — the
45
- // template renders "# 12: Use X over Y" for 0012-….md. Dropping the unpadded 12
46
- // keeps the padded
47
- // stem in the ADR column the only number in the file, so nothing here can be
48
- // mistaken for the stem `adr new -s` expects.
49
- function title(body, fallback) {
50
- const first = body.split(/\r?\n/)[0] || '';
51
- return first.replace(/^#\s*(\d+\s*[:.]\s*)?/, '').trim() || fallback;
52
- }
53
-
54
- // First non-empty line of the `## Status` section. The tool maintains this:
55
- // "Accepted", or "Superseded by [4: …](0004-….md)" after `adr new -s`.
56
- function status(body) {
57
- const lines = body.split(/\r?\n/);
58
- const start = lines.findIndex((l) => /^##\s+Status\s*$/i.test(l));
59
- if (start === -1) return 'Unknown';
60
- for (const line of lines.slice(start + 1)) {
61
- if (/^##\s/.test(line)) break;
62
- if (line.trim()) return line.trim();
63
- }
64
- return 'Unknown';
65
- }
66
-
67
- function render(dir, files) {
68
- const rows = files.map((name) => {
69
- const body = fs.readFileSync(path.join(root, dir, name), 'utf8');
70
- const stem = name.replace(/\.md$/, ''); // padded — what `adr new -s` needs
71
- const cell = (s) => s.replace(/\|/g, '\\|'); // never break the table
72
- return `| [${stem}](./${name}) | ${cell(title(body, stem))} | ${cell(status(body))} |`;
73
- });
74
-
75
- return [
76
- '# Architecture Decision Records',
77
- '',
78
- 'Generated from the ADR files by the `sync-adr-toc` hook — do not hand-edit.',
79
- '',
80
- '| ADR | Title | Status |',
81
- '|-----|-------|--------|',
82
- ...rows,
83
- '',
84
- ].join('\n');
85
- }
86
-
87
- // The tool writes its own table of contents to `<adr-dir>/decisions.md` on every
88
- // `adr new`. README.md supersedes it, so drop it rather than commit two indexes
89
- // that have to agree with each other.
90
- //
91
- // Only ever deletes a file that is recognisably that generated index: the exact
92
- // heading it writes, followed by nothing but link lines. `decisions.md` is an
93
- // ordinary name for a hand-written decision log, and deleting one of those on
94
- // the next `adr new` would destroy work no backup covers.
95
- const GENERATED_TOC = /^# Table of Contents\r?\n\r?\n(?:- \[[^\]]*\]\([^)]*\)\r?\n?)*$/;
96
-
97
- function removeRedundantToc(dir) {
98
- const stale = path.join(root, dir, 'decisions.md');
99
- try {
100
- if (!fs.existsSync(stale)) return;
101
- if (!GENERATED_TOC.test(fs.readFileSync(stale, 'utf8'))) {
102
- process.stdout.write(
103
- `Left ${dir}/decisions.md alone — it is not the generated index. ` +
104
- 'README.md is the one this hook maintains.\n'
105
- );
106
- return;
107
- }
108
- fs.unlinkSync(stale);
109
- process.stdout.write(`Removed ${dir}/decisions.md (README.md is the index).\n`);
110
- } catch {
111
- /* not worth failing the hook over */
112
- }
113
- }
114
-
115
- function main() {
116
- if (!process.argv.includes('--write') && !isAdrCommand()) return;
117
-
118
- const dir = adrDir();
119
- removeRedundantToc(dir);
120
- const files = fs
121
- .readdirSync(path.join(root, dir))
122
- .filter((f) => ADR_FILE.test(f))
123
- .sort();
124
- if (files.length === 0) return;
125
-
126
- const target = path.join(root, dir, 'README.md');
127
- const next = render(dir, files);
128
-
129
- let current = null;
130
- try {
131
- current = fs.readFileSync(target, 'utf8');
132
- } catch {
133
- /* no index yet */
134
- }
135
- if (current === next) return; // already in sync
136
-
137
- fs.writeFileSync(target, next);
138
- process.stdout.write(`Regenerated ${dir}/README.md (${files.length} ADRs).\n`);
139
- }
140
-
141
- try {
142
- main();
143
- } catch {
144
- /* fail open */
145
- }
146
- process.exit(0);
1
+ #!/usr/bin/env node
2
+ // PostToolUse hook: after any `adr` command, rebuild docs/adr/README.md from the
3
+ // ADR files themselves — one row per ADR with its title and current status.
4
+ //
5
+ // The index is fully derived, so it can never drift: every run re-reads the
6
+ // source. That is why there is no need to be clever about which adr subcommand
7
+ // ran, or to protect the file from being rewritten.
8
+ //
9
+ // Also deletes the tool's own `decisions.md`. Every `adr new` regenerates that
10
+ // file unconditionally (not just on `adr generate toc`), which would leave two
11
+ // competing indexes side by side in the same directory. README.md is the richer
12
+ // one — it carries a status column — so it is the one kept.
13
+ //
14
+ // Also runnable directly: node sync-adr-toc.cjs --write
15
+ //
16
+ // Fails open at exit 0 on any problem — a hook must never block a session.
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
22
+ const ADR_FILE = /^\d+-.*\.md$/;
23
+
24
+ // The tool reads its directory from a committed `.adr-dir` (default `doc/adr`).
25
+ function adrDir() {
26
+ try {
27
+ return fs.readFileSync(path.join(root, '.adr-dir'), 'utf8').trim() || 'doc/adr';
28
+ } catch {
29
+ return 'doc/adr';
30
+ }
31
+ }
32
+
33
+ // Was this Bash call an adr command? (`adr new -q …` counts; a hyphenated name
34
+ // such as `adr-new` does not, hence the required whitespace.)
35
+ function isAdrCommand() {
36
+ try {
37
+ const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
38
+ return /\badr\s/.test(payload?.tool_input?.command || '');
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ // First line, minus the leading "# " and the tool's own number prefix — the
45
+ // template renders "# 12: Use X over Y" for 0012-….md. Dropping the unpadded 12
46
+ // keeps the padded
47
+ // stem in the ADR column the only number in the file, so nothing here can be
48
+ // mistaken for the stem `adr new -s` expects.
49
+ function title(body, fallback) {
50
+ const first = body.split(/\r?\n/)[0] || '';
51
+ return first.replace(/^#\s*(\d+\s*[:.]\s*)?/, '').trim() || fallback;
52
+ }
53
+
54
+ // First non-empty line of the `## Status` section. The tool maintains this:
55
+ // "Accepted", or "Superseded by [4: …](0004-….md)" after `adr new -s`.
56
+ function status(body) {
57
+ const lines = body.split(/\r?\n/);
58
+ const start = lines.findIndex((l) => /^##\s+Status\s*$/i.test(l));
59
+ if (start === -1) return 'Unknown';
60
+ for (const line of lines.slice(start + 1)) {
61
+ if (/^##\s/.test(line)) break;
62
+ if (line.trim()) return line.trim();
63
+ }
64
+ return 'Unknown';
65
+ }
66
+
67
+ function render(dir, files) {
68
+ const rows = files.map((name) => {
69
+ const body = fs.readFileSync(path.join(root, dir, name), 'utf8');
70
+ const stem = name.replace(/\.md$/, ''); // padded — what `adr new -s` needs
71
+ const cell = (s) => s.replace(/\|/g, '\\|'); // never break the table
72
+ return `| [${stem}](./${name}) | ${cell(title(body, stem))} | ${cell(status(body))} |`;
73
+ });
74
+
75
+ return [
76
+ '# Architecture Decision Records',
77
+ '',
78
+ 'Generated from the ADR files by the `sync-adr-toc` hook — do not hand-edit.',
79
+ '',
80
+ '| ADR | Title | Status |',
81
+ '|-----|-------|--------|',
82
+ ...rows,
83
+ '',
84
+ ].join('\n');
85
+ }
86
+
87
+ // The tool writes its own table of contents to `<adr-dir>/decisions.md` on every
88
+ // `adr new`. README.md supersedes it, so drop it rather than commit two indexes
89
+ // that have to agree with each other.
90
+ //
91
+ // Only ever deletes a file that is recognisably that generated index: the exact
92
+ // heading it writes, followed by nothing but link lines. `decisions.md` is an
93
+ // ordinary name for a hand-written decision log, and deleting one of those on
94
+ // the next `adr new` would destroy work no backup covers.
95
+ const GENERATED_TOC = /^# Table of Contents\r?\n\r?\n(?:- \[[^\]]*\]\([^)]*\)\r?\n?)*$/;
96
+
97
+ function removeRedundantToc(dir) {
98
+ const stale = path.join(root, dir, 'decisions.md');
99
+ try {
100
+ if (!fs.existsSync(stale)) return;
101
+ if (!GENERATED_TOC.test(fs.readFileSync(stale, 'utf8'))) {
102
+ process.stdout.write(
103
+ `Left ${dir}/decisions.md alone — it is not the generated index. ` +
104
+ 'README.md is the one this hook maintains.\n'
105
+ );
106
+ return;
107
+ }
108
+ fs.unlinkSync(stale);
109
+ process.stdout.write(`Removed ${dir}/decisions.md (README.md is the index).\n`);
110
+ } catch {
111
+ /* not worth failing the hook over */
112
+ }
113
+ }
114
+
115
+ function main() {
116
+ if (!process.argv.includes('--write') && !isAdrCommand()) return;
117
+
118
+ const dir = adrDir();
119
+ removeRedundantToc(dir);
120
+ const files = fs
121
+ .readdirSync(path.join(root, dir))
122
+ .filter((f) => ADR_FILE.test(f))
123
+ .sort();
124
+ if (files.length === 0) return;
125
+
126
+ const target = path.join(root, dir, 'README.md');
127
+ const next = render(dir, files);
128
+
129
+ let current = null;
130
+ try {
131
+ current = fs.readFileSync(target, 'utf8');
132
+ } catch {
133
+ /* no index yet */
134
+ }
135
+ if (current === next) return; // already in sync
136
+
137
+ fs.writeFileSync(target, next);
138
+ process.stdout.write(`Regenerated ${dir}/README.md (${files.length} ADRs).\n`);
139
+ }
140
+
141
+ try {
142
+ main();
143
+ } catch {
144
+ /* fail open */
145
+ }
146
+ process.exit(0);
@@ -1,166 +1,166 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Usage Cache Writer - UserPromptSubmit & PostToolUse Hook
4
- *
5
- * Fetches Claude Code usage limits from Anthropic OAuth API and writes to cache.
6
- * The cache is read by statusline.cjs to show the 5-hour rate-limit budget.
7
- *
8
- * Self-contained port (no .ck.json / hook-logger deps). Fails open: any error
9
- * just emits {continue:true} so it can never block a prompt or tool call.
10
- *
11
- * Features:
12
- * - Cross-platform credential retrieval (macOS Keychain, file-based)
13
- * - API response caching (60s TTL)
14
- * - Throttled API calls (1 min for prompts, 5 mins for tool use)
15
- */
16
-
17
- try {
18
- const fs = require("fs");
19
- const path = require("path");
20
- const os = require("os");
21
- const { execSync } = require("child_process");
22
-
23
- // Cache configuration
24
- const USAGE_CACHE_FILE = path.join(os.tmpdir(), "ck-usage-limits-cache.json");
25
- const FETCH_INTERVAL_MS = 300000; // 5 minutes for PostToolUse
26
- const FETCH_INTERVAL_PROMPT_MS = 60000; // 1 minute for UserPromptSubmit
27
-
28
- /**
29
- * Get Claude OAuth credentials (cross-platform)
30
- */
31
- function getClaudeCredentials() {
32
- // macOS: Try Keychain first
33
- if (os.platform() === "darwin") {
34
- try {
35
- const result = execSync('security find-generic-password -s "Claude Code-credentials" -w', {
36
- timeout: 5000,
37
- encoding: "utf-8",
38
- stdio: ["pipe", "pipe", "ignore"],
39
- }).trim();
40
- const parsed = JSON.parse(result);
41
- if (parsed.claudeAiOauth?.accessToken) {
42
- return parsed.claudeAiOauth.accessToken;
43
- }
44
- } catch {
45
- // Fallback to file-based
46
- }
47
- }
48
-
49
- // File-based credentials (Linux/Windows, or macOS fallback)
50
- const credPath = path.join(os.homedir(), ".claude", ".credentials.json");
51
- try {
52
- const content = fs.readFileSync(credPath, "utf-8");
53
- const parsed = JSON.parse(content);
54
- return parsed.claudeAiOauth?.accessToken || null;
55
- } catch {
56
- return null;
57
- }
58
- }
59
-
60
- /**
61
- * Check if we should fetch (throttled)
62
- */
63
- function shouldFetch(isUserPrompt = false) {
64
- const interval = isUserPrompt ? FETCH_INTERVAL_PROMPT_MS : FETCH_INTERVAL_MS;
65
-
66
- try {
67
- if (fs.existsSync(USAGE_CACHE_FILE)) {
68
- const cache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, "utf-8"));
69
- if (Date.now() - cache.timestamp < interval) {
70
- return false;
71
- }
72
- }
73
- } catch {}
74
- return true;
75
- }
76
-
77
- /**
78
- * Write cache atomically (temp+rename prevents partial reads by statusline)
79
- */
80
- function writeCache(status, data = null) {
81
- const tmpFile = `${USAGE_CACHE_FILE}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
82
- try {
83
- fs.writeFileSync(
84
- tmpFile,
85
- JSON.stringify({
86
- timestamp: Date.now(),
87
- status,
88
- data,
89
- })
90
- );
91
- fs.renameSync(tmpFile, USAGE_CACHE_FILE);
92
- } catch {
93
- try { fs.unlinkSync(tmpFile); } catch {}
94
- }
95
- }
96
-
97
- /**
98
- * Fetch usage limits from Anthropic OAuth API and write to cache
99
- * Always writes status to cache (available or unavailable) for statusline fallback
100
- */
101
- async function fetchAndCacheUsageLimits() {
102
- const token = getClaudeCredentials();
103
- if (!token) {
104
- writeCache("unavailable");
105
- return;
106
- }
107
-
108
- try {
109
- const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
110
- method: "GET",
111
- headers: {
112
- Accept: "application/json",
113
- "Content-Type": "application/json",
114
- Authorization: `Bearer ${token}`,
115
- "anthropic-beta": "oauth-2025-04-20",
116
- "User-Agent": "claudekit-engineer/1.0",
117
- },
118
- });
119
-
120
- if (!response.ok) {
121
- writeCache("unavailable");
122
- return;
123
- }
124
-
125
- const data = await response.json();
126
- writeCache("available", data);
127
- } catch {
128
- writeCache("unavailable");
129
- }
130
- }
131
-
132
- /**
133
- * Main hook execution - just fetch and cache, no injection
134
- */
135
- async function main() {
136
- const result = { continue: true };
137
-
138
- try {
139
- // Read hook input
140
- let inputStr = "";
141
- try {
142
- inputStr = fs.readFileSync(0, "utf-8");
143
- } catch {}
144
-
145
- const input = JSON.parse(inputStr || "{}");
146
-
147
- // Detect hook type (UserPromptSubmit vs PostToolUse) for throttle interval
148
- const isUserPrompt = typeof input.prompt === "string";
149
-
150
- // Check if we should fetch (throttled)
151
- if (shouldFetch(isUserPrompt)) {
152
- await fetchAndCacheUsageLimits();
153
- }
154
- } catch {}
155
-
156
- // Output result (no injection, just continue)
157
- console.log(JSON.stringify(result));
158
- }
159
-
160
- main().catch(() => {
161
- console.log(JSON.stringify({ continue: true }));
162
- process.exit(0);
163
- });
164
- } catch (e) {
165
- process.exit(0); // fail-open
166
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Usage Cache Writer - UserPromptSubmit & PostToolUse Hook
4
+ *
5
+ * Fetches Claude Code usage limits from Anthropic OAuth API and writes to cache.
6
+ * The cache is read by statusline.cjs to show the 5-hour rate-limit budget.
7
+ *
8
+ * Self-contained port (no .ck.json / hook-logger deps). Fails open: any error
9
+ * just emits {continue:true} so it can never block a prompt or tool call.
10
+ *
11
+ * Features:
12
+ * - Cross-platform credential retrieval (macOS Keychain, file-based)
13
+ * - API response caching (60s TTL)
14
+ * - Throttled API calls (1 min for prompts, 5 mins for tool use)
15
+ */
16
+
17
+ try {
18
+ const fs = require("fs");
19
+ const path = require("path");
20
+ const os = require("os");
21
+ const { execSync } = require("child_process");
22
+
23
+ // Cache configuration
24
+ const USAGE_CACHE_FILE = path.join(os.tmpdir(), "ck-usage-limits-cache.json");
25
+ const FETCH_INTERVAL_MS = 300000; // 5 minutes for PostToolUse
26
+ const FETCH_INTERVAL_PROMPT_MS = 60000; // 1 minute for UserPromptSubmit
27
+
28
+ /**
29
+ * Get Claude OAuth credentials (cross-platform)
30
+ */
31
+ function getClaudeCredentials() {
32
+ // macOS: Try Keychain first
33
+ if (os.platform() === "darwin") {
34
+ try {
35
+ const result = execSync('security find-generic-password -s "Claude Code-credentials" -w', {
36
+ timeout: 5000,
37
+ encoding: "utf-8",
38
+ stdio: ["pipe", "pipe", "ignore"],
39
+ }).trim();
40
+ const parsed = JSON.parse(result);
41
+ if (parsed.claudeAiOauth?.accessToken) {
42
+ return parsed.claudeAiOauth.accessToken;
43
+ }
44
+ } catch {
45
+ // Fallback to file-based
46
+ }
47
+ }
48
+
49
+ // File-based credentials (Linux/Windows, or macOS fallback)
50
+ const credPath = path.join(os.homedir(), ".claude", ".credentials.json");
51
+ try {
52
+ const content = fs.readFileSync(credPath, "utf-8");
53
+ const parsed = JSON.parse(content);
54
+ return parsed.claudeAiOauth?.accessToken || null;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Check if we should fetch (throttled)
62
+ */
63
+ function shouldFetch(isUserPrompt = false) {
64
+ const interval = isUserPrompt ? FETCH_INTERVAL_PROMPT_MS : FETCH_INTERVAL_MS;
65
+
66
+ try {
67
+ if (fs.existsSync(USAGE_CACHE_FILE)) {
68
+ const cache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, "utf-8"));
69
+ if (Date.now() - cache.timestamp < interval) {
70
+ return false;
71
+ }
72
+ }
73
+ } catch {}
74
+ return true;
75
+ }
76
+
77
+ /**
78
+ * Write cache atomically (temp+rename prevents partial reads by statusline)
79
+ */
80
+ function writeCache(status, data = null) {
81
+ const tmpFile = `${USAGE_CACHE_FILE}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
82
+ try {
83
+ fs.writeFileSync(
84
+ tmpFile,
85
+ JSON.stringify({
86
+ timestamp: Date.now(),
87
+ status,
88
+ data,
89
+ })
90
+ );
91
+ fs.renameSync(tmpFile, USAGE_CACHE_FILE);
92
+ } catch {
93
+ try { fs.unlinkSync(tmpFile); } catch {}
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Fetch usage limits from Anthropic OAuth API and write to cache
99
+ * Always writes status to cache (available or unavailable) for statusline fallback
100
+ */
101
+ async function fetchAndCacheUsageLimits() {
102
+ const token = getClaudeCredentials();
103
+ if (!token) {
104
+ writeCache("unavailable");
105
+ return;
106
+ }
107
+
108
+ try {
109
+ const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
110
+ method: "GET",
111
+ headers: {
112
+ Accept: "application/json",
113
+ "Content-Type": "application/json",
114
+ Authorization: `Bearer ${token}`,
115
+ "anthropic-beta": "oauth-2025-04-20",
116
+ "User-Agent": "claudekit-engineer/1.0",
117
+ },
118
+ });
119
+
120
+ if (!response.ok) {
121
+ writeCache("unavailable");
122
+ return;
123
+ }
124
+
125
+ const data = await response.json();
126
+ writeCache("available", data);
127
+ } catch {
128
+ writeCache("unavailable");
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Main hook execution - just fetch and cache, no injection
134
+ */
135
+ async function main() {
136
+ const result = { continue: true };
137
+
138
+ try {
139
+ // Read hook input
140
+ let inputStr = "";
141
+ try {
142
+ inputStr = fs.readFileSync(0, "utf-8");
143
+ } catch {}
144
+
145
+ const input = JSON.parse(inputStr || "{}");
146
+
147
+ // Detect hook type (UserPromptSubmit vs PostToolUse) for throttle interval
148
+ const isUserPrompt = typeof input.prompt === "string";
149
+
150
+ // Check if we should fetch (throttled)
151
+ if (shouldFetch(isUserPrompt)) {
152
+ await fetchAndCacheUsageLimits();
153
+ }
154
+ } catch {}
155
+
156
+ // Output result (no injection, just continue)
157
+ console.log(JSON.stringify(result));
158
+ }
159
+
160
+ main().catch(() => {
161
+ console.log(JSON.stringify({ continue: true }));
162
+ process.exit(0);
163
+ });
164
+ } catch (e) {
165
+ process.exit(0); // fail-open
166
+ }