@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.
- package/INSTALLATION.md +52 -52
- package/README.md +49 -49
- package/bin/musketeer.js +168 -168
- package/manifest.json +333 -301
- package/package.json +48 -48
- package/src/dotnet-scaffold-copier.js +79 -79
- package/src/provisioner/detect.js +93 -93
- package/src/self-update.js +77 -77
- package/template/.claude/agents/code-reviewer.md +182 -166
- package/template/.claude/agents/git-manager.md +18 -18
- package/template/.claude/agents/hallmark-auditor.md +78 -78
- package/template/.claude/agents/researcher.md +33 -33
- package/template/.claude/hooks/block-unsafe-adr-title.cjs +85 -85
- package/template/.claude/hooks/git-skill-reminder.cjs +53 -0
- package/template/.claude/hooks/init-adr-dir.cjs +173 -173
- package/template/.claude/hooks/inject-adr-flags.cjs +94 -94
- package/template/.claude/hooks/lib/adr/command-scan.cjs +115 -115
- package/template/.claude/hooks/lib/characteristics/checker.cjs +357 -357
- package/template/.claude/hooks/lib/colors.cjs +180 -122
- package/template/.claude/hooks/lib/git-info-cache.cjs +191 -191
- package/template/.claude/hooks/lib/transcript-parser.cjs +300 -277
- package/template/.claude/hooks/sync-adr-toc.cjs +146 -146
- package/template/.claude/hooks/{usage-context-awareness.cjs → usage-quota-cache-refresh.cjs} +166 -166
- package/template/.claude/hooks/validate-characteristics-hook.cjs +66 -66
- package/template/.claude/hooks/validate-cml-hook.js +145 -145
- package/template/.claude/skills/adr-writer/SKILL.md +48 -48
- package/template/.claude/skills/adr-writer/references/adr-example.md +35 -35
- package/template/.claude/skills/architecture-characteristic-writer/SKILL.md +215 -215
- package/template/.claude/skills/architecture-characteristic-writer/assets/worksheet-template.md +29 -29
- package/template/.claude/skills/architecture-characteristic-writer/references/characteristics-catalog.md +40 -40
- package/template/.claude/skills/architecture-characteristic-writer/scripts/ranking-table.cjs +171 -171
- package/template/.claude/skills/code-review/SKILL.md +201 -54
- package/template/.claude/skills/code-review/references/checklist-workflow.md +96 -0
- package/template/.claude/skills/code-review/references/checklists/api.md +52 -52
- package/template/.claude/skills/code-review/references/checklists/base.md +100 -100
- package/template/.claude/skills/code-review/references/checklists/web-app.md +54 -54
- package/template/.claude/skills/code-review/references/code-review-reception.md +113 -0
- package/template/.claude/skills/code-review/references/codebase-scan-workflow.md +30 -0
- package/template/.claude/skills/code-review/references/edge-case-scouting.md +119 -0
- package/template/.claude/skills/code-review/references/input-mode-resolution.md +135 -0
- package/template/.claude/skills/code-review/references/parallel-review-workflow.md +76 -0
- package/template/.claude/skills/code-review/references/requesting-code-review.md +116 -0
- package/template/.claude/skills/code-review/references/spec-compliance-review.md +43 -0
- package/template/.claude/skills/code-review/references/task-management-reviews.md +140 -0
- package/template/.claude/skills/code-review/references/verification-before-completion.md +139 -0
- package/template/.claude/skills/context-map/SKILL.md +80 -80
- package/template/.claude/skills/context-map/example.cml +106 -106
- package/template/.claude/skills/context-map/reference/Bounded Context/Bounded Context.md +40 -40
- package/template/.claude/skills/context-map/reference/Bounded Context/businessModel.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context/domainVisionStatement.md +2 -2
- package/template/.claude/skills/context-map/reference/Bounded Context/evolution.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context/implementationTechnology.md +1 -1
- package/template/.claude/skills/context-map/reference/Bounded Context/implements.md +1 -1
- package/template/.claude/skills/context-map/reference/Bounded Context/knowledgeLevel.md +4 -4
- package/template/.claude/skills/context-map/reference/Bounded Context/realizes.md +9 -9
- package/template/.claude/skills/context-map/reference/Bounded Context/refines.md +10 -10
- package/template/.claude/skills/context-map/reference/Bounded Context/responsibilities.md +26 -26
- package/template/.claude/skills/context-map/reference/Bounded Context/type.md +23 -23
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Anticorruption Layer.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Bounded Context Relationship.md +12 -12
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Conformist.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Customer-Supplier (C-S).md +22 -22
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Open Host Service.md +4 -4
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Partnership (P).md +13 -13
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Published Language.md +4 -4
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Shared Kernel (SK).md +12 -12
- package/template/.claude/skills/context-map/reference/Context Map.md +62 -62
- package/template/.claude/skills/context-map/reference/Domain/Domain.md +30 -30
- package/template/.claude/skills/context-map/reference/Domain/supports.md +33 -33
- package/template/.claude/skills/context-map/reference/Domain/type.md +3 -3
- package/template/.claude/skills/context-map/reference/Semantic Rules.md +32 -32
- package/template/.claude/skills/git/SKILL.md +131 -115
- package/template/.claude/skills/git/references/branch-management.md +88 -88
- package/template/.claude/skills/git/references/commit-standards.md +46 -46
- package/template/.claude/skills/git/references/context-efficiency.md +54 -0
- package/template/.claude/skills/git/references/gh-cli-guide.md +109 -109
- package/template/.claude/skills/git/references/safety-protocols.md +69 -69
- package/template/.claude/skills/git/references/workflow-commit.md +58 -58
- package/template/.claude/skills/git/references/workflow-merge-pr.md +136 -0
- package/template/.claude/skills/git/references/workflow-merge.md +48 -48
- package/template/.claude/skills/git/references/workflow-pr.md +58 -58
- package/template/.claude/skills/git/references/workflow-push.md +52 -52
- package/template/.claude/skills/hallmark/SKILL.md +552 -552
- package/template/.claude/skills/hallmark/references/anti-patterns.md +412 -412
- package/template/.claude/skills/hallmark/references/assets.md +406 -406
- package/template/.claude/skills/hallmark/references/color.md +95 -95
- package/template/.claude/skills/hallmark/references/component-cookbook.md +256 -256
- package/template/.claude/skills/hallmark/references/components/c1-outlined-chip.md +12 -12
- package/template/.claude/skills/hallmark/references/components/c2-inline-form-as-cta.md +16 -16
- package/template/.claude/skills/hallmark/references/components/c3-typographic-link.md +8 -8
- package/template/.claude/skills/hallmark/references/components/c4-sticky-bottom-bar.md +16 -16
- package/template/.claude/skills/hallmark/references/components/f1-bento-grid.md +20 -20
- package/template/.claude/skills/hallmark/references/components/f2-sticky-scroll-stack.md +20 -20
- package/template/.claude/skills/hallmark/references/components/f3-tabular-spec-sheet.md +11 -11
- package/template/.claude/skills/hallmark/references/components/f4-step-sequence.md +11 -11
- package/template/.claude/skills/hallmark/references/components/f5-annotated-screenshot.md +11 -11
- package/template/.claude/skills/hallmark/references/components/f6-product-card-grid.md +41 -41
- package/template/.claude/skills/hallmark/references/components/ft1-mast-headed.md +13 -13
- package/template/.claude/skills/hallmark/references/components/ft2-inline-rule-single-line.md +10 -10
- package/template/.claude/skills/hallmark/references/components/ft3-index-style-category-list.md +12 -12
- package/template/.claude/skills/hallmark/references/components/ft4-dense-typographic.md +10 -10
- package/template/.claude/skills/hallmark/references/components/ft5-statement.md +21 -21
- package/template/.claude/skills/hallmark/references/components/ft6-letter-close.md +19 -19
- package/template/.claude/skills/hallmark/references/components/ft7-newsletter-first.md +27 -27
- package/template/.claude/skills/hallmark/references/components/ft8-marquee-scroll.md +25 -25
- package/template/.claude/skills/hallmark/references/components/h1-marquee.md +15 -15
- package/template/.claude/skills/hallmark/references/components/h2-split-diptych.md +15 -15
- package/template/.claude/skills/hallmark/references/components/h3-quote-led.md +11 -11
- package/template/.claude/skills/hallmark/references/components/h4-stat-led.md +14 -14
- package/template/.claude/skills/hallmark/references/components/h5-letter-hero.md +11 -11
- package/template/.claude/skills/hallmark/references/components/h6-photographic-fold.md +16 -16
- package/template/.claude/skills/hallmark/references/components/h7-demo-video-clipped-by-viewport-edge.md +27 -27
- package/template/.claude/skills/hallmark/references/components/h8-mockup-split-browser-framed.md +23 -23
- package/template/.claude/skills/hallmark/references/components/h9-custom-illustration-centerpiece.md +27 -27
- package/template/.claude/skills/hallmark/references/components/n1-wordmark-2-links.md +12 -12
- package/template/.claude/skills/hallmark/references/components/n10-floating-on-scroll-morph.md +19 -19
- package/template/.claude/skills/hallmark/references/components/n2-floating-chip.md +14 -14
- package/template/.claude/skills/hallmark/references/components/n3-side-rail.md +14 -14
- package/template/.claude/skills/hallmark/references/components/n4-hidden-behind-k.md +9 -9
- package/template/.claude/skills/hallmark/references/components/n5-floating-pill.md +28 -28
- package/template/.claude/skills/hallmark/references/components/n6-newspaper-masthead.md +24 -24
- package/template/.claude/skills/hallmark/references/components/n7-brutal-slab.md +22 -22
- package/template/.claude/skills/hallmark/references/components/n8-terminal-command.md +21 -21
- package/template/.claude/skills/hallmark/references/components/n9-edge-aligned-minimal.md +17 -17
- package/template/.claude/skills/hallmark/references/components/s1-left-margin-numbered.md +15 -15
- package/template/.claude/skills/hallmark/references/components/s2-hanging.md +13 -13
- package/template/.claude/skills/hallmark/references/components/s3-sticky-pinned.md +19 -19
- package/template/.claude/skills/hallmark/references/components/s4-inline-no-break.md +11 -11
- package/template/.claude/skills/hallmark/references/components/s5-bottom-anchored.md +13 -13
- package/template/.claude/skills/hallmark/references/components/t1-pull-quote-with-marginalia.md +12 -12
- package/template/.claude/skills/hallmark/references/components/t2-logo-wall-hairline.md +19 -19
- package/template/.claude/skills/hallmark/references/components/t3-single-huge-quote.md +11 -11
- package/template/.claude/skills/hallmark/references/components/t4-numbered-stat-strip.md +14 -14
- package/template/.claude/skills/hallmark/references/contract.md +24 -24
- package/template/.claude/skills/hallmark/references/copy.md +182 -182
- package/template/.claude/skills/hallmark/references/custom-craft.md +626 -626
- package/template/.claude/skills/hallmark/references/custom-theme.md +329 -329
- package/template/.claude/skills/hallmark/references/design-md.md +116 -116
- package/template/.claude/skills/hallmark/references/export-formats.md +328 -328
- package/template/.claude/skills/hallmark/references/floating-nav.md +89 -89
- package/template/.claude/skills/hallmark/references/genres/atmospheric.md +65 -65
- package/template/.claude/skills/hallmark/references/genres/editorial.md +70 -70
- package/template/.claude/skills/hallmark/references/genres/modern-minimal.md +67 -67
- package/template/.claude/skills/hallmark/references/genres/playful.md +65 -65
- package/template/.claude/skills/hallmark/references/hero-enrichment.md +474 -474
- package/template/.claude/skills/hallmark/references/imagery-kit.md +170 -170
- package/template/.claude/skills/hallmark/references/interaction-and-states.md +207 -207
- package/template/.claude/skills/hallmark/references/layout-and-space.md +111 -111
- package/template/.claude/skills/hallmark/references/macrostructures/01-bento-grid.md +35 -35
- package/template/.claude/skills/hallmark/references/macrostructures/02-long-document.md +34 -34
- package/template/.claude/skills/hallmark/references/macrostructures/03-marquee-hero.md +31 -31
- package/template/.claude/skills/hallmark/references/macrostructures/04-stat-led.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/05-workbench.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/06-conversational-faq.md +33 -33
- package/template/.claude/skills/hallmark/references/macrostructures/07-manifesto.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/08-photographic.md +34 -34
- package/template/.claude/skills/hallmark/references/macrostructures/09-quote-led.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/10-specimen.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/11-catalogue.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/12-letter.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/13-index-first.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/14-narrative-workflow.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/15-split-studio.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/17-type-specimen.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/18-portfolio-grid.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/19-map-diagram.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/20-ecosystem-index.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/21-component-playground.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures.md +89 -89
- package/template/.claude/skills/hallmark/references/microinteractions.md +260 -260
- package/template/.claude/skills/hallmark/references/motion.md +109 -109
- package/template/.claude/skills/hallmark/references/preview-examples.md +49 -49
- package/template/.claude/skills/hallmark/references/responsive.md +138 -138
- package/template/.claude/skills/hallmark/references/slop-test.md +205 -205
- package/template/.claude/skills/hallmark/references/structure.md +164 -164
- package/template/.claude/skills/hallmark/references/study.md +511 -511
- package/template/.claude/skills/hallmark/references/typography.md +243 -243
- package/template/.claude/skills/hallmark/references/verbs/audit.md +25 -25
- package/template/.claude/skills/hallmark/references/verbs/redesign.md +269 -269
- package/template/.claude/skills/hallmark-loop/SKILL.md +105 -105
- package/template/.claude/skills/hallmark-loop/references/auditor-call.md +60 -60
- package/template/.claude/skills/hallmark-loop/references/capture.md +78 -78
- package/template/.claude/skills/hallmark-loop/references/loop-control.md +79 -79
- package/template/.claude/skills/handoff/SKILL.md +15 -15
- package/template/.claude/skills/knowledge-crunching/SKILL.md +94 -94
- package/template/.claude/skills/research/SKILL.md +69 -69
- package/template/.claude/skills/skill-creator/LICENSE.txt +201 -201
- package/template/.claude/skills/skill-creator/SKILL.md +154 -149
- package/template/.claude/skills/skill-creator/agents/analyzer.md +274 -274
- package/template/.claude/skills/skill-creator/agents/comparator.md +202 -202
- package/template/.claude/skills/skill-creator/agents/grader.md +223 -223
- package/template/.claude/skills/skill-creator/assets/eval_review.html +146 -146
- package/template/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -471
- package/template/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -1325
- package/template/.claude/skills/skill-creator/references/benchmark-optimization-guide.md +86 -86
- package/template/.claude/skills/skill-creator/references/distribution-guide.md +79 -79
- package/template/.claude/skills/skill-creator/references/eval-infrastructure-guide.md +129 -129
- package/template/.claude/skills/skill-creator/references/eval-schemas.md +121 -121
- package/template/.claude/skills/skill-creator/references/mcp-skills-integration.md +71 -71
- package/template/.claude/skills/skill-creator/references/metadata-quality-criteria.md +94 -94
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-hosting.md +104 -104
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-overview.md +89 -89
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-schema.md +93 -93
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-sources.md +103 -103
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-troubleshooting.md +76 -76
- package/template/.claude/skills/skill-creator/references/script-quality-criteria.md +106 -106
- package/template/.claude/skills/skill-creator/references/skill-anatomy-and-requirements.md +77 -77
- package/template/.claude/skills/skill-creator/references/skill-creation-workflow.md +152 -151
- package/template/.claude/skills/skill-creator/references/skill-design-patterns.md +75 -75
- package/template/.claude/skills/skill-creator/references/skillmark-benchmark-criteria.md +102 -102
- package/template/.claude/skills/skill-creator/references/structure-organization-criteria.md +114 -114
- package/template/.claude/skills/skill-creator/references/testing-and-iteration.md +78 -78
- package/template/.claude/skills/skill-creator/references/token-efficiency-criteria.md +74 -74
- package/template/.claude/skills/skill-creator/references/troubleshooting-guide.md +81 -81
- package/template/.claude/skills/skill-creator/references/validation-checklist.md +83 -83
- package/template/.claude/skills/skill-creator/references/writing-effective-instructions.md +88 -88
- package/template/.claude/skills/skill-creator/references/yaml-frontmatter-reference.md +92 -92
- package/template/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -401
- package/template/.claude/skills/skill-creator/scripts/encoding_utils.py +36 -36
- package/template/.claude/skills/skill-creator/scripts/generate_report.py +326 -326
- package/template/.claude/skills/skill-creator/scripts/improve_description.py +248 -248
- package/template/.claude/skills/skill-creator/scripts/init_skill.py +360 -360
- package/template/.claude/skills/skill-creator/scripts/package_skill.py +143 -143
- package/template/.claude/skills/skill-creator/scripts/quick_validate.py +110 -110
- package/template/.claude/skills/skill-creator/scripts/run_eval.py +310 -310
- package/template/.claude/skills/skill-creator/scripts/run_loop.py +332 -332
- package/template/.claude/skills/skill-creator/scripts/utils.py +47 -47
- package/template/.claude/skills/tdd/SKILL.md +142 -142
- package/template/.claude/skills/tdd/deep-modules.md +15 -15
- package/template/.claude/skills/tdd/interface-design.md +31 -31
- package/template/.claude/skills/tdd/mocking.md +59 -59
- package/template/.claude/skills/tdd/refactoring.md +10 -10
- package/template/.claude/skills/tdd/tests.md +61 -61
- package/template/.claude/statusline.cjs +0 -0
- package/template/.claude/skills/code-review/references/adversarial-review.md +0 -223
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// PreToolUse guard: block an `adr new` title the tool cannot render safely.
|
|
3
|
-
//
|
|
4
|
-
// The tool builds the ADR by running plain JS string replacements over the
|
|
5
|
-
// template, in a fixed order: DATE, TITLE, NUMBER, STATUS. `String.replace`
|
|
6
|
-
// with a string pattern rewrites the FIRST occurrence, and by the time STATUS
|
|
7
|
-
// is substituted the title is already sitting in the document — on line 1,
|
|
8
|
-
// ahead of the real `STATUS` placeholder in the `## Status` section.
|
|
9
|
-
//
|
|
10
|
-
// So an uppercase STATUS inside the title captures the substitution meant for
|
|
11
|
-
// the status line. Verified against @meza/adr-tools 2.0.4:
|
|
12
|
-
// adr new -q -- "Use STATUS codes for errors"
|
|
13
|
-
// writes `# 1: Use Accepted codes for errors` and leaves the real status as the
|
|
14
|
-
// literal word STATUS — exit 0, no warning, wrong data in the index.
|
|
15
|
-
//
|
|
16
|
-
// Only STATUS is blocked, and only in uppercase:
|
|
17
|
-
// - the replacement is case-sensitive, so "Use status codes" is fine;
|
|
18
|
-
// - DATE is substituted before the title is inserted, so it cannot be caught;
|
|
19
|
-
// - NUMBER and TITLE are substituted at points that precede the title text in
|
|
20
|
-
// the template shipped with this skill, so the real placeholder always wins.
|
|
21
|
-
// That last one holds because of where the tokens sit in
|
|
22
|
-
// `references/adr-template.md` — revisit this guard if that file is ever
|
|
23
|
-
// reordered so `## Status` precedes the heading.
|
|
24
|
-
//
|
|
25
|
-
// A title starting with `-` is the second case: the tool parses its own flags
|
|
26
|
-
// with commander, so without a `--` separator the title is read as an unknown
|
|
27
|
-
// option. That one fails cleanly (exit 1, no file created), but it is still
|
|
28
|
-
// worth catching early with a clearer reason than "unknown option".
|
|
29
|
-
//
|
|
30
|
-
// Blocking, not warning: the STATUS case corrupts data with no error at all, so
|
|
31
|
-
// there is nothing later in the pipeline that will catch it.
|
|
32
|
-
//
|
|
33
|
-
// The `|` and `&` characters used to be blocked here too. Those were hazards of
|
|
34
|
-
// the old bash implementation, which substituted the title into
|
|
35
|
-
// `sed -e "s|TITLE|$title|"`. Substitution is JS now, and both characters were
|
|
36
|
-
// re-verified as harmless, so blocking them would only refuse valid titles.
|
|
37
|
-
|
|
38
|
-
const { invokesAdr } = require("./lib/adr/command-scan.cjs");
|
|
39
|
-
|
|
40
|
-
let raw = "";
|
|
41
|
-
process.stdin.on("data", (chunk) => (raw += chunk));
|
|
42
|
-
process.stdin.on("end", () => {
|
|
43
|
-
let input;
|
|
44
|
-
try {
|
|
45
|
-
input = JSON.parse(raw || "{}");
|
|
46
|
-
} catch {
|
|
47
|
-
process.exit(0); // unparseable payload — fail open, don't block legit work
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const command = (input && input.tool_input && input.tool_input.command) || "";
|
|
51
|
-
if (!invokesAdr(command, "new")) process.exit(0);
|
|
52
|
-
|
|
53
|
-
// The skill always places `--` immediately before a quoted title. Extract
|
|
54
|
-
// that argument; anything else about the command's shape is not this hook's
|
|
55
|
-
// concern.
|
|
56
|
-
const afterDashDash = command.match(/--\s+(["'])((?:(?!\1).)*)\1/);
|
|
57
|
-
const title = afterDashDash ? afterDashDash[2] : null;
|
|
58
|
-
|
|
59
|
-
let reason = null;
|
|
60
|
-
if (!afterDashDash) {
|
|
61
|
-
reason =
|
|
62
|
-
"no `--` before the title (or no quoted title found after it). Without `--`, " +
|
|
63
|
-
"a title starting with `-` is parsed as an unknown option and no ADR is " +
|
|
64
|
-
'created. Always: adr new -q [-s STEM]... -- "Title".';
|
|
65
|
-
} else if (title.includes("STATUS")) {
|
|
66
|
-
reason =
|
|
67
|
-
"the title contains `STATUS` in uppercase. The tool substitutes STATUS into the " +
|
|
68
|
-
'template after the title is already in the document, so "Use STATUS codes" ' +
|
|
69
|
-
'becomes "Use Accepted codes" and the real Status section is left as the literal ' +
|
|
70
|
-
"word STATUS — it exits 0, so nothing else will catch this. Lowercase `status` is fine.";
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (reason) {
|
|
74
|
-
process.stdout.write(
|
|
75
|
-
JSON.stringify({
|
|
76
|
-
hookSpecificOutput: {
|
|
77
|
-
hookEventName: "PreToolUse",
|
|
78
|
-
permissionDecision: "deny",
|
|
79
|
-
permissionDecisionReason: `Refusing this \`adr new\` call: ${reason} Rename the title and retry.`,
|
|
80
|
-
},
|
|
81
|
-
})
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
process.exit(0);
|
|
85
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse guard: block an `adr new` title the tool cannot render safely.
|
|
3
|
+
//
|
|
4
|
+
// The tool builds the ADR by running plain JS string replacements over the
|
|
5
|
+
// template, in a fixed order: DATE, TITLE, NUMBER, STATUS. `String.replace`
|
|
6
|
+
// with a string pattern rewrites the FIRST occurrence, and by the time STATUS
|
|
7
|
+
// is substituted the title is already sitting in the document — on line 1,
|
|
8
|
+
// ahead of the real `STATUS` placeholder in the `## Status` section.
|
|
9
|
+
//
|
|
10
|
+
// So an uppercase STATUS inside the title captures the substitution meant for
|
|
11
|
+
// the status line. Verified against @meza/adr-tools 2.0.4:
|
|
12
|
+
// adr new -q -- "Use STATUS codes for errors"
|
|
13
|
+
// writes `# 1: Use Accepted codes for errors` and leaves the real status as the
|
|
14
|
+
// literal word STATUS — exit 0, no warning, wrong data in the index.
|
|
15
|
+
//
|
|
16
|
+
// Only STATUS is blocked, and only in uppercase:
|
|
17
|
+
// - the replacement is case-sensitive, so "Use status codes" is fine;
|
|
18
|
+
// - DATE is substituted before the title is inserted, so it cannot be caught;
|
|
19
|
+
// - NUMBER and TITLE are substituted at points that precede the title text in
|
|
20
|
+
// the template shipped with this skill, so the real placeholder always wins.
|
|
21
|
+
// That last one holds because of where the tokens sit in
|
|
22
|
+
// `references/adr-template.md` — revisit this guard if that file is ever
|
|
23
|
+
// reordered so `## Status` precedes the heading.
|
|
24
|
+
//
|
|
25
|
+
// A title starting with `-` is the second case: the tool parses its own flags
|
|
26
|
+
// with commander, so without a `--` separator the title is read as an unknown
|
|
27
|
+
// option. That one fails cleanly (exit 1, no file created), but it is still
|
|
28
|
+
// worth catching early with a clearer reason than "unknown option".
|
|
29
|
+
//
|
|
30
|
+
// Blocking, not warning: the STATUS case corrupts data with no error at all, so
|
|
31
|
+
// there is nothing later in the pipeline that will catch it.
|
|
32
|
+
//
|
|
33
|
+
// The `|` and `&` characters used to be blocked here too. Those were hazards of
|
|
34
|
+
// the old bash implementation, which substituted the title into
|
|
35
|
+
// `sed -e "s|TITLE|$title|"`. Substitution is JS now, and both characters were
|
|
36
|
+
// re-verified as harmless, so blocking them would only refuse valid titles.
|
|
37
|
+
|
|
38
|
+
const { invokesAdr } = require("./lib/adr/command-scan.cjs");
|
|
39
|
+
|
|
40
|
+
let raw = "";
|
|
41
|
+
process.stdin.on("data", (chunk) => (raw += chunk));
|
|
42
|
+
process.stdin.on("end", () => {
|
|
43
|
+
let input;
|
|
44
|
+
try {
|
|
45
|
+
input = JSON.parse(raw || "{}");
|
|
46
|
+
} catch {
|
|
47
|
+
process.exit(0); // unparseable payload — fail open, don't block legit work
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const command = (input && input.tool_input && input.tool_input.command) || "";
|
|
51
|
+
if (!invokesAdr(command, "new")) process.exit(0);
|
|
52
|
+
|
|
53
|
+
// The skill always places `--` immediately before a quoted title. Extract
|
|
54
|
+
// that argument; anything else about the command's shape is not this hook's
|
|
55
|
+
// concern.
|
|
56
|
+
const afterDashDash = command.match(/--\s+(["'])((?:(?!\1).)*)\1/);
|
|
57
|
+
const title = afterDashDash ? afterDashDash[2] : null;
|
|
58
|
+
|
|
59
|
+
let reason = null;
|
|
60
|
+
if (!afterDashDash) {
|
|
61
|
+
reason =
|
|
62
|
+
"no `--` before the title (or no quoted title found after it). Without `--`, " +
|
|
63
|
+
"a title starting with `-` is parsed as an unknown option and no ADR is " +
|
|
64
|
+
'created. Always: adr new -q [-s STEM]... -- "Title".';
|
|
65
|
+
} else if (title.includes("STATUS")) {
|
|
66
|
+
reason =
|
|
67
|
+
"the title contains `STATUS` in uppercase. The tool substitutes STATUS into the " +
|
|
68
|
+
'template after the title is already in the document, so "Use STATUS codes" ' +
|
|
69
|
+
'becomes "Use Accepted codes" and the real Status section is left as the literal ' +
|
|
70
|
+
"word STATUS — it exits 0, so nothing else will catch this. Lowercase `status` is fine.";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (reason) {
|
|
74
|
+
process.stdout.write(
|
|
75
|
+
JSON.stringify({
|
|
76
|
+
hookSpecificOutput: {
|
|
77
|
+
hookEventName: "PreToolUse",
|
|
78
|
+
permissionDecision: "deny",
|
|
79
|
+
permissionDecisionReason: `Refusing this \`adr new\` call: ${reason} Rename the title and retry.`,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
process.exit(0);
|
|
85
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse hook (core company): make the ck:git skill load on git work.
|
|
3
|
+
//
|
|
4
|
+
// Skill auto-activation is a model judgement, not enforcement — the agent
|
|
5
|
+
// routinely runs `git commit` / `git push` straight through Bash without ever
|
|
6
|
+
// opening the skill, so its conventional-commit format, split rules and secret
|
|
7
|
+
// scan are silently skipped. A description alone cannot fix that; only a hook
|
|
8
|
+
// runs every time.
|
|
9
|
+
//
|
|
10
|
+
// Fires on state-changing git/gh operations only. Read-only commands (status,
|
|
11
|
+
// log, diff, show) are skipped so the reminder does not burn context on every
|
|
12
|
+
// incidental `git status`.
|
|
13
|
+
//
|
|
14
|
+
// Matches mid-command too (`cd repo && git push`), since the operation is
|
|
15
|
+
// often not the first word.
|
|
16
|
+
//
|
|
17
|
+
// Always exits 0: this advises, it never blocks.
|
|
18
|
+
|
|
19
|
+
const fs = require("fs");
|
|
20
|
+
|
|
21
|
+
const SKILL = ".claude/skills/git/SKILL.md";
|
|
22
|
+
|
|
23
|
+
// Mutating git verbs, plus the gh surfaces the skill covers.
|
|
24
|
+
const GIT_OPS =
|
|
25
|
+
/(^|[\s;&|(])(git\s+(commit|push|merge|rebase|tag|revert|reset|cherry-pick|switch|checkout|branch|remote|stash)|gh\s+(pr|release|repo))\b/;
|
|
26
|
+
|
|
27
|
+
function isGitOperation(command) {
|
|
28
|
+
if (!command || typeof command !== "string") return false;
|
|
29
|
+
return GIT_OPS.test(command);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const payload = JSON.parse(fs.readFileSync(0, "utf-8"));
|
|
34
|
+
const command = payload?.tool_input?.command || "";
|
|
35
|
+
|
|
36
|
+
if (!isGitOperation(command)) process.exit(0);
|
|
37
|
+
|
|
38
|
+
process.stdout.write(
|
|
39
|
+
JSON.stringify({
|
|
40
|
+
hookSpecificOutput: {
|
|
41
|
+
hookEventName: "PreToolUse",
|
|
42
|
+
additionalContext:
|
|
43
|
+
`This is a git operation. Read ${SKILL} (the ck:git skill) and follow it — its ` +
|
|
44
|
+
"conventional-commit format, commit-splitting rules, secret scan and branch " +
|
|
45
|
+
"protections are project policy, not suggestions. Do that before running the " +
|
|
46
|
+
"command; if you have already read it this session, carry on.",
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
} catch {
|
|
52
|
+
process.exit(0); // fail open — never block a command over a reminder
|
|
53
|
+
}
|
|
@@ -1,173 +1,173 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// PreToolUse guard: set up `.adr-dir` before the first `adr new` in a project,
|
|
3
|
-
// so the agent never has to pick between `adr init` and a hand-written file.
|
|
4
|
-
//
|
|
5
|
-
// Two setup paths, and picking the wrong one is destructive:
|
|
6
|
-
// - fresh (no `.adr-dir`, no numbered ADRs yet): `adr init docs/adr` is safe —
|
|
7
|
-
// it creates docs/adr/, writes `.adr-dir`, and adds a baseline ADR.
|
|
8
|
-
// - migration (numbered ADRs already exist, but no `.adr-dir`): `adr init`
|
|
9
|
-
// would ALSO add that baseline ADR, burning the next real number. The fix
|
|
10
|
-
// is a plain `.adr-dir` file with no tool call.
|
|
11
|
-
// Already initialized: nothing to do, both paths are a no-op.
|
|
12
|
-
//
|
|
13
|
-
// Also makes sure this skill's ADR template is the one `adr new` picks up, by
|
|
14
|
-
// placing it at `<adr-dir>/templates/template.md` — the third step of the tool's
|
|
15
|
-
// own template resolution, and the reason no env var has to be injected.
|
|
16
|
-
//
|
|
17
|
-
// Runs the setup itself (as a side effect) before allowing `adr new` through,
|
|
18
|
-
// and denies a *direct* `adr init` call whenever running it would be wrong —
|
|
19
|
-
// already initialized, or migration state — since either would burn a number.
|
|
20
|
-
// Anything it does is echoed to stdout so it stays visible in the transcript.
|
|
21
|
-
|
|
22
|
-
const fs = require('fs');
|
|
23
|
-
const path = require('path');
|
|
24
|
-
const { spawnSync } = require('child_process');
|
|
25
|
-
const { invokesAdr } = require('./lib/adr/command-scan.cjs');
|
|
26
|
-
|
|
27
|
-
const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
28
|
-
const ADR_DIR = 'docs/adr';
|
|
29
|
-
const ADR_DIR_FILE = path.join(root, '.adr-dir');
|
|
30
|
-
const ADR_FILE = /^\d+-.*\.md$/;
|
|
31
|
-
const SKILL_TEMPLATE = path.join(
|
|
32
|
-
root,
|
|
33
|
-
'.claude',
|
|
34
|
-
'skills',
|
|
35
|
-
'adr-writer',
|
|
36
|
-
'references',
|
|
37
|
-
'adr-template.md'
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
function allow(message) {
|
|
41
|
-
if (message) process.stdout.write(message + '\n');
|
|
42
|
-
process.exit(0);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function deny(reason) {
|
|
46
|
-
process.stdout.write(
|
|
47
|
-
JSON.stringify({
|
|
48
|
-
hookSpecificOutput: {
|
|
49
|
-
hookEventName: 'PreToolUse',
|
|
50
|
-
permissionDecision: 'deny',
|
|
51
|
-
permissionDecisionReason: reason,
|
|
52
|
-
},
|
|
53
|
-
})
|
|
54
|
-
);
|
|
55
|
-
process.exit(0);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function readAdrDir() {
|
|
59
|
-
try {
|
|
60
|
-
return fs.readFileSync(ADR_DIR_FILE, 'utf8').trim() || null;
|
|
61
|
-
} catch {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function isInitialized() {
|
|
67
|
-
return readAdrDir() !== null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function hasNumberedAdrs() {
|
|
71
|
-
try {
|
|
72
|
-
return fs.readdirSync(path.join(root, ADR_DIR)).some((f) => ADR_FILE.test(f));
|
|
73
|
-
} catch {
|
|
74
|
-
return false; // directory doesn't exist yet -> nothing to migrate
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function runAdrInit() {
|
|
79
|
-
// `adr` is an npm bin — a real executable on every platform, so it is called
|
|
80
|
-
// directly rather than through a shell script interpreter. `shell: true` is
|
|
81
|
-
// here only because npm installs it as `adr.cmd` on Windows, which
|
|
82
|
-
// CreateProcess cannot launch on its own; every argument is a constant, so
|
|
83
|
-
// there is nothing for a shell to interpolate.
|
|
84
|
-
return spawnSync('adr', ['init', ADR_DIR], { cwd: root, shell: true, encoding: 'utf8' });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// `adr init` writes `.adr-dir` with `path.relative`, so on Windows it lands as
|
|
88
|
-
// `docs\adr` with no trailing newline. That file is committed and read on every
|
|
89
|
-
// other machine, where a backslash is an ordinary filename character and not a
|
|
90
|
-
// separator. Rewrite it in the portable form the rest of this toolchain emits.
|
|
91
|
-
function normalizeAdrDirFile() {
|
|
92
|
-
const declared = readAdrDir();
|
|
93
|
-
if (!declared) return;
|
|
94
|
-
fs.writeFileSync(ADR_DIR_FILE, `${declared.split(path.sep).join('/')}\n`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// The tool resolves its template as: explicit argument, then $ADR_TEMPLATE, then
|
|
98
|
-
// `<adr-dir>/templates/template.md`, then its own bundled default. Copying this
|
|
99
|
-
// skill's template into the third slot means a bare `adr new` produces a
|
|
100
|
-
// musketeer ADR with no environment set up for it.
|
|
101
|
-
//
|
|
102
|
-
// Only ever writes when the file is absent, so a project that has customised its
|
|
103
|
-
// own template keeps it.
|
|
104
|
-
function ensureTemplate() {
|
|
105
|
-
try {
|
|
106
|
-
const dir = readAdrDir() || ADR_DIR;
|
|
107
|
-
const target = path.join(root, dir, 'templates', 'template.md');
|
|
108
|
-
if (fs.existsSync(target) || !fs.existsSync(SKILL_TEMPLATE)) return null;
|
|
109
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
110
|
-
fs.copyFileSync(SKILL_TEMPLATE, target);
|
|
111
|
-
return `Installed the adr-writer template at ${dir}/templates/template.md.`;
|
|
112
|
-
} catch {
|
|
113
|
-
return null; // never block an ADR over the template copy
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function main() {
|
|
118
|
-
const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
|
|
119
|
-
const command = payload?.tool_input?.command || '';
|
|
120
|
-
const isInit = invokesAdr(command, 'init');
|
|
121
|
-
const isNew = invokesAdr(command, 'new');
|
|
122
|
-
if (!isInit && !isNew) return allow();
|
|
123
|
-
|
|
124
|
-
if (isInitialized()) {
|
|
125
|
-
if (isInit) {
|
|
126
|
-
return deny(
|
|
127
|
-
'.adr-dir already exists — do not run `adr init` again, it would add a duplicate ' +
|
|
128
|
-
'baseline ADR and burn the next number. Run `adr new` directly.'
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
return allow(ensureTemplate()); // adr new, already set up: nothing else to do
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (hasNumberedAdrs()) {
|
|
135
|
-
// Migration: numbered ADRs exist but `.adr-dir` does not. `adr init` would
|
|
136
|
-
// still add a baseline ADR here, so it is never the right command.
|
|
137
|
-
if (isInit) {
|
|
138
|
-
return deny(
|
|
139
|
-
'Numbered ADRs already exist without `.adr-dir` — this is a migration, not a fresh ' +
|
|
140
|
-
'project. `adr init` would add a duplicate baseline ADR and burn the next number. ' +
|
|
141
|
-
'Run `adr new` directly; `.adr-dir` is set up automatically.'
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
fs.writeFileSync(ADR_DIR_FILE, `${ADR_DIR}\n`);
|
|
145
|
-
const note = ensureTemplate();
|
|
146
|
-
return allow(
|
|
147
|
-
`Migration detected: wrote .adr-dir (${ADR_DIR}) by hand, no baseline ADR added.` +
|
|
148
|
-
(note ? `\n${note}` : '')
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Fresh project.
|
|
153
|
-
if (isInit) return allow(); // the agent's own `adr init` call is correct here — let it run
|
|
154
|
-
const res = runAdrInit();
|
|
155
|
-
if (res.error || res.status !== 0) {
|
|
156
|
-
return allow(
|
|
157
|
-
`Could not run \`adr init ${ADR_DIR}\` (${res.error?.message || res.stderr}); ` +
|
|
158
|
-
'letting the original command run and fail with its own error.'
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
normalizeAdrDirFile();
|
|
162
|
-
const note = ensureTemplate();
|
|
163
|
-
return allow(
|
|
164
|
-
`Fresh project: ran \`adr init ${ADR_DIR}\` (creates the baseline ADR).` +
|
|
165
|
-
(note ? `\n${note}` : '')
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
main();
|
|
171
|
-
} catch {
|
|
172
|
-
allow(); // fail open
|
|
173
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse guard: set up `.adr-dir` before the first `adr new` in a project,
|
|
3
|
+
// so the agent never has to pick between `adr init` and a hand-written file.
|
|
4
|
+
//
|
|
5
|
+
// Two setup paths, and picking the wrong one is destructive:
|
|
6
|
+
// - fresh (no `.adr-dir`, no numbered ADRs yet): `adr init docs/adr` is safe —
|
|
7
|
+
// it creates docs/adr/, writes `.adr-dir`, and adds a baseline ADR.
|
|
8
|
+
// - migration (numbered ADRs already exist, but no `.adr-dir`): `adr init`
|
|
9
|
+
// would ALSO add that baseline ADR, burning the next real number. The fix
|
|
10
|
+
// is a plain `.adr-dir` file with no tool call.
|
|
11
|
+
// Already initialized: nothing to do, both paths are a no-op.
|
|
12
|
+
//
|
|
13
|
+
// Also makes sure this skill's ADR template is the one `adr new` picks up, by
|
|
14
|
+
// placing it at `<adr-dir>/templates/template.md` — the third step of the tool's
|
|
15
|
+
// own template resolution, and the reason no env var has to be injected.
|
|
16
|
+
//
|
|
17
|
+
// Runs the setup itself (as a side effect) before allowing `adr new` through,
|
|
18
|
+
// and denies a *direct* `adr init` call whenever running it would be wrong —
|
|
19
|
+
// already initialized, or migration state — since either would burn a number.
|
|
20
|
+
// Anything it does is echoed to stdout so it stays visible in the transcript.
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const { spawnSync } = require('child_process');
|
|
25
|
+
const { invokesAdr } = require('./lib/adr/command-scan.cjs');
|
|
26
|
+
|
|
27
|
+
const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
28
|
+
const ADR_DIR = 'docs/adr';
|
|
29
|
+
const ADR_DIR_FILE = path.join(root, '.adr-dir');
|
|
30
|
+
const ADR_FILE = /^\d+-.*\.md$/;
|
|
31
|
+
const SKILL_TEMPLATE = path.join(
|
|
32
|
+
root,
|
|
33
|
+
'.claude',
|
|
34
|
+
'skills',
|
|
35
|
+
'adr-writer',
|
|
36
|
+
'references',
|
|
37
|
+
'adr-template.md'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
function allow(message) {
|
|
41
|
+
if (message) process.stdout.write(message + '\n');
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function deny(reason) {
|
|
46
|
+
process.stdout.write(
|
|
47
|
+
JSON.stringify({
|
|
48
|
+
hookSpecificOutput: {
|
|
49
|
+
hookEventName: 'PreToolUse',
|
|
50
|
+
permissionDecision: 'deny',
|
|
51
|
+
permissionDecisionReason: reason,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readAdrDir() {
|
|
59
|
+
try {
|
|
60
|
+
return fs.readFileSync(ADR_DIR_FILE, 'utf8').trim() || null;
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isInitialized() {
|
|
67
|
+
return readAdrDir() !== null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function hasNumberedAdrs() {
|
|
71
|
+
try {
|
|
72
|
+
return fs.readdirSync(path.join(root, ADR_DIR)).some((f) => ADR_FILE.test(f));
|
|
73
|
+
} catch {
|
|
74
|
+
return false; // directory doesn't exist yet -> nothing to migrate
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function runAdrInit() {
|
|
79
|
+
// `adr` is an npm bin — a real executable on every platform, so it is called
|
|
80
|
+
// directly rather than through a shell script interpreter. `shell: true` is
|
|
81
|
+
// here only because npm installs it as `adr.cmd` on Windows, which
|
|
82
|
+
// CreateProcess cannot launch on its own; every argument is a constant, so
|
|
83
|
+
// there is nothing for a shell to interpolate.
|
|
84
|
+
return spawnSync('adr', ['init', ADR_DIR], { cwd: root, shell: true, encoding: 'utf8' });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// `adr init` writes `.adr-dir` with `path.relative`, so on Windows it lands as
|
|
88
|
+
// `docs\adr` with no trailing newline. That file is committed and read on every
|
|
89
|
+
// other machine, where a backslash is an ordinary filename character and not a
|
|
90
|
+
// separator. Rewrite it in the portable form the rest of this toolchain emits.
|
|
91
|
+
function normalizeAdrDirFile() {
|
|
92
|
+
const declared = readAdrDir();
|
|
93
|
+
if (!declared) return;
|
|
94
|
+
fs.writeFileSync(ADR_DIR_FILE, `${declared.split(path.sep).join('/')}\n`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// The tool resolves its template as: explicit argument, then $ADR_TEMPLATE, then
|
|
98
|
+
// `<adr-dir>/templates/template.md`, then its own bundled default. Copying this
|
|
99
|
+
// skill's template into the third slot means a bare `adr new` produces a
|
|
100
|
+
// musketeer ADR with no environment set up for it.
|
|
101
|
+
//
|
|
102
|
+
// Only ever writes when the file is absent, so a project that has customised its
|
|
103
|
+
// own template keeps it.
|
|
104
|
+
function ensureTemplate() {
|
|
105
|
+
try {
|
|
106
|
+
const dir = readAdrDir() || ADR_DIR;
|
|
107
|
+
const target = path.join(root, dir, 'templates', 'template.md');
|
|
108
|
+
if (fs.existsSync(target) || !fs.existsSync(SKILL_TEMPLATE)) return null;
|
|
109
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
110
|
+
fs.copyFileSync(SKILL_TEMPLATE, target);
|
|
111
|
+
return `Installed the adr-writer template at ${dir}/templates/template.md.`;
|
|
112
|
+
} catch {
|
|
113
|
+
return null; // never block an ADR over the template copy
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function main() {
|
|
118
|
+
const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
|
|
119
|
+
const command = payload?.tool_input?.command || '';
|
|
120
|
+
const isInit = invokesAdr(command, 'init');
|
|
121
|
+
const isNew = invokesAdr(command, 'new');
|
|
122
|
+
if (!isInit && !isNew) return allow();
|
|
123
|
+
|
|
124
|
+
if (isInitialized()) {
|
|
125
|
+
if (isInit) {
|
|
126
|
+
return deny(
|
|
127
|
+
'.adr-dir already exists — do not run `adr init` again, it would add a duplicate ' +
|
|
128
|
+
'baseline ADR and burn the next number. Run `adr new` directly.'
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return allow(ensureTemplate()); // adr new, already set up: nothing else to do
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (hasNumberedAdrs()) {
|
|
135
|
+
// Migration: numbered ADRs exist but `.adr-dir` does not. `adr init` would
|
|
136
|
+
// still add a baseline ADR here, so it is never the right command.
|
|
137
|
+
if (isInit) {
|
|
138
|
+
return deny(
|
|
139
|
+
'Numbered ADRs already exist without `.adr-dir` — this is a migration, not a fresh ' +
|
|
140
|
+
'project. `adr init` would add a duplicate baseline ADR and burn the next number. ' +
|
|
141
|
+
'Run `adr new` directly; `.adr-dir` is set up automatically.'
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
fs.writeFileSync(ADR_DIR_FILE, `${ADR_DIR}\n`);
|
|
145
|
+
const note = ensureTemplate();
|
|
146
|
+
return allow(
|
|
147
|
+
`Migration detected: wrote .adr-dir (${ADR_DIR}) by hand, no baseline ADR added.` +
|
|
148
|
+
(note ? `\n${note}` : '')
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Fresh project.
|
|
153
|
+
if (isInit) return allow(); // the agent's own `adr init` call is correct here — let it run
|
|
154
|
+
const res = runAdrInit();
|
|
155
|
+
if (res.error || res.status !== 0) {
|
|
156
|
+
return allow(
|
|
157
|
+
`Could not run \`adr init ${ADR_DIR}\` (${res.error?.message || res.stderr}); ` +
|
|
158
|
+
'letting the original command run and fail with its own error.'
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
normalizeAdrDirFile();
|
|
162
|
+
const note = ensureTemplate();
|
|
163
|
+
return allow(
|
|
164
|
+
`Fresh project: ran \`adr init ${ADR_DIR}\` (creates the baseline ADR).` +
|
|
165
|
+
(note ? `\n${note}` : '')
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
main();
|
|
171
|
+
} catch {
|
|
172
|
+
allow(); // fail open
|
|
173
|
+
}
|