@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,94 +1,94 @@
1
- #!/usr/bin/env node
2
- // PreToolUse guard: add the one flag every `adr new` call needs, so the agent
3
- // can write a bare `adr new -- "Title"` and still get a safe command.
4
- //
5
- // -q / --quiet — without it, an `-s` pattern matching more than one file
6
- // opens an interactive prompt on stdin and hangs the session. With it,
7
- // the ambiguous case throws instead, so the wrong ADR is never superseded
8
- // by a silent guess.
9
- //
10
- // Nothing else is injected. The template no longer needs an env var: `adr`
11
- // resolves `<adr-dir>/templates/template.md` on its own, and init-adr-dir.cjs
12
- // puts musketeer's template there. There is no editor to suppress either —
13
- // `adr` only opens one on an explicit `--open`.
14
- //
15
- // Uses PreToolUse's `updatedInput` (not just allow/deny) to rewrite the
16
- // command before it runs. A `-q` the agent already wrote is left alone, and
17
- // the call is a silent no-op.
18
- //
19
- // Safety: a title that happens to contain the literal text "adr new" could
20
- // make the matching regex fire inside a quoted string. Guarded by counting
21
- // quote characters before each match — an odd count means "inside an open
22
- // string", and that occurrence is left untouched rather than risk corrupting
23
- // the title.
24
-
25
- const fs = require('fs');
26
- const {
27
- invocationRe,
28
- isInsideQuotes,
29
- isInsideHeredoc,
30
- invokesAdr,
31
- } = require('./lib/adr/command-scan.cjs');
32
-
33
- const QUIET = /(?:^|\s)(?:-q|--quiet)(?=\s|$)/;
34
-
35
- // Everything between `adr new` and either the `--` option terminator or the end
36
- // of this command segment, whichever comes first.
37
- //
38
- // The title lives after `--` and may legitimately contain the text "-q"
39
- // ("Use -q for quiet builds"). Scanning it would make the hook believe the flag
40
- // was already supplied and skip an invocation that genuinely needs it, so the
41
- // title is never part of the region searched.
42
- function flagsRegion(command, from) {
43
- const rest = command.slice(from);
44
- let end = rest.length;
45
-
46
- const terminator = rest.search(/\s--(\s|$)/);
47
- if (terminator !== -1) end = terminator;
48
-
49
- for (let i = 0; i < end; i++) {
50
- if (';&|\n'.includes(rest[i]) && !isInsideQuotes(command, from + i)) {
51
- end = i;
52
- break;
53
- }
54
- }
55
- return rest.slice(0, end);
56
- }
57
-
58
- function main() {
59
- const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
60
- const command = payload?.tool_input?.command || '';
61
- if (!invokesAdr(command, 'new')) return;
62
-
63
- let changed = false;
64
- const updated = command.replace(invocationRe('new'), (whole, boundary, ws, envPrefix, adrNew, offset) => {
65
- // Both guards, for the same reason the deny hooks use them: a heredoc body
66
- // is documentation being written, and injecting a flag into it would
67
- // rewrite the file's contents rather than the command.
68
- // `offset` is the boundary character; step past it to the word `adr`.
69
- const at = offset + boundary.length + ws.length + envPrefix.length;
70
- if (isInsideQuotes(command, at) || isInsideHeredoc(command, at)) return whole;
71
- if (QUIET.test(flagsRegion(command, at + adrNew.length))) return whole;
72
- changed = true;
73
- return `${boundary}${ws}${envPrefix}${adrNew} -q`;
74
- });
75
- if (!changed) return;
76
-
77
- process.stdout.write(
78
- JSON.stringify({
79
- systemMessage: 'Added -q to `adr new` — without it an ambiguous -s prompts on stdin and hangs.',
80
- hookSpecificOutput: {
81
- hookEventName: 'PreToolUse',
82
- permissionDecision: 'allow',
83
- updatedInput: { command: updated },
84
- },
85
- })
86
- );
87
- }
88
-
89
- try {
90
- main();
91
- } catch {
92
- /* fail open: leave the command untouched */
93
- }
94
- process.exit(0);
1
+ #!/usr/bin/env node
2
+ // PreToolUse guard: add the one flag every `adr new` call needs, so the agent
3
+ // can write a bare `adr new -- "Title"` and still get a safe command.
4
+ //
5
+ // -q / --quiet — without it, an `-s` pattern matching more than one file
6
+ // opens an interactive prompt on stdin and hangs the session. With it,
7
+ // the ambiguous case throws instead, so the wrong ADR is never superseded
8
+ // by a silent guess.
9
+ //
10
+ // Nothing else is injected. The template no longer needs an env var: `adr`
11
+ // resolves `<adr-dir>/templates/template.md` on its own, and init-adr-dir.cjs
12
+ // puts musketeer's template there. There is no editor to suppress either —
13
+ // `adr` only opens one on an explicit `--open`.
14
+ //
15
+ // Uses PreToolUse's `updatedInput` (not just allow/deny) to rewrite the
16
+ // command before it runs. A `-q` the agent already wrote is left alone, and
17
+ // the call is a silent no-op.
18
+ //
19
+ // Safety: a title that happens to contain the literal text "adr new" could
20
+ // make the matching regex fire inside a quoted string. Guarded by counting
21
+ // quote characters before each match — an odd count means "inside an open
22
+ // string", and that occurrence is left untouched rather than risk corrupting
23
+ // the title.
24
+
25
+ const fs = require('fs');
26
+ const {
27
+ invocationRe,
28
+ isInsideQuotes,
29
+ isInsideHeredoc,
30
+ invokesAdr,
31
+ } = require('./lib/adr/command-scan.cjs');
32
+
33
+ const QUIET = /(?:^|\s)(?:-q|--quiet)(?=\s|$)/;
34
+
35
+ // Everything between `adr new` and either the `--` option terminator or the end
36
+ // of this command segment, whichever comes first.
37
+ //
38
+ // The title lives after `--` and may legitimately contain the text "-q"
39
+ // ("Use -q for quiet builds"). Scanning it would make the hook believe the flag
40
+ // was already supplied and skip an invocation that genuinely needs it, so the
41
+ // title is never part of the region searched.
42
+ function flagsRegion(command, from) {
43
+ const rest = command.slice(from);
44
+ let end = rest.length;
45
+
46
+ const terminator = rest.search(/\s--(\s|$)/);
47
+ if (terminator !== -1) end = terminator;
48
+
49
+ for (let i = 0; i < end; i++) {
50
+ if (';&|\n'.includes(rest[i]) && !isInsideQuotes(command, from + i)) {
51
+ end = i;
52
+ break;
53
+ }
54
+ }
55
+ return rest.slice(0, end);
56
+ }
57
+
58
+ function main() {
59
+ const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
60
+ const command = payload?.tool_input?.command || '';
61
+ if (!invokesAdr(command, 'new')) return;
62
+
63
+ let changed = false;
64
+ const updated = command.replace(invocationRe('new'), (whole, boundary, ws, envPrefix, adrNew, offset) => {
65
+ // Both guards, for the same reason the deny hooks use them: a heredoc body
66
+ // is documentation being written, and injecting a flag into it would
67
+ // rewrite the file's contents rather than the command.
68
+ // `offset` is the boundary character; step past it to the word `adr`.
69
+ const at = offset + boundary.length + ws.length + envPrefix.length;
70
+ if (isInsideQuotes(command, at) || isInsideHeredoc(command, at)) return whole;
71
+ if (QUIET.test(flagsRegion(command, at + adrNew.length))) return whole;
72
+ changed = true;
73
+ return `${boundary}${ws}${envPrefix}${adrNew} -q`;
74
+ });
75
+ if (!changed) return;
76
+
77
+ process.stdout.write(
78
+ JSON.stringify({
79
+ systemMessage: 'Added -q to `adr new` — without it an ambiguous -s prompts on stdin and hangs.',
80
+ hookSpecificOutput: {
81
+ hookEventName: 'PreToolUse',
82
+ permissionDecision: 'allow',
83
+ updatedInput: { command: updated },
84
+ },
85
+ })
86
+ );
87
+ }
88
+
89
+ try {
90
+ main();
91
+ } catch {
92
+ /* fail open: leave the command untouched */
93
+ }
94
+ process.exit(0);
@@ -1,115 +1,115 @@
1
- 'use strict';
2
-
3
- // Tells a real `adr` invocation apart from a command that merely mentions one.
4
- //
5
- // Three PreToolUse hooks key off `adr new` and `adr init`. Matching those words
6
- // anywhere in the command string is too loose: a commit message explaining ADR
7
- // work, a grep for the phrase, or a heredoc of documentation all contain them
8
- // and none of them run the tool. Denying those commands leaves the user staring
9
- // at an error about a command they never wrote.
10
- //
11
- // Three tests must all pass before an occurrence counts:
12
- //
13
- // 1. It sits at a command position — the start of the string, or just after a
14
- // separator such as `;` `&&` `||` `|` `(` or a newline — allowing for any
15
- // KEY=value assignments in front of it.
16
- // 2. It is not inside an open quote.
17
- // 3. It is not inside a heredoc body, which is data being written rather than
18
- // a command being run.
19
- //
20
- // Where the two disagree with each other, the answer is "not an invocation".
21
- // A missed invocation makes a hook skip work the user will notice at once; a
22
- // false one blocks an unrelated command for a reason nobody can guess.
23
-
24
- // boundary/start, whitespace, any existing KEY=value prefix, then `adr <sub>`.
25
- //
26
- // The subcommand must not run straight into a word character or a hyphen. `\b`
27
- // alone is not enough: it sits happily between the `w` of `new` and the `-` of
28
- // `adr new-thing`, so that string would count as an `adr new` invocation. A hook
29
- // that only reads the command could shrug that off, but inject-adr-flags writes
30
- // the flag directly after the subcommand and would turn it into `adr new -q-thing`.
31
- // A trailing `;` or `|` still has to count, so this is a negated class rather
32
- // than a required space.
33
- function invocationRe(sub) {
34
- return new RegExp(
35
- `(^|[;&|\\n(){}])([ \\t]*)((?:[A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|"[^"]*"|\\S*)[ \\t]+)*)(adr[ \\t]+${sub})(?![\\w-])`,
36
- 'g'
37
- );
38
- }
39
-
40
- // An odd number of quote characters before this point means the point is inside
41
- // a string that is still open.
42
- function isInsideQuotes(command, index) {
43
- const before = command.slice(0, index);
44
- const dq = (before.match(/"/g) || []).length;
45
- const sq = (before.match(/'/g) || []).length;
46
- return dq % 2 === 1 || sq % 2 === 1;
47
- }
48
-
49
- const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
50
-
51
- // `<<TAG`, `<<-TAG`, `<<'TAG'`, `<<"TAG"`. `<<<` is a herestring, not a heredoc,
52
- // and does not match here because `<` is neither a quote nor a word character.
53
- const HEREDOC_OPEN = /<<(-?)[ \t]*(?:'([^']+)'|"([^"]+)"|([A-Za-z_][A-Za-z0-9_]*))/g;
54
-
55
- // Offset ranges covering heredoc bodies.
56
- //
57
- // A heredoc body is data being written, not commands being run. Writing install
58
- // docs that show `adr new -- "Use X"` is an ordinary thing to do in this repo,
59
- // and the line sits at the start of its own line with balanced quotes in front
60
- // of it, so both of the other tests would wave it through.
61
- //
62
- // Known limit: several heredocs opened on one line share the first body start,
63
- // so the ranges merge. That errs toward treating more text as data, which only
64
- // ever makes this scanner quieter.
65
- function heredocRanges(command) {
66
- const ranges = [];
67
- const re = new RegExp(HEREDOC_OPEN.source, 'g');
68
- let m;
69
-
70
- while ((m = re.exec(command))) {
71
- const tag = m[2] || m[3] || m[4];
72
- const newline = command.indexOf('\n', m.index);
73
- if (newline === -1) continue; // an opener with no body on this command
74
-
75
- const start = newline + 1;
76
- // `<<-` lets the terminator be indented with tabs.
77
- const terminator = new RegExp(`^${m[1] === '-' ? '[ \\t]*' : ''}${escapeRe(tag)}[ \\t]*$`, 'm');
78
- const hit = command.slice(start).match(terminator);
79
- ranges.push([start, hit ? start + hit.index : command.length]);
80
- }
81
-
82
- return ranges;
83
- }
84
-
85
- function isInsideHeredoc(command, index) {
86
- return heredocRanges(command).some(([start, end]) => index >= start && index < end);
87
- }
88
-
89
- // Where the word `adr` itself starts. The match begins at the boundary
90
- // character before it, which is close enough for counting quotes but one
91
- // position short of the heredoc body it may sit in.
92
- function adrOffsetOf(match) {
93
- return match.index + match[1].length + match[2].length + match[3].length;
94
- }
95
-
96
- // True when the command really runs `adr <sub>` at least once.
97
- function invokesAdr(command, sub) {
98
- const re = invocationRe(sub);
99
- const bodies = heredocRanges(command);
100
- const inBody = (i) => bodies.some(([start, end]) => i >= start && i < end);
101
- let m;
102
- while ((m = re.exec(command))) {
103
- const at = adrOffsetOf(m);
104
- if (!isInsideQuotes(command, at) && !inBody(at)) return true;
105
- }
106
- return false;
107
- }
108
-
109
- module.exports = {
110
- invokesAdr,
111
- isInsideQuotes,
112
- isInsideHeredoc,
113
- heredocRanges,
114
- invocationRe,
115
- };
1
+ 'use strict';
2
+
3
+ // Tells a real `adr` invocation apart from a command that merely mentions one.
4
+ //
5
+ // Three PreToolUse hooks key off `adr new` and `adr init`. Matching those words
6
+ // anywhere in the command string is too loose: a commit message explaining ADR
7
+ // work, a grep for the phrase, or a heredoc of documentation all contain them
8
+ // and none of them run the tool. Denying those commands leaves the user staring
9
+ // at an error about a command they never wrote.
10
+ //
11
+ // Three tests must all pass before an occurrence counts:
12
+ //
13
+ // 1. It sits at a command position — the start of the string, or just after a
14
+ // separator such as `;` `&&` `||` `|` `(` or a newline — allowing for any
15
+ // KEY=value assignments in front of it.
16
+ // 2. It is not inside an open quote.
17
+ // 3. It is not inside a heredoc body, which is data being written rather than
18
+ // a command being run.
19
+ //
20
+ // Where the two disagree with each other, the answer is "not an invocation".
21
+ // A missed invocation makes a hook skip work the user will notice at once; a
22
+ // false one blocks an unrelated command for a reason nobody can guess.
23
+
24
+ // boundary/start, whitespace, any existing KEY=value prefix, then `adr <sub>`.
25
+ //
26
+ // The subcommand must not run straight into a word character or a hyphen. `\b`
27
+ // alone is not enough: it sits happily between the `w` of `new` and the `-` of
28
+ // `adr new-thing`, so that string would count as an `adr new` invocation. A hook
29
+ // that only reads the command could shrug that off, but inject-adr-flags writes
30
+ // the flag directly after the subcommand and would turn it into `adr new -q-thing`.
31
+ // A trailing `;` or `|` still has to count, so this is a negated class rather
32
+ // than a required space.
33
+ function invocationRe(sub) {
34
+ return new RegExp(
35
+ `(^|[;&|\\n(){}])([ \\t]*)((?:[A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|"[^"]*"|\\S*)[ \\t]+)*)(adr[ \\t]+${sub})(?![\\w-])`,
36
+ 'g'
37
+ );
38
+ }
39
+
40
+ // An odd number of quote characters before this point means the point is inside
41
+ // a string that is still open.
42
+ function isInsideQuotes(command, index) {
43
+ const before = command.slice(0, index);
44
+ const dq = (before.match(/"/g) || []).length;
45
+ const sq = (before.match(/'/g) || []).length;
46
+ return dq % 2 === 1 || sq % 2 === 1;
47
+ }
48
+
49
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
50
+
51
+ // `<<TAG`, `<<-TAG`, `<<'TAG'`, `<<"TAG"`. `<<<` is a herestring, not a heredoc,
52
+ // and does not match here because `<` is neither a quote nor a word character.
53
+ const HEREDOC_OPEN = /<<(-?)[ \t]*(?:'([^']+)'|"([^"]+)"|([A-Za-z_][A-Za-z0-9_]*))/g;
54
+
55
+ // Offset ranges covering heredoc bodies.
56
+ //
57
+ // A heredoc body is data being written, not commands being run. Writing install
58
+ // docs that show `adr new -- "Use X"` is an ordinary thing to do in this repo,
59
+ // and the line sits at the start of its own line with balanced quotes in front
60
+ // of it, so both of the other tests would wave it through.
61
+ //
62
+ // Known limit: several heredocs opened on one line share the first body start,
63
+ // so the ranges merge. That errs toward treating more text as data, which only
64
+ // ever makes this scanner quieter.
65
+ function heredocRanges(command) {
66
+ const ranges = [];
67
+ const re = new RegExp(HEREDOC_OPEN.source, 'g');
68
+ let m;
69
+
70
+ while ((m = re.exec(command))) {
71
+ const tag = m[2] || m[3] || m[4];
72
+ const newline = command.indexOf('\n', m.index);
73
+ if (newline === -1) continue; // an opener with no body on this command
74
+
75
+ const start = newline + 1;
76
+ // `<<-` lets the terminator be indented with tabs.
77
+ const terminator = new RegExp(`^${m[1] === '-' ? '[ \\t]*' : ''}${escapeRe(tag)}[ \\t]*$`, 'm');
78
+ const hit = command.slice(start).match(terminator);
79
+ ranges.push([start, hit ? start + hit.index : command.length]);
80
+ }
81
+
82
+ return ranges;
83
+ }
84
+
85
+ function isInsideHeredoc(command, index) {
86
+ return heredocRanges(command).some(([start, end]) => index >= start && index < end);
87
+ }
88
+
89
+ // Where the word `adr` itself starts. The match begins at the boundary
90
+ // character before it, which is close enough for counting quotes but one
91
+ // position short of the heredoc body it may sit in.
92
+ function adrOffsetOf(match) {
93
+ return match.index + match[1].length + match[2].length + match[3].length;
94
+ }
95
+
96
+ // True when the command really runs `adr <sub>` at least once.
97
+ function invokesAdr(command, sub) {
98
+ const re = invocationRe(sub);
99
+ const bodies = heredocRanges(command);
100
+ const inBody = (i) => bodies.some(([start, end]) => i >= start && i < end);
101
+ let m;
102
+ while ((m = re.exec(command))) {
103
+ const at = adrOffsetOf(m);
104
+ if (!isInsideQuotes(command, at) && !inBody(at)) return true;
105
+ }
106
+ return false;
107
+ }
108
+
109
+ module.exports = {
110
+ invokesAdr,
111
+ isInsideQuotes,
112
+ isInsideHeredoc,
113
+ heredocRanges,
114
+ invocationRe,
115
+ };