@vpxa/aikit 0.1.214 → 0.1.215

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.
@@ -137,39 +137,33 @@ decision-makers: '{list everyone who owns the decision}'
137
137
 
138
138
  ## Directory
139
139
 
140
- If the repo already has an ADR directory, keep it.
140
+ If repo already has ADR dir, keep it.
141
141
 
142
- If the repo has no ADR directory, choose based on project size:
142
+ If not:
143
143
 
144
- - **\`docs/decisions/\`** — MADR default, recommended for projects with existing \`docs/\` structure.
145
- - **\`adr/\`** — simpler alternative for smaller repos.
144
+ - **\`docs/decisions/\`** — MADR default when repo already uses \`docs/\`.
145
+ - **\`adr/\`** — simpler small-repo default.
146
146
 
147
- Detection order (used by scripts): \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`docs/adrs/\`, \`decisions/\`.
147
+ Detection order: \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`docs/adrs/\`, \`decisions/\`.
148
148
 
149
149
  ## Filename Conventions
150
150
 
151
151
  Pattern: \`YYYY-MM-DD-title-with-dashes.md\`
152
152
 
153
- - \`YYYY-MM-DD\` is the ADR creation date (matches the \`date\` frontmatter field).
154
- - Title uses lowercase, dashes, present-tense imperative verb phrase.
155
- - Examples: \`2025-06-15-choose-database.md\`, \`2025-07-01-adopt-adrs.md\`
156
- - Multiple ADRs on the same date are fine — the slug suffix disambiguates them.
157
-
158
- If a repo already uses slug-only filenames (no date prefix), follow that convention.
153
+ - Date matches \`date\` frontmatter.
154
+ - Slug: lowercase, dash-separated, imperative verb phrase.
155
+ - Same-day ADRs are fine; slug disambiguates.
156
+ - If repo already uses slug-only filenames, keep that convention.
159
157
 
160
158
  ## Minimal Sections
161
159
 
162
- At minimum, every ADR must clearly include:
160
+ Every ADR needs:
163
161
 
164
- 1. **Context**: why the decision exists now, what constraints/drivers apply.
162
+ 1. **Context**: why now; which constraints/drivers apply.
165
163
  2. **Decision**: what is chosen.
166
- 3. **Consequences**: what becomes easier/harder, risks, costs, follow-ups.
167
-
168
- For agent-first ADRs, also ensure:
164
+ 3. **Consequences**: what gets easier/harder, plus risks, costs, follow-ups.
169
165
 
170
- - Constraints are explicit and measurable
171
- - Non-goals are stated
172
- - Follow-up tasks are identified
166
+ For agent-first ADRs also state explicit constraints, non-goals, and follow-up tasks.
173
167
 
174
168
  ## Status Values
175
169
 
@@ -187,11 +181,11 @@ Common statuses:
187
181
 
188
182
  | Status | Meaning |
189
183
  | ----------------------------- | ------------------------------------------------------------- |
190
- | \`proposed\` | Under discussion, not yet decided |
191
- | \`accepted\` | Decision is active and should be followed |
192
- | \`rejected\` | Considered but explicitly not adopted |
193
- | \`deprecated\` | Was accepted but no longer applies — explain replacement path |
194
- | \`superseded by [title](link)\` | Replaced by a newer ADR — always link both ways |
184
+ | \`proposed\` | Under discussion |
185
+ | \`accepted\` | Active decision |
186
+ | \`rejected\` | Considered, not adopted |
187
+ | \`deprecated\` | No longer applies |
188
+ | \`superseded by [title](link)\` | Replaced by newer ADR |
195
189
 
196
190
  ## YAML Front Matter Fields
197
191
 
@@ -200,96 +194,31 @@ Common statuses:
200
194
  | \`status\` | Yes | Current lifecycle state |
201
195
  | \`date\` | Yes | Date of last status change (YYYY-MM-DD) |
202
196
  | \`decision-makers\` | Yes | People who own the decision |
203
- | \`consulted\` | No | Subject-matter experts consulted (two-way communication) |
204
- | \`informed\` | No | Stakeholders kept up-to-date (one-way communication) |
197
+ | \`consulted\` | No | Experts consulted |
198
+ | \`informed\` | No | Stakeholders updated |
205
199
 
206
- The \`consulted\` and \`informed\` fields follow the RACI model and are useful for audit trails in larger teams.
200
+ \`consulted\` and \`informed\` follow RACI.
207
201
 
208
202
  ## Mutability
209
203
 
210
- - Prefer appending new information with a date stamp over rewriting existing content.
211
- - If a decision is replaced, create a new ADR and explicitly supersede the old one.
212
- - Status changes and after-action notes are fine to edit in-place.
204
+ - Append dated notes instead of rewriting prior reasoning.
205
+ - If decision changes, create new ADR and supersede old one.
206
+ - Status changes and after-action notes can be edited in place.
213
207
 
214
208
  ## Categories (Large Projects)
215
209
 
216
- For repos accumulating many ADRs, use subdirectories:
217
-
218
- \`\`\`
219
- contributing/decisions/ # or docs/decisions/
220
- backend/
221
- 2025-06-15-use-postgres.md
222
- frontend/
223
- 2025-06-20-use-react.md
224
- infrastructure/
225
- 2025-07-01-use-terraform.md
226
- \`\`\`
227
-
228
- Date prefixes are local to each category. Choose a categorization scheme early (by architectural layer, by domain, by team) and document it in the index.
229
-
230
- Alternative: use tags or a flat structure with a searchable index. Subdirectories are simpler and work with all tools.
210
+ For many ADRs, subdirectories are fine. Date prefixes stay local to category; pick one scheme early and document it in index.
231
211
  `},{file:`references/examples.md`,content:`# ADR Examples
232
212
 
233
- These are filled-out examples showing the same decision at two levels of detail. Use these as reference when drafting ADRs — never leave placeholder text in a real ADR.
213
+ Filled examples of same decision at two detail levels. Reference only; never ship placeholder text.
234
214
 
235
215
  ## Short Version (Simple Template)
236
216
 
237
- \`\`\`markdown
238
- ---
239
- status: accepted
240
- date: 2025-06-15
241
- decision-makers: Sarah Chen, Joel
242
- ---
243
-
244
- # Use SQLite for local development database
245
-
246
- ## Context and Problem Statement
247
-
248
- Our integration tests require a database but currently hit a shared PostgreSQL instance, causing flaky tests from concurrent writes and slow CI (3+ minute setup per run). We need a fast, isolated database for local dev and CI that doesn't require infrastructure provisioning.
249
-
250
- ## Decision
251
-
252
- Use SQLite (via sql.js) for local development and CI test runs. Production remains on PostgreSQL. We'll use a thin data-access layer that abstracts the database engine, tested against both SQLite and PostgreSQL in CI.
253
-
254
- Non-goals: we are NOT migrating production to SQLite or building a full ORM abstraction.
255
-
256
- ## Consequences
257
-
258
- - Good, because CI setup drops from 3+ minutes to ~2 seconds (no DB provisioning)
259
- - Good, because tests are fully isolated — no shared state between runs
260
- - Good, because developers can run the full test suite offline
261
- - Bad, because we must maintain compatibility between SQLite and PostgreSQL SQL dialects
262
- - Bad, because some PostgreSQL-specific features (JSONB operators, array columns) can't be tested locally
263
-
264
- ## Implementation Plan
265
-
266
- - **Affected paths**: \`src/db/client.ts\` (new abstraction layer), \`src/db/sqlite-client.ts\` (new), \`src/db/pg-client.ts\` (refactored from current inline usage), \`tests/setup.ts\`, \`package.json\`
267
- - **Dependencies**: add \`sql.js@1.x\` and \`@types/sql.js@1.x\` as devDependencies; no production dependency changes
268
- - **Patterns to follow**: existing repository pattern in \`src/db/repositories/\` — all queries go through repository methods, never raw SQL in business logic
269
- - **Patterns to avoid**: do not import \`sql.js\` or \`pg\` directly outside \`src/db/\`; do not use PostgreSQL-specific SQL (JSONB operators, \`ANY()\`, array literals) in shared queries
270
-
271
- ### Verification
272
-
273
- - [ ] \`npm test\` passes with \`DB_ENGINE=sqlite\` (default for test env)
274
- - [ ] \`npm test\` passes with \`DB_ENGINE=postgres\` against a real PostgreSQL instance
275
- - [ ] No imports of \`sql.js\` or \`pg\` outside \`src/db/\`
276
- - [ ] CI pipeline total time under 90 seconds (was 5+ minutes)
277
- - [ ] \`src/db/client.ts\` exports a unified interface used by all repositories
278
-
279
- ## Alternatives Considered
280
-
281
- - Docker PostgreSQL per CI run: Reliable parity, but adds 90s+ startup and requires Docker-in-Docker on CI.
282
- - In-memory PostgreSQL (pg-mem): Good API compatibility, but incomplete support for our schema (triggers, CTEs) and unmaintained.
283
-
284
- ## More Information
285
-
286
- - Follow-up: create weekly CI job running full suite against real PostgreSQL (#348)
287
- - Revisit trigger: if dialect-drift bugs exceed 2 per quarter, reconsider Docker PostgreSQL approach
288
- \`\`\`
217
+ Use \`assets/templates/adr-simple.md\` for short-form drafting. The filled example below covers the same decision space in more detail.
289
218
 
290
219
  ## Long Version (MADR Template)
291
220
 
292
- The same decision with full options analysis:
221
+ Same decision with full options analysis:
293
222
 
294
223
  \`\`\`markdown
295
224
  ---
@@ -423,81 +352,73 @@ Spin up a fresh PostgreSQL container for each CI job.
423
352
  \`\`\`
424
353
  `},{file:`references/review-checklist.md`,content:`# ADR Review Checklist
425
354
 
426
- Use this checklist in Phase 3 to validate an ADR before finalizing. The goal: **could a coding agent read this ADR and start implementing the decision immediately, without asking any clarifying questions?**
355
+ Use in Phase 3. Goal: **could a coding agent read this ADR and implement without follow-up questions?**
427
356
 
428
357
  ## Agent-Readiness Checks
429
358
 
430
359
  ### Context & Problem
431
360
 
432
- - [ ] A reader with no prior context can understand why this decision exists
433
- - [ ] The trigger is clear (what changed, broke, or is about to break)
434
- - [ ] No tribal knowledge is assumed acronyms are defined, systems are named explicitly
435
- - [ ] Links to relevant issues, PRs, or prior ADRs are included
361
+ - [ ] Reader with no context can understand why decision exists
362
+ - [ ] Trigger is clear
363
+ - [ ] No tribal knowledge assumed; acronyms and systems are explicit
364
+ - [ ] Relevant issues, PRs, or ADRs are linked
436
365
 
437
366
  ### Decision
438
367
 
439
- - [ ] The decision is specific enough to act on (not "use a better approach" but "use X for Y")
440
- - [ ] Scope is bounded — what's in AND what's out (non-goals)
441
- - [ ] Constraints are explicit and measurable where possible (e.g., "< 200ms p95" not "fast enough")
368
+ - [ ] Decision is specific enough to act on
369
+ - [ ] Scope is bounded; non-goals are stated
370
+ - [ ] Constraints are explicit and measurable where possible
442
371
 
443
372
  ### Consequences
444
373
 
445
- - [ ] Each consequence is concrete and actionable, not aspirational
446
- - [ ] Follow-up tasks are identified (migrations, config changes, documentation, new tests)
447
- - [ ] Risks are stated with mitigation strategies or acceptance rationale
448
- - [ ] No consequence is a disguised restatement of the decision
374
+ - [ ] Consequences are concrete and actionable
375
+ - [ ] Follow-up tasks are identified
376
+ - [ ] Risks include mitigation or acceptance rationale
449
377
 
450
378
  ### Implementation Plan
451
379
 
452
- - [ ] Affected files/directories are named explicitly (not "the database code" but "src/db/client.ts")
453
- - [ ] Dependencies to add/remove are specified with version constraints
454
- - [ ] Patterns to follow reference existing code (not abstract descriptions)
455
- - [ ] Patterns to avoid are stated (what NOT to do)
456
- - [ ] Configuration changes are listed (env vars, config files, feature flags)
457
- - [ ] If replacing something, migration steps are described
380
+ - [ ] Affected files/directories are explicit
381
+ - [ ] Dependency changes include version constraints
382
+ - [ ] Patterns to follow reference existing code
383
+ - [ ] Patterns to avoid are explicit
384
+ - [ ] Config and migration steps are listed when needed
458
385
 
459
386
  ### Verification
460
387
 
461
- - [ ] Criteria are checkboxes, not prose
462
- - [ ] Each criterion is testable — an agent could write a test or run a command to check it
463
- - [ ] Criteria cover both "it works" (functional) and "it's done right" (structural/architectural)
464
- - [ ] No criterion is vague ("it performs well" → "p95 latency < 200ms under 100 concurrent requests")
388
+ - [ ] Criteria are checkboxes
389
+ - [ ] Each criterion is testable
390
+ - [ ] Criteria cover functional and structural outcomes
465
391
 
466
392
  ### Options (MADR template)
467
393
 
468
- - [ ] At least two options were genuinely considered (not just "do the thing" vs "do nothing")
469
- - [ ] Each option has real pros AND cons (not a straw-man comparison)
470
- - [ ] The justification for the chosen option references specific drivers or tradeoffs
471
- - [ ] Rejected options explain WHY they were rejected, not just what they are
394
+ - [ ] At least two genuine options were considered
395
+ - [ ] Each option has real pros and cons
396
+ - [ ] Chosen option cites specific drivers or tradeoffs
472
397
 
473
398
  ### Meta
474
399
 
475
- - [ ] Status is set correctly (usually \`proposed\` for new ADRs)
400
+ - [ ] Status is correct
476
401
  - [ ] Date is set
477
402
  - [ ] Decision-makers are listed
478
- - [ ] Title is a verb phrase describing the decision (not the problem)
403
+ - [ ] Title is a decision verb phrase
479
404
  - [ ] Filename follows repo conventions
480
405
 
481
406
  ## Quick Scoring
482
407
 
483
- Count the checked items. This isn't a gate — it's a conversation tool.
408
+ Count checked items.
484
409
 
485
410
  - **All checked**: Ship it.
486
- - **1–3 unchecked**: Discuss the gaps with the human. Most can be fixed in a minute.
487
- - **4+ unchecked**: The ADR needs more work. Go back to Phase 1 for the fuzzy areas.
411
+ - **1–3 unchecked**: Discuss gaps.
412
+ - **4+ unchecked**: Return to Phase 1.
488
413
 
489
414
  ## Common Failure Modes
490
415
 
491
- | Symptom | Root Cause | Fix |
492
- | ------------------------------------------ | -------------------------------------- | ----------------------------------------------------------- |
493
- | "Improve performance" as a consequence | Vague intent | Ask: "improve which metric, by how much, measured how?" |
494
- | Only one option listed | Decision already made, ADR is post-hoc | Ask: "what did you reject and why?" — capture the reasoning |
495
- | Context reads like a solution pitch | Skipped problem framing | Rewrite context as the problem, move solution to Decision |
496
- | Consequences are all positive | Cherry-picking | Ask: "what gets harder? what's the maintenance cost?" |
497
- | "We decided to use X" with no why | Missing justification | Ask: "why X over Y?" — the 'over Y' forces comparison |
498
- | Implementation Plan says "update the code" | Too abstract | Ask: "which files, which functions, what pattern?" |
499
- | Verification says "it works" | Not testable | Ask: "what command would you run to prove it works?" |
500
- | No affected paths listed | Implementation Plan is hand-wavy | Agent should scan the codebase and propose specific paths |
416
+ | Symptom | Root Cause | Fix |
417
+ | --- | --- | --- |
418
+ | "Improve performance" as a consequence | Vague intent | Ask: "which metric, by how much, measured how?" |
419
+ | Only one option listed | Decision already made | Ask: "what did you reject and why?" |
420
+ | Implementation Plan says "update the code" | Too abstract | Ask: "which files, which functions, what pattern?" |
421
+ | Verification says "it works" | Not testable | Ask: "what command proves it works?" |
501
422
  `},{file:`references/template-variants.md`,content:`# Template Variants
502
423
 
503
424
  This skill ships two templates in \`assets/templates/\`.
@@ -506,11 +427,11 @@ This skill ships two templates in \`assets/templates/\`.
506
427
 
507
428
  File: \`assets/templates/adr-simple.md\`
508
429
 
509
- Use this when:
430
+ Use when:
510
431
 
511
- - The decision is straightforward (one clear winner, minimal tradeoffs)
512
- - You mainly need "why, what, consequences, how to implement"
513
- - Alternatives are few and can be dismissed in a sentence each
432
+ - Decision is straightforward
433
+ - You need "why, what, consequences, impl"
434
+ - Alternatives are few and brief
514
435
  - Speed matters more than exhaustive comparison
515
436
 
516
437
  Sections: Context and Problem Statement → Decision → Consequences → Implementation Plan → Verification → Alternatives Considered (optional) → More Information (optional).
@@ -519,25 +440,24 @@ Sections: Context and Problem Statement → Decision → Consequences → Implem
519
440
 
520
441
  File: \`assets/templates/adr-madr.md\`
521
442
 
522
- Use this when:
443
+ Use when:
523
444
 
524
- - You have multiple real options and want to document structured tradeoffs
525
- - You need to capture decision drivers explicitly (what criteria mattered)
526
- - The decision is likely to be revisited and the comparison needs to survive
527
- - Stakeholders need to see the reasoning process, not just the outcome
445
+ - Multiple real options need structured tradeoffs
446
+ - Decision drivers must be explicit
447
+ - Decision may be revisited later
448
+ - Stakeholders need reasoning, not only outcome
528
449
 
529
450
  Sections: Context and Problem Statement → Decision Drivers (optional) → Considered Options → Decision Outcome → Consequences → Implementation Plan → Verification → Pros and Cons of the Options (optional) → More Information (optional).
530
451
 
531
- This template aligns with [MADR 4.0](https://adr.github.io/madr/) and extends it with agent-first sections.
452
+ Aligns with [MADR 4.0](https://adr.github.io/madr/) plus agent-first sections.
532
453
 
533
454
  ## Both Templates Share
534
455
 
535
- - **YAML front matter** for metadata (status, date, decision-makers, consulted, informed)
536
- - **Implementation Plan** — affected paths, dependencies, patterns to follow/avoid, configuration, migration steps. This is what makes the ADR an executable spec for agents.
537
- - **Verification as checkboxes** — testable criteria an agent can validate after implementation
538
- - **Agent-first framing**: placeholder text prompts you to be specific, measurable, and self-contained
539
- - **"More Information" section** for cross-links, follow-ups, and revisit triggers
540
- - **"Neutral, because..."** as a third argument category alongside Good and Bad
456
+ - **YAML front matter** for status, date, decision-makers, consulted, informed
457
+ - **Implementation Plan** — affected paths, deps, patterns to follow/avoid, config, migration steps
458
+ - **Verification as checkboxes** — testable criteria after impl
459
+ - **Agent-first framing** placeholders push specificity and measurable constraints
460
+ - **"More Information"** for cross-links, follow-ups, revisit triggers
541
461
 
542
462
  ## Choosing Between Them
543
463
 
@@ -549,829 +469,393 @@ This template aligns with [MADR 4.0](https://adr.github.io/madr/) and extends it
549
469
  | Expected lifetime | Months | Years |
550
470
  | Needs stakeholder review | No | Yes |
551
471
 
552
- When in doubt, start with Simple. You can always expand to MADR if the discussion reveals more complexity.
472
+ When in doubt, start with Simple. Expand to MADR if needed.
553
473
  `},{file:`scripts/bootstrap_adr.js`,content:`#!/usr/bin/env node
554
- /**
555
- * Bootstrap ADRs in a repo:
556
- * - create ADR directory
557
- * - create adr/README.md (index) using a template
558
- * - create first ADR: "Adopt architecture decision records"
559
- */
560
-
561
- const fs = require('node:fs');
562
- const path = require('node:path');
563
-
564
- function die(msg) {
565
- process.stderr.write(\`\${msg}\\n\`);
566
- process.exit(1);
474
+ const fs=require('node:fs');
475
+ const path=require('node:path');
476
+
477
+ function die(msg){process.stderr.write(\`\${msg}\\n\`);process.exit(1);}
478
+ function toPosix(value){return value.split(path.sep).join('/');}
479
+ function slugify(text){
480
+ const value=String(text||'').trim().toLowerCase();
481
+ return value.replace(/['"\`]/g,'').replace(/[^a-z0-9]+/g,'-').replace(/-{2,}/g,'-').replace(/^-+|-+$/g,'')||'decision';
567
482
  }
568
-
569
- function parseArgs(argv) {
570
- const out = {
571
- repoRoot: '.',
572
- dir: 'adr',
573
- forceIndex: false,
574
- indexFile: null,
575
- firstTitle: 'Adopt architecture decision records',
576
- firstStatus: 'accepted',
577
- deciders: '',
578
- technicalStory: '',
579
- strategy: 'date',
580
- json: false,
581
- };
582
-
583
- for (let i = 2; i < argv.length; i++) {
584
- const a = argv[i];
585
- const next = () => {
586
- if (i + 1 >= argv.length) die(\`Missing value for \${a}\`);
587
- return argv[++i];
588
- };
589
-
590
- if (a === '--repo-root') out.repoRoot = next();
591
- else if (a === '--dir') out.dir = next();
592
- else if (a === '--force-index') out.forceIndex = true;
593
- else if (a === '--index-file') out.indexFile = next();
594
- else if (a === '--first-title') out.firstTitle = next();
595
- else if (a === '--first-status') out.firstStatus = next();
596
- else if (a === '--deciders') out.deciders = next();
597
- else if (a === '--technical-story') out.technicalStory = next();
598
- else if (a === '--strategy') out.strategy = next();
599
- else if (a === '--json') out.json = true;
600
- else if (a === '--help' || a === '-h') {
601
- process.stdout.write(
602
- [
603
- 'Usage: node bootstrap_adr.js [options]',
604
- '',
605
- 'Options:',
606
- ' --repo-root <path> Repo root (default: .)',
607
- ' --dir <path> ADR directory (default: adr)',
608
- ' --index-file <path> Override index file path (relative to repo root unless absolute)',
609
- ' --force-index Overwrite index file if it exists',
610
- ' --first-title <text> Title for initial ADR',
611
- ' --first-status <text> Status for initial ADR (default: accepted)',
612
- ' --strategy date|slug|auto Filename strategy for initial ADR (default: date)',
613
- ' --json Output machine-readable JSON (default: off)',
614
- '',
615
- ].join('\\n'),
616
- );
483
+ function parseArgs(argv){
484
+ const out={repoRoot:'.',dir:'adr',forceIndex:false,indexFile:null,firstTitle:'Adopt architecture decision records',firstStatus:'accepted',deciders:'',technicalStory:'',strategy:'date',json:false};
485
+ for(let index=2;index<argv.length;index++){
486
+ const arg=argv[index];
487
+ const next=()=>{if(index+1>=argv.length) die(\`Missing value for \${arg}\`);return argv[++index];};
488
+ if(arg==='--repo-root') out.repoRoot=next();
489
+ else if(arg==='--dir') out.dir=next();
490
+ else if(arg==='--force-index') out.forceIndex=true;
491
+ else if(arg==='--index-file') out.indexFile=next();
492
+ else if(arg==='--first-title') out.firstTitle=next();
493
+ else if(arg==='--first-status') out.firstStatus=next();
494
+ else if(arg==='--deciders') out.deciders=next();
495
+ else if(arg==='--technical-story') out.technicalStory=next();
496
+ else if(arg==='--strategy') out.strategy=next();
497
+ else if(arg==='--json') out.json=true;
498
+ else if(arg==='--help'||arg==='-h'){
499
+ process.stdout.write([
500
+ 'Usage: node bootstrap_adr.js [options]',
501
+ '',
502
+ 'Options:',
503
+ ' --repo-root <path> Repo root (default: .)',
504
+ ' --dir <path> ADR dir (default: adr)',
505
+ ' --index-file <path> Index path override',
506
+ ' --force-index Overwrite existing index',
507
+ ' --first-title <text> Initial ADR title',
508
+ ' --first-status <text> Initial ADR status (default: accepted)',
509
+ ' --strategy date|slug|auto Filename strategy (default: date)',
510
+ ' --json Emit JSON',
511
+ '',
512
+ ].join('\\n'));
617
513
  process.exit(0);
618
- } else {
619
- die(\`Unknown arg: \${a}\`);
620
- }
514
+ }else die(\`Unknown arg: \${arg}\`);
621
515
  }
622
-
623
- if (!['auto', 'date', 'slug'].includes(out.strategy)) die(\`Invalid --strategy: \${out.strategy}\`);
516
+ if(!['auto','date','slug'].includes(out.strategy)) die(\`Invalid --strategy: \${out.strategy}\`);
624
517
  return out;
625
518
  }
626
-
627
- function loadReadmeTemplate() {
628
- const skillRoot = path.resolve(__dirname, '..');
629
- const templatePath = path.join(skillRoot, 'assets', 'templates', 'adr-readme.md');
630
- if (!fs.existsSync(templatePath)) die(\`README template not found: \${templatePath}\`);
631
- return fs.readFileSync(templatePath, 'utf8');
632
- }
633
-
634
- function writeIndex(indexFile, adrDirName, { force }) {
635
- if (fs.existsSync(indexFile) && !force) return;
636
- const content = loadReadmeTemplate().replaceAll('{ADR_DIR}', adrDirName);
637
- fs.mkdirSync(path.dirname(indexFile), { recursive: true });
638
- fs.writeFileSync(indexFile, \`\${content.trimEnd()}\\n\`, 'utf8');
519
+ function loadReadmeTemplate(){
520
+ const templatePath=path.join(path.resolve(__dirname,'..'),'assets','templates','adr-readme.md');
521
+ if(!fs.existsSync(templatePath)) die(\`README template not found: \${templatePath}\`);
522
+ return fs.readFileSync(templatePath,'utf8');
639
523
  }
640
-
641
- function slugify(text) {
642
- const t = String(text || '')
643
- .trim()
644
- .toLowerCase();
645
- const noQuotes = t.replace(/['"\`]/g, '');
646
- const dashed = noQuotes.replace(/[^a-z0-9]+/g, '-').replace(/-{2,}/g, '-');
647
- const trimmed = dashed.replace(/^-+/, '').replace(/-+$/, '');
648
- return trimmed || 'decision';
524
+ function writeIndex(indexFile,adrDirName,{force}){
525
+ if(fs.existsSync(indexFile)&&!force) return;
526
+ const content=loadReadmeTemplate().replaceAll('{ADR_DIR}',adrDirName);
527
+ fs.mkdirSync(path.dirname(indexFile),{recursive:true});
528
+ fs.writeFileSync(indexFile,\`\${content.trimEnd()}\\n\`,'utf8');
649
529
  }
650
-
651
- function toPosix(p) {
652
- return p.split(path.sep).join('/');
653
- }
654
-
655
- function generateFirstAdr({ title, status, date, deciders, adrDir }) {
656
- const deciderLine = deciders
657
- ? String(deciders)
658
- .split(',')
659
- .map((s) => s.trim())
660
- .filter(Boolean)
661
- .join(', ')
662
- : '';
663
-
530
+ function generateFirstAdr({title,status,date,deciders,adrDir}){
531
+ const decisionMakers=deciders?String(deciders).split(',').map((name)=>name.trim()).filter(Boolean).join(', '):'';
664
532
  return \`---
665
533
  status: \${status}
666
534
  date: \${date}
667
- decision-makers: \${deciderLine}
535
+ decision-makers: \${decisionMakers}
668
536
  ---
669
537
 
670
538
  # \${title}
671
539
 
672
540
  ## Context and Problem Statement
673
541
 
674
- Architecture decisions in this project are made implicitly — through code, conversations, and tribal knowledge. When a new contributor (human or AI agent) joins the codebase, there is no record of *why* things are built the way they are. This makes it hard to:
675
-
676
- - Understand whether a pattern is intentional or accidental
677
- - Know if a past decision still applies or has been superseded
678
- - Avoid relitigating decisions that were already carefully considered
679
-
680
- We need a lightweight, version-controlled way to capture decisions where the code lives.
542
+ Architecture decisions are mostly implicit: code, chat, tribal knowledge. New contributors cannot quickly recover why patterns exist or whether old decisions still apply. We need version-controlled decision records near code.
681
543
 
682
544
  ## Decision
683
545
 
684
- Adopt Architecture Decision Records (ADRs) using the MADR 4.0 format, stored in \\\`\${adrDir}/\\\`.
546
+ Adopt Architecture Decision Records (ADRs) using MADR 4.0, stored in \\\`\${adrDir}/\\\`.
685
547
 
686
548
  Conventions:
687
549
  - One ADR per file, named \\\`YYYY-MM-DD-title-with-dashes.md\\\`
688
- - New ADRs start as \\\`proposed\\\`, move to \\\`accepted\\\` or \\\`rejected\\\`
689
- - Superseded ADRs link to their replacement
690
- - ADRs are written to be self-contained a coding agent should be able to read one and implement the decision without further context
550
+ - New ADRs start as \\\`proposed\\\`, then move to \\\`accepted\\\` or \\\`rejected\\\`
551
+ - Superseded ADRs link to replacements
552
+ - ADRs are self-contained enough for coding agents to implement from them
691
553
 
692
554
  ## Consequences
693
555
 
694
- * Good, because decisions are discoverable and version-controlled alongside the code
695
- * Good, because new contributors (human or agent) can understand the "why" behind architecture choices
696
- * Good, because the team builds a shared decision log that prevents relitigating settled questions
697
- * Bad, because writing ADRs takes time though a good ADR saves more time than it costs
698
- * Neutral, because ADRs require periodic review to mark outdated decisions as deprecated or superseded
556
+ * Good, because decisions stay discoverable and version-controlled with code
557
+ * Good, because contributors can recover why architecture choices were made
558
+ * Bad, because writing ADRs costs time
559
+ * Neutral, because ADRs need periodic review for deprecation or supersession
699
560
 
700
561
  ## Alternatives Considered
701
562
 
702
- * No formal records: Continue making decisions in conversations and code comments. Rejected because context is lost and decisions get relitigated.
703
- * Wiki or Notion pages: Capture decisions outside the repo. Rejected because they drift out of sync with the code and are not version-controlled.
704
- * Lightweight RFCs: More heavyweight process with formal review cycles. Rejected as overkill for most decisions ADRs can scale up to RFC-level detail when needed.
563
+ * No formal records: Rejected because context is lost and decisions get relitigated.
564
+ * Wiki or Notion pages: Rejected because they drift from code and lack repo history.
565
+ * Lightweight RFCs: Rejected as too heavy for most decisions; ADRs can still scale up when needed.
705
566
  \`;
706
567
  }
707
-
708
- function updateIndexFile(indexFile, { relLink, title, status, date }) {
709
- if (!fs.existsSync(indexFile)) return;
710
- const content = fs.readFileSync(indexFile, 'utf8');
711
- if (content.includes(relLink)) return;
712
-
713
- const entryLine = \`- [\${title}](\${relLink}) (\${status}, \${date})\`;
714
-
715
- // Append after "## ADRs" heading if found, otherwise append at end
716
- const normalized = content.replace(/\\r\\n/g, '\\n');
717
- const lines = normalized.split('\\n');
718
- const headingIdx = lines.findIndex((l) => /^##\\s+ADRs\\s*$/i.test(l));
719
-
720
- if (headingIdx !== -1) {
721
- // Insert after the heading (and any blank line after it)
722
- let insertAt = headingIdx + 1;
723
- while (insertAt < lines.length && lines[insertAt].trim() === '') insertAt++;
724
- lines.splice(insertAt, 0, entryLine);
725
- } else {
726
- lines.push(entryLine);
727
- }
728
-
729
- fs.writeFileSync(indexFile, lines.join('\\n'), 'utf8');
568
+ function updateIndexFile(indexFile,{relLink,title,status,date}){
569
+ if(!fs.existsSync(indexFile)) return;
570
+ const content=fs.readFileSync(indexFile,'utf8');
571
+ if(content.includes(relLink)) return;
572
+ const entryLine=\`- [\${title}](\${relLink}) (\${status}, \${date})\`;
573
+ const lines=content.replace(/\\r\\n/g,'\\n').split('\\n');
574
+ const headingIndex=lines.findIndex((line)=>/^##\\s+ADRs\\s*$/i.test(line));
575
+ if(headingIndex!==-1){
576
+ let insertAt=headingIndex+1;
577
+ while(insertAt<lines.length&&lines[insertAt].trim()==='') insertAt++;
578
+ lines.splice(insertAt,0,entryLine);
579
+ }else lines.push(entryLine);
580
+ fs.writeFileSync(indexFile,lines.join('\\n'),'utf8');
730
581
  }
731
-
732
- function main() {
733
- const args = parseArgs(process.argv);
734
-
735
- const repoRoot = path.resolve(process.cwd(), args.repoRoot);
736
- if (!fs.existsSync(repoRoot)) die(\`Repo root does not exist: \${repoRoot}\`);
737
-
738
- const adrDir = path.resolve(repoRoot, args.dir);
739
- fs.mkdirSync(adrDir, { recursive: true });
740
-
741
- const indexFile = args.indexFile
742
- ? path.isAbsolute(args.indexFile)
743
- ? args.indexFile
744
- : path.resolve(repoRoot, args.indexFile)
745
- : path.join(adrDir, 'README.md');
746
-
747
- const indexExistedBefore = fs.existsSync(indexFile);
748
- writeIndex(indexFile, args.dir, { force: args.forceIndex });
749
- const indexWritten = fs.existsSync(indexFile) && (!indexExistedBefore || args.forceIndex);
750
-
751
- // Create the first ADR as a filled-out decision (not a blank template).
752
- const relIndex = path.isAbsolute(indexFile) ? path.relative(repoRoot, indexFile) : indexFile;
753
- const today = new Date().toISOString().slice(0, 10);
754
-
755
- const firstAdrContent = generateFirstAdr({
756
- title: args.firstTitle,
757
- status: args.firstStatus,
758
- date: today,
759
- deciders: args.deciders,
760
- adrDir: args.dir,
761
- });
762
-
763
- // Determine filename using same logic as new_adr.js
764
- const strategy = args.strategy === 'auto' ? 'date' : args.strategy;
765
- let firstAdrFilename;
766
- if (strategy === 'date') {
767
- firstAdrFilename = \`\${today}-\${slugify(args.firstTitle)}.md\`;
768
- } else {
769
- firstAdrFilename = \`\${slugify(args.firstTitle)}.md\`;
770
- }
771
- const firstAdrPath = path.join(adrDir, firstAdrFilename);
772
- fs.writeFileSync(firstAdrPath, \`\${firstAdrContent.trimEnd()}\\n\`, 'utf8');
773
-
774
- // Update index
775
- const relLink = toPosix(path.relative(path.dirname(indexFile), firstAdrPath));
776
- updateIndexFile(indexFile, {
777
- relLink,
778
- title: args.firstTitle,
779
- status: args.firstStatus,
780
- date: today,
781
- });
782
-
783
- if (args.json) {
784
- const payload = {
785
- repoRoot,
786
- adrDir,
787
- adrDirRelPath: toPosix(path.relative(repoRoot, adrDir)),
788
- indexPath: indexFile,
789
- indexRelPath: toPosix(relIndex),
790
- indexExistedBefore,
791
- indexWritten,
792
- firstAdr: {
793
- createdAdrPath: firstAdrPath,
794
- createdAdrRelPath: toPosix(path.relative(repoRoot, firstAdrPath)),
795
- title: args.firstTitle,
796
- status: args.firstStatus,
797
- strategy,
798
- date: today,
799
- },
800
- date: today,
801
- };
802
- process.stdout.write(\`\${JSON.stringify(payload)}\\n\`);
582
+ function main(){
583
+ const args=parseArgs(process.argv);
584
+ const repoRoot=path.resolve(process.cwd(),args.repoRoot);
585
+ if(!fs.existsSync(repoRoot)) die(\`Repo root does not exist: \${repoRoot}\`);
586
+ const adrDir=path.resolve(repoRoot,args.dir);
587
+ fs.mkdirSync(adrDir,{recursive:true});
588
+ const indexFile=args.indexFile?(path.isAbsolute(args.indexFile)?args.indexFile:path.resolve(repoRoot,args.indexFile)):path.join(adrDir,'README.md');
589
+ const indexExistedBefore=fs.existsSync(indexFile);
590
+ writeIndex(indexFile,args.dir,{force:args.forceIndex});
591
+ const indexWritten=fs.existsSync(indexFile)&&(!indexExistedBefore||args.forceIndex);
592
+ const relIndex=path.isAbsolute(indexFile)?path.relative(repoRoot,indexFile):indexFile;
593
+ const date=new Date().toISOString().slice(0,10);
594
+ const strategy=args.strategy==='auto'?'date':args.strategy;
595
+ const fileName=strategy==='date'?\`\${date}-\${slugify(args.firstTitle)}.md\`:\`\${slugify(args.firstTitle)}.md\`;
596
+ const firstAdrPath=path.join(adrDir,fileName);
597
+ fs.writeFileSync(firstAdrPath,\`\${generateFirstAdr({title:args.firstTitle,status:args.firstStatus,date,deciders:args.deciders,adrDir:args.dir}).trimEnd()}\\n\`,'utf8');
598
+ updateIndexFile(indexFile,{relLink:toPosix(path.relative(path.dirname(indexFile),firstAdrPath)),title:args.firstTitle,status:args.firstStatus,date});
599
+ if(args.json){
600
+ process.stdout.write(\`\${JSON.stringify({repoRoot,adrDir,adrDirRelPath:toPosix(path.relative(repoRoot,adrDir)),indexPath:indexFile,indexRelPath:toPosix(relIndex),indexExistedBefore,indexWritten,firstAdr:{createdAdrPath:firstAdrPath,createdAdrRelPath:toPosix(path.relative(repoRoot,firstAdrPath)),title:args.firstTitle,status:args.firstStatus,strategy,date},date})}\\n\`);
803
601
  return;
804
602
  }
805
-
806
603
  process.stdout.write(\`\${firstAdrPath}\\n\`);
807
- process.stdout.write(\`Bootstrapped ADRs at \${adrDir} (\${today})\\n\`);
604
+ process.stdout.write(\`Bootstrapped ADRs at \${adrDir} (\${date})\\n\`);
808
605
  process.stdout.write(\`Index: \${indexFile}\\n\`);
809
606
  }
810
607
 
811
608
  main();
812
609
  `},{file:`scripts/new_adr.js`,content:`#!/usr/bin/env node
813
- /**
814
- * Create a new ADR markdown file using repo conventions and a template.
815
- *
816
- * Design goals:
817
- * - Safe defaults (auto-detect adr directory + numbering)
818
- * - No external deps
819
- * - Works even if the repo has no ADRs yet
820
- */
821
-
822
- const fs = require('node:fs');
823
- const path = require('node:path');
824
-
825
- function die(msg) {
826
- process.stderr.write(\`\${msg}\\n\`);
827
- process.exit(1);
828
- }
829
-
830
- function slugify(text) {
831
- const t = String(text || '')
832
- .trim()
833
- .toLowerCase();
834
- const noQuotes = t.replace(/['"\`]/g, '');
835
- const dashed = noQuotes.replace(/[^a-z0-9]+/g, '-').replace(/-{2,}/g, '-');
836
- const trimmed = dashed.replace(/^-+/, '').replace(/-+$/, '');
837
- return trimmed || 'decision';
838
- }
839
-
840
- function toPosix(p) {
841
- return p.split(path.sep).join('/');
610
+ const fs=require('node:fs');
611
+ const path=require('node:path');
612
+
613
+ function die(msg){process.stderr.write(\`\${msg}\\n\`);process.exit(1);}
614
+ function toPosix(value){return value.split(path.sep).join('/');}
615
+ function slugify(text){
616
+ const value=String(text||'').trim().toLowerCase();
617
+ return value.replace(/['"\`]/g,'').replace(/[^a-z0-9]+/g,'-').replace(/-{2,}/g,'-').replace(/^-+|-+$/g,'')||'decision';
842
618
  }
843
-
844
- function parseArgs(argv) {
845
- const out = {
846
- repoRoot: '.',
847
- dir: null,
848
- noCreateDir: false,
849
- title: null,
850
- status: 'proposed',
851
- template: 'simple', // simple | madr
852
- strategy: 'auto', // auto | date | slug
853
- deciders: '',
854
- consulted: '',
855
- informed: '',
856
- technicalStory: '',
857
- chosenOption: '',
858
- updateIndex: false,
859
- indexFile: null,
860
- json: false,
861
- };
862
-
863
- for (let i = 2; i < argv.length; i++) {
864
- const a = argv[i];
865
- const next = () => {
866
- if (i + 1 >= argv.length) die(\`Missing value for \${a}\`);
867
- return argv[++i];
868
- };
869
-
870
- if (a === '--repo-root') out.repoRoot = next();
871
- else if (a === '--dir') out.dir = next();
872
- else if (a === '--no-create-dir') out.noCreateDir = true;
873
- else if (a === '--title') out.title = next();
874
- else if (a === '--status') out.status = next();
875
- else if (a === '--template') out.template = next();
876
- else if (a === '--strategy') out.strategy = next();
877
- else if (a === '--deciders') out.deciders = next();
878
- else if (a === '--consulted') out.consulted = next();
879
- else if (a === '--informed') out.informed = next();
880
- else if (a === '--technical-story') out.technicalStory = next();
881
- else if (a === '--chosen-option') out.chosenOption = next();
882
- else if (a === '--update-index') out.updateIndex = true;
883
- else if (a === '--index-file') out.indexFile = next();
884
- else if (a === '--json') out.json = true;
885
- else if (a === '--help' || a === '-h') {
886
- process.stdout.write(
887
- [
888
- 'Usage: node new_adr.js --title "Choose database" [options]',
889
- '',
890
- 'Options:',
891
- ' --repo-root <path> Repo root (default: .)',
892
- ' --dir <path> ADR directory (default: auto-detect, else adr/)',
893
- ' --no-create-dir Do not create ADR directory if missing',
894
- ' --status <value> ADR status (default: proposed)',
895
- ' --template simple|madr Template (default: simple)',
896
- ' --strategy auto|date|slug Filename strategy (default: auto)',
897
- ' --deciders "a,b" Deciders list',
898
- ' --consulted "a,b" Consulted experts (RACI)',
899
- ' --informed "a,b" Informed stakeholders (RACI)',
900
- ' --technical-story <x> Issue/ticket/PR link or short ref',
901
- ' --chosen-option <x> MADR template: chosen option label',
902
- ' --update-index Update adr/README.md (or existing index)',
903
- ' --index-file <path> Override index file (relative to repo root unless absolute)',
904
- ' --json Output machine-readable JSON (default: off)',
905
- '',
906
- ].join('\\n'),
907
- );
619
+ function parseArgs(argv){
620
+ const out={repoRoot:'.',dir:null,noCreateDir:false,title:null,status:'proposed',template:'simple',strategy:'auto',deciders:'',consulted:'',informed:'',technicalStory:'',chosenOption:'',updateIndex:false,indexFile:null,json:false};
621
+ for(let index=2;index<argv.length;index++){
622
+ const arg=argv[index];
623
+ const next=()=>{if(index+1>=argv.length) die(\`Missing value for \${arg}\`);return argv[++index];};
624
+ if(arg==='--repo-root') out.repoRoot=next();
625
+ else if(arg==='--dir') out.dir=next();
626
+ else if(arg==='--no-create-dir') out.noCreateDir=true;
627
+ else if(arg==='--title') out.title=next();
628
+ else if(arg==='--status') out.status=next();
629
+ else if(arg==='--template') out.template=next();
630
+ else if(arg==='--strategy') out.strategy=next();
631
+ else if(arg==='--deciders') out.deciders=next();
632
+ else if(arg==='--consulted') out.consulted=next();
633
+ else if(arg==='--informed') out.informed=next();
634
+ else if(arg==='--technical-story') out.technicalStory=next();
635
+ else if(arg==='--chosen-option') out.chosenOption=next();
636
+ else if(arg==='--update-index') out.updateIndex=true;
637
+ else if(arg==='--index-file') out.indexFile=next();
638
+ else if(arg==='--json') out.json=true;
639
+ else if(arg==='--help'||arg==='-h'){
640
+ process.stdout.write([
641
+ 'Usage: node new_adr.js --title "Choose database" [options]',
642
+ '',
643
+ 'Options:',
644
+ ' --repo-root <path> Repo root (default: .)',
645
+ ' --dir <path> ADR dir (default: auto-detect, else adr/)',
646
+ ' --no-create-dir Do not create ADR dir',
647
+ ' --status <value> ADR status (default: proposed)',
648
+ ' --template simple|madr Template (default: simple)',
649
+ ' --strategy auto|date|slug Filename strategy (default: auto)',
650
+ ' --deciders "a,b" Deciders list',
651
+ ' --consulted "a,b" Consulted list (RACI)',
652
+ ' --informed "a,b" Informed list (RACI)',
653
+ ' --technical-story <x> Issue/ticket/PR ref',
654
+ ' --chosen-option <x> MADR chosen option label',
655
+ ' --update-index Update adr/README.md (or existing index)',
656
+ ' --index-file <path> Index file override',
657
+ ' --json Emit JSON',
658
+ '',
659
+ ].join('\\n'));
908
660
  process.exit(0);
909
- } else {
910
- die(\`Unknown arg: \${a}\`);
911
- }
661
+ }else die(\`Unknown arg: \${arg}\`);
912
662
  }
913
-
914
- if (!out.title) die('Missing required --title');
915
-
916
- if (!['simple', 'madr'].includes(out.template)) die(\`Invalid --template: \${out.template}\`);
917
- if (!['auto', 'date', 'slug'].includes(out.strategy)) die(\`Invalid --strategy: \${out.strategy}\`);
918
-
663
+ if(!out.title) die('Missing required --title');
664
+ if(!['simple','madr'].includes(out.template)) die(\`Invalid --template: \${out.template}\`);
665
+ if(!['auto','date','slug'].includes(out.strategy)) die(\`Invalid --strategy: \${out.strategy}\`);
919
666
  return out;
920
667
  }
921
-
922
- function detectAdrDir(repoRoot) {
923
- const candidates = [
924
- path.join(repoRoot, 'contributing', 'decisions'),
925
- path.join(repoRoot, 'docs', 'decisions'),
926
- path.join(repoRoot, 'adr'),
927
- path.join(repoRoot, 'docs', 'adr'),
928
- path.join(repoRoot, 'docs', 'adrs'),
929
- path.join(repoRoot, 'decisions'),
930
- ];
931
- for (const p of candidates) {
932
- try {
933
- if (fs.statSync(p).isDirectory()) return p;
934
- } catch {
935
- // ignore
936
- }
668
+ function detectAdrDir(repoRoot){
669
+ for(const candidate of [path.join(repoRoot,'contributing','decisions'),path.join(repoRoot,'docs','decisions'),path.join(repoRoot,'adr'),path.join(repoRoot,'docs','adr'),path.join(repoRoot,'docs','adrs'),path.join(repoRoot,'decisions')]){
670
+ try{if(fs.statSync(candidate).isDirectory()) return candidate;}catch{}
937
671
  }
938
672
  return null;
939
673
  }
940
-
941
- function listMdFiles(dir) {
942
- let entries = [];
943
- try {
944
- entries = fs.readdirSync(dir, { withFileTypes: true });
945
- } catch {
946
- return [];
947
- }
948
- return entries
949
- .filter((e) => e.isFile() && e.name.toLowerCase().endsWith('.md'))
950
- .map((e) => e.name);
674
+ function listMdFiles(dir){
675
+ try{return fs.readdirSync(dir,{withFileTypes:true}).filter((entry)=>entry.isFile()&&entry.name.toLowerCase().endsWith('.md')).map((entry)=>entry.name);}catch{return [];}
951
676
  }
952
-
953
- function detectStrategy(adrDir) {
954
- const md = listMdFiles(adrDir);
955
- for (const name of md) {
956
- if (/^\\d{4}-\\d{2}-\\d{2}-/.test(name)) return 'date';
957
- }
958
- if (md.length > 0) return 'slug';
959
- return 'date';
677
+ function detectStrategy(adrDir){
678
+ const files=listMdFiles(adrDir);
679
+ if(files.some((name)=>/^\\d{4}-\\d{2}-\\d{2}-/.test(name))) return 'date';
680
+ return files.length?'slug':'date';
960
681
  }
961
-
962
- function todayISO() {
963
- return new Date().toISOString().slice(0, 10);
682
+ function todayISO(){return new Date().toISOString().slice(0,10);}
683
+ function loadTemplate(name){
684
+ const templatePath=path.join(path.resolve(__dirname,'..'),'assets','templates',\`adr-\${name}.md\`);
685
+ if(!fs.existsSync(templatePath)) die(\`Template not found: \${templatePath}\`);
686
+ return fs.readFileSync(templatePath,'utf8');
964
687
  }
965
-
966
- function loadTemplate(templateName) {
967
- const skillRoot = path.resolve(__dirname, '..');
968
- const templatePath = path.join(skillRoot, 'assets', 'templates', \`adr-\${templateName}.md\`);
969
- if (!fs.existsSync(templatePath)) die(\`Template not found: \${templatePath}\`);
970
- return fs.readFileSync(templatePath, 'utf8');
688
+ function renderTemplate(raw,vars){
689
+ let out=raw;
690
+ out=out.replace(/^(status:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.status}\`);
691
+ out=out.replace(/^(date:\\s*)\\{[^}]*\\}\\s*$/m,\`$1\${vars.date}\`);
692
+ out=out.replace(/^(decision-makers:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.deciders||''}\`);
693
+ out=vars.consulted?out.replace(/^(consulted:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.consulted}\`):out.replace(/^consulted:\\s*["']?\\{[^}]*\\}["']?\\s*\\n/m,'');
694
+ out=vars.informed?out.replace(/^(informed:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.informed}\`):out.replace(/^informed:\\s*["']?\\{[^}]*\\}["']?\\s*\\n/m,'');
695
+ return out
696
+ .replace(/^(#\\s+)\\{short title[^}]*\\}\\s*$/m,\`$1\${vars.title}\`)
697
+ .replaceAll('{TITLE}',vars.title)
698
+ .replaceAll('{STATUS}',vars.status)
699
+ .replaceAll('{DATE}',vars.date)
700
+ .replaceAll('{DECIDERS}',vars.deciders)
701
+ .replaceAll('{TECHNICAL_STORY}',vars.technicalStory)
702
+ .replaceAll('{CHOSEN_OPTION}',vars.chosenOption);
971
703
  }
972
-
973
- function renderTemplate(raw, vars) {
974
- // Handle YAML front matter placeholders (quoted and unquoted)
975
- let out = raw;
976
-
977
- // YAML front matter fields — replace the whole placeholder pattern
978
- // e.g. status: "{proposed | accepted | ...}" → status: proposed
979
- out = out.replace(/^(status:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m, \`$1\${vars.status}\`);
980
- out = out.replace(/^(date:\\s*)\\{[^}]*\\}\\s*$/m, \`$1\${vars.date}\`);
981
- out = out.replace(/^(decision-makers:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m, \`$1\${vars.deciders || ''}\`);
982
-
983
- // consulted / informed: replace if a value was provided, otherwise remove the
984
- // entire line so we don't leak placeholder text like "{list everyone...}"
985
- if (vars.consulted) {
986
- out = out.replace(/^(consulted:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m, \`$1\${vars.consulted}\`);
987
- } else {
988
- out = out.replace(/^consulted:\\s*["']?\\{[^}]*\\}["']?\\s*\\n/m, '');
989
- }
990
- if (vars.informed) {
991
- out = out.replace(/^(informed:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m, \`$1\${vars.informed}\`);
992
- } else {
993
- out = out.replace(/^informed:\\s*["']?\\{[^}]*\\}["']?\\s*\\n/m, '');
994
- }
995
-
996
- // Replace MADR-style heading placeholder
997
- out = out.replace(/^(#\\s+)\\{short title[^}]*\\}\\s*$/m, \`$1\${vars.title}\`);
998
-
999
- // Inline placeholders (title in heading, etc.)
1000
- out = out
1001
- .replaceAll('{TITLE}', vars.title)
1002
- .replaceAll('{STATUS}', vars.status)
1003
- .replaceAll('{DATE}', vars.date)
1004
- .replaceAll('{DECIDERS}', vars.deciders)
1005
- .replaceAll('{TECHNICAL_STORY}', vars.technicalStory)
1006
- .replaceAll('{CHOSEN_OPTION}', vars.chosenOption);
1007
-
1008
- return out;
704
+ function chooseIndexFile(adrDir){
705
+ for(const name of ['README.md','index.md']){const candidate=path.join(adrDir,name);if(fs.existsSync(candidate)) return candidate;}
706
+ return path.join(adrDir,'README.md');
1009
707
  }
1010
-
1011
- function chooseIndexFile(adrDir) {
1012
- for (const name of ['README.md', 'index.md']) {
1013
- const p = path.join(adrDir, name);
1014
- if (fs.existsSync(p)) return p;
1015
- }
1016
- return path.join(adrDir, 'README.md');
708
+ function insertIndexEntryUnderHeading(lines,headingRegex,entryLine){
709
+ const headingIndex=lines.findIndex((line)=>headingRegex.test(line));
710
+ if(headingIndex===-1) return {lines,inserted:false};
711
+ let sectionEnd=lines.length;
712
+ for(let index=headingIndex+1;index<lines.length;index++){if(/^##\\s+/.test(lines[index])){sectionEnd=index;break;}}
713
+ let insertAt=sectionEnd;
714
+ for(let index=sectionEnd-1;index>headingIndex;index--){if(/^[-*]\\s+/.test(lines[index])){insertAt=index+1;break;}}
715
+ const out=[...lines];
716
+ if(insertAt===headingIndex+1&&out[insertAt]!=='') out.splice(insertAt,0,'');
717
+ out.splice(insertAt,0,entryLine);
718
+ return {lines:out,inserted:true};
1017
719
  }
1018
-
1019
- function insertIndexEntryUnderHeading(lines, headingRegex, entryLine) {
1020
- // Returns { lines, inserted }
1021
- const headingIndex = lines.findIndex((l) => headingRegex.test(l));
1022
- if (headingIndex === -1) return { lines, inserted: false };
1023
-
1024
- let sectionEnd = lines.length;
1025
- for (let i = headingIndex + 1; i < lines.length; i++) {
1026
- if (/^##\\s+/.test(lines[i])) {
1027
- sectionEnd = i;
1028
- break;
1029
- }
1030
- }
1031
-
1032
- // Prefer inserting at end of list in this section if there is a list.
1033
- let lastListItem = -1;
1034
- for (let i = sectionEnd - 1; i > headingIndex; i--) {
1035
- if (/^[-*]\\s+/.test(lines[i])) {
1036
- lastListItem = i;
1037
- break;
1038
- }
1039
- }
1040
-
1041
- const insertAt = lastListItem !== -1 ? lastListItem + 1 : sectionEnd;
1042
-
1043
- const out = [...lines];
1044
-
1045
- // Ensure there's a blank line after the heading if we're inserting immediately after it.
1046
- if (insertAt === headingIndex + 1 && out[insertAt] !== '') {
1047
- out.splice(insertAt, 0, '');
1048
- }
1049
-
1050
- out.splice(insertAt, 0, entryLine);
1051
- return { lines: out, inserted: true };
1052
- }
1053
-
1054
- function updateIndex(indexFile, { relLink, title, status, date }) {
1055
- let content = '';
1056
- if (fs.existsSync(indexFile)) content = fs.readFileSync(indexFile, 'utf8');
1057
- else content = '# ADR Log\\n\\n';
1058
-
1059
- if (content.includes(relLink)) return false;
1060
-
1061
- const normalized = content.replace(/\\r\\n/g, '\\n');
1062
- const hadTrailingNewline = normalized.endsWith('\\n');
1063
- let lines = normalized.split('\\n');
1064
- // Normalize away the trailing empty split element so insertion math is sane.
1065
- if (hadTrailingNewline && lines.length > 0 && lines[lines.length - 1] === '') {
1066
- lines = lines.slice(0, -1);
1067
- }
1068
- const entryLine = \`- [\${title}](\${relLink}) (\${status}, \${date})\`;
1069
-
1070
- // Prefer inserting under "## ADRs" if it exists, otherwise append at EOF.
1071
- const r = insertIndexEntryUnderHeading(lines, /^##\\s+ADRs\\s*$/i, entryLine);
1072
- const nextLines = r.inserted ? r.lines : [...lines, entryLine];
1073
-
1074
- let next = nextLines.join('\\n');
1075
- if (hadTrailingNewline) next += '\\n';
1076
-
1077
- fs.mkdirSync(path.dirname(indexFile), { recursive: true });
1078
- fs.writeFileSync(indexFile, next, 'utf8');
720
+ function updateIndex(indexFile,{relLink,title,status,date}){
721
+ const source=fs.existsSync(indexFile)?fs.readFileSync(indexFile,'utf8'):'# ADR Log\\n\\n';
722
+ if(source.includes(relLink)) return false;
723
+ const normalized=source.replace(/\\r\\n/g,'\\n');
724
+ const hadTrailingNewline=normalized.endsWith('\\n');
725
+ const lines=normalized.split('\\n');
726
+ if(hadTrailingNewline&&lines[lines.length-1]==='') lines.pop();
727
+ const entryLine=\`- [\${title}](\${relLink}) (\${status}, \${date})\`;
728
+ const result=insertIndexEntryUnderHeading(lines,/^##\\s+ADRs\\s*$/i,entryLine);
729
+ let next=(result.inserted?result.lines:[...lines,entryLine]).join('\\n');
730
+ if(hadTrailingNewline) next+='\\n';
731
+ fs.mkdirSync(path.dirname(indexFile),{recursive:true});
732
+ fs.writeFileSync(indexFile,next,'utf8');
1079
733
  return true;
1080
734
  }
1081
-
1082
- function main() {
1083
- const args = parseArgs(process.argv);
1084
-
1085
- const repoRoot = path.resolve(process.cwd(), args.repoRoot);
1086
- if (!fs.existsSync(repoRoot)) die(\`Repo root does not exist: \${repoRoot}\`);
1087
-
1088
- let adrDir;
1089
- if (args.dir) adrDir = path.resolve(repoRoot, args.dir);
1090
- else adrDir = detectAdrDir(repoRoot) || path.join(repoRoot, 'adr');
1091
-
1092
- if (!fs.existsSync(adrDir)) {
1093
- if (args.noCreateDir) die(\`ADR directory does not exist: \${adrDir}\`);
1094
- fs.mkdirSync(adrDir, { recursive: true });
735
+ function main(){
736
+ const args=parseArgs(process.argv);
737
+ const repoRoot=path.resolve(process.cwd(),args.repoRoot);
738
+ if(!fs.existsSync(repoRoot)) die(\`Repo root does not exist: \${repoRoot}\`);
739
+ const adrDir=args.dir?path.resolve(repoRoot,args.dir):detectAdrDir(repoRoot)||path.join(repoRoot,'adr');
740
+ if(!fs.existsSync(adrDir)){if(args.noCreateDir) die(\`ADR directory does not exist: \${adrDir}\`);fs.mkdirSync(adrDir,{recursive:true});}
741
+ const strategy=args.strategy==='auto'?detectStrategy(adrDir):args.strategy;
742
+ const title=String(args.title).trim();
743
+ const slug=slugify(title);
744
+ const date=todayISO();
745
+ let out=path.join(adrDir,strategy==='date'?\`\${date}-\${slug}.md\`:\`\${slug}.md\`);
746
+ if(fs.existsSync(out)){
747
+ if(strategy==='date') die(\`ADR already exists: \${out}\`);
748
+ let suffix=2;
749
+ while(true){const candidate=path.join(adrDir,\`\${slug}-\${suffix}.md\`);if(!fs.existsSync(candidate)){out=candidate;break;}suffix++;}
1095
750
  }
1096
-
1097
- let strategy = args.strategy;
1098
- if (strategy === 'auto') strategy = detectStrategy(adrDir);
1099
-
1100
- const title = String(args.title).trim();
1101
- const slug = slugify(title);
1102
-
1103
- const today = todayISO();
1104
-
1105
- let filename;
1106
- if (strategy === 'date') {
1107
- filename = \`\${today}-\${slug}.md\`;
1108
- } else {
1109
- filename = \`\${slug}.md\`;
1110
- }
1111
-
1112
- let out = path.join(adrDir, filename);
1113
- if (fs.existsSync(out)) {
1114
- if (strategy === 'date') die(\`ADR already exists: \${out}\`);
1115
- let i = 2;
1116
- while (true) {
1117
- const candidate = path.join(adrDir, \`\${slug}-\${i}.md\`);
1118
- if (!fs.existsSync(candidate)) {
1119
- out = candidate;
1120
- break;
1121
- }
1122
- i++;
1123
- }
1124
- }
1125
-
1126
- const deciders = String(args.deciders || '')
1127
- .split(',')
1128
- .map((s) => s.trim())
1129
- .filter(Boolean)
1130
- .join(', ');
1131
-
1132
- const consulted = String(args.consulted || '')
1133
- .split(',')
1134
- .map((s) => s.trim())
1135
- .filter(Boolean)
1136
- .join(', ');
1137
- const informed = String(args.informed || '')
1138
- .split(',')
1139
- .map((s) => s.trim())
1140
- .filter(Boolean)
1141
- .join(', ');
1142
-
1143
- const raw = loadTemplate(args.template);
1144
- const rendered = renderTemplate(raw, {
1145
- title,
1146
- status: String(args.status).trim(),
1147
- date: today,
1148
- deciders,
1149
- consulted,
1150
- informed,
1151
- technicalStory: String(args.technicalStory || '').trim(),
1152
- chosenOption: String(args.chosenOption || '').trim(),
1153
- });
1154
-
1155
- fs.writeFileSync(out, \`\${rendered.trimEnd()}\\n\`, 'utf8');
1156
-
1157
- let updatedIndexPath = null;
1158
- let indexChanged = false;
1159
-
1160
- if (args.updateIndex) {
1161
- let indexFile;
1162
- if (args.indexFile) {
1163
- indexFile = path.isAbsolute(args.indexFile)
1164
- ? args.indexFile
1165
- : path.resolve(repoRoot, args.indexFile);
1166
- } else {
1167
- indexFile = chooseIndexFile(adrDir);
1168
- }
1169
-
1170
- const relLink = toPosix(path.relative(path.dirname(indexFile), out));
1171
- indexChanged = updateIndex(indexFile, {
1172
- relLink,
1173
- title,
1174
- status: String(args.status).trim(),
1175
- date: today,
1176
- });
1177
- updatedIndexPath = indexFile;
1178
- }
1179
-
1180
- if (args.json) {
1181
- const payload = {
1182
- repoRoot,
1183
- adrDir,
1184
- createdAdrPath: out,
1185
- createdAdrRelPath: toPosix(path.relative(repoRoot, out)),
1186
- title,
1187
- status: String(args.status).trim(),
1188
- template: args.template,
1189
- strategy,
1190
- date: today,
1191
- indexUpdated: Boolean(updatedIndexPath),
1192
- indexChanged,
1193
- indexPath: updatedIndexPath,
1194
- indexRelPath: updatedIndexPath ? toPosix(path.relative(repoRoot, updatedIndexPath)) : null,
1195
- };
1196
- process.stdout.write(\`\${JSON.stringify(payload)}\\n\`);
1197
- } else {
1198
- process.stdout.write(\`\${out}\\n\`);
751
+ const deciders=String(args.deciders||'').split(',').map((value)=>value.trim()).filter(Boolean).join(', ');
752
+ const consulted=String(args.consulted||'').split(',').map((value)=>value.trim()).filter(Boolean).join(', ');
753
+ const informed=String(args.informed||'').split(',').map((value)=>value.trim()).filter(Boolean).join(', ');
754
+ const rendered=renderTemplate(loadTemplate(args.template),{title,status:String(args.status).trim(),date,deciders,consulted,informed,technicalStory:String(args.technicalStory||'').trim(),chosenOption:String(args.chosenOption||'').trim()});
755
+ fs.writeFileSync(out,\`\${rendered.trimEnd()}\\n\`,'utf8');
756
+ let updatedIndexPath=null;
757
+ let indexChanged=false;
758
+ if(args.updateIndex){
759
+ const indexFile=args.indexFile?(path.isAbsolute(args.indexFile)?args.indexFile:path.resolve(repoRoot,args.indexFile)):chooseIndexFile(adrDir);
760
+ indexChanged=updateIndex(indexFile,{relLink:toPosix(path.relative(path.dirname(indexFile),out)),title,status:String(args.status).trim(),date});
761
+ updatedIndexPath=indexFile;
1199
762
  }
763
+ if(args.json) process.stdout.write(\`\${JSON.stringify({repoRoot,adrDir,createdAdrPath:out,createdAdrRelPath:toPosix(path.relative(repoRoot,out)),title,status:String(args.status).trim(),template:args.template,strategy,date,indexUpdated:Boolean(updatedIndexPath),indexChanged,indexPath:updatedIndexPath,indexRelPath:updatedIndexPath?toPosix(path.relative(repoRoot,updatedIndexPath)):null})}\\n\`);
764
+ else process.stdout.write(\`\${out}\\n\`);
1200
765
  }
1201
766
 
1202
767
  main();
1203
768
  `},{file:`scripts/set_adr_status.js`,content:`#!/usr/bin/env node
1204
- /**
1205
- * Update an ADR's status in-place.
1206
- *
1207
- * Supported patterns:
1208
- * - Bullet status: "- Status: proposed" or "* Status: proposed"
1209
- * - Nygard-style section: "## Status" followed by a single-line status value
1210
- */
1211
-
1212
- const fs = require('node:fs');
1213
- const path = require('node:path');
1214
-
1215
- function die(msg) {
1216
- process.stderr.write(\`\${msg}\\n\`);
1217
- process.exit(1);
1218
- }
1219
-
1220
- function toPosix(p) {
1221
- return p.split(path.sep).join('/');
1222
- }
1223
-
1224
- function parseArgs(argv) {
1225
- if (argv.includes('--help') || argv.includes('-h')) {
1226
- process.stdout.write(
1227
- [
1228
- 'Usage: node set_adr_status.js <path> --status <value> [--json]',
1229
- '',
1230
- 'Example:',
1231
- ' node set_adr_status.js adr/2025-06-15-foo.md --status accepted',
1232
- '',
1233
- ].join('\\n'),
1234
- );
769
+ const fs=require('node:fs');
770
+ const path=require('node:path');
771
+
772
+ function die(msg){process.stderr.write(\`\${msg}\\n\`);process.exit(1);}
773
+ function toPosix(value){return value.split(path.sep).join('/');}
774
+ function parseArgs(argv){
775
+ if(argv.includes('--help')||argv.includes('-h')){
776
+ process.stdout.write([
777
+ 'Usage: node set_adr_status.js <path> --status <value> [--json]',
778
+ '',
779
+ 'Example:',
780
+ ' node set_adr_status.js adr/2025-06-15-foo.md --status accepted',
781
+ '',
782
+ ].join('\\n'));
1235
783
  process.exit(0);
1236
784
  }
1237
-
1238
- if (argv.length < 3) die('Missing <path>');
1239
- const file = argv[2];
1240
-
1241
- let status = null;
1242
- let json = false;
1243
- for (let i = 3; i < argv.length; i++) {
1244
- const a = argv[i];
1245
- if (a === '--status') {
1246
- if (i + 1 >= argv.length) die('Missing value for --status');
1247
- status = argv[++i];
1248
- } else if (a === '--json') {
1249
- json = true;
1250
- } else {
1251
- die(\`Unknown arg: \${a}\`);
1252
- }
785
+ if(argv.length<3) die('Missing <path>');
786
+ let status=null;
787
+ let json=false;
788
+ for(let index=3;index<argv.length;index++){
789
+ const arg=argv[index];
790
+ if(arg==='--status'){
791
+ if(index+1>=argv.length) die('Missing value for --status');
792
+ status=argv[++index];
793
+ }else if(arg==='--json') json=true;
794
+ else die(\`Unknown arg: \${arg}\`);
1253
795
  }
1254
- if (!status) die('Missing required --status');
1255
- return { file, status: String(status).trim(), json };
796
+ if(!status) die('Missing required --status');
797
+ return {file:argv[2],status:String(status).trim(),json};
1256
798
  }
1257
-
1258
- function setYamlFrontMatterStatus(lines, newStatus) {
1259
- // YAML front matter: starts with '---', ends with next '---'
1260
- if (lines.length < 2 || lines[0].trim() !== '---') return { lines, changed: false };
1261
-
1262
- let changed = false;
1263
- const out = [];
1264
- let inFrontMatter = true;
1265
- let passedOpening = false;
1266
-
1267
- for (let i = 0; i < lines.length; i++) {
1268
- const line = lines[i];
1269
-
1270
- if (i === 0 && line.trim() === '---') {
1271
- passedOpening = true;
1272
- out.push(line);
1273
- continue;
1274
- }
1275
-
1276
- if (passedOpening && inFrontMatter && line.trim() === '---') {
1277
- inFrontMatter = false;
1278
- out.push(line);
1279
- continue;
1280
- }
1281
-
1282
- if (passedOpening && inFrontMatter && /^status\\s*:/.test(line)) {
1283
- out.push(\`status: \${newStatus}\`);
1284
- changed = true;
1285
- continue;
1286
- }
1287
-
799
+ function setYaml(lines,status){
800
+ if(lines.length<2||lines[0].trim()!=='---') return {lines,changed:false};
801
+ const out=[];
802
+ let inFrontMatter=true;
803
+ let passedOpening=false;
804
+ let changed=false;
805
+ for(let index=0;index<lines.length;index++){
806
+ const line=lines[index];
807
+ if(index===0&&line.trim()==='---'){passedOpening=true;out.push(line);continue;}
808
+ if(passedOpening&&inFrontMatter&&line.trim()==='---'){inFrontMatter=false;out.push(line);continue;}
809
+ if(passedOpening&&inFrontMatter&&/^status\\s*:/.test(line)){out.push(\`status: \${status}\`);changed=true;continue;}
1288
810
  out.push(line);
1289
811
  }
1290
-
1291
- return { lines: out, changed };
812
+ return {lines:out,changed};
1292
813
  }
1293
-
1294
- function setBulletStatus(lines, newStatus) {
1295
- let changed = false;
1296
- const out = lines.map((line) => {
1297
- const m = line.match(/^([*-])\\s*Status:\\s*(.*)$/);
1298
- if (!m) return line;
1299
- changed = true;
1300
- return \`\${m[1]} Status: \${newStatus}\`;
1301
- });
1302
- return { lines: out, changed };
814
+ function setBullet(lines,status){
815
+ let changed=false;
816
+ return {
817
+ changed,
818
+ lines:lines.map((line)=>{
819
+ const match=line.match(/^([*-])\\s*Status:\\s*(.*)$/);
820
+ if(!match) return line;
821
+ changed=true;
822
+ return \`\${match[1]} Status: \${status}\`;
823
+ }),
824
+ };
1303
825
  }
1304
-
1305
- function setSectionStatus(lines, newStatus) {
1306
- let changed = false;
1307
- const out = [];
1308
-
1309
- for (let i = 0; i < lines.length; i++) {
1310
- out.push(lines[i]);
1311
-
1312
- if (!/^##\\s+Status\\s*$/.test(lines[i])) continue;
1313
-
1314
- // Replace next non-empty, non-heading line. If not found, insert.
1315
- let j = i + 1;
1316
- while (j < lines.length && lines[j].trim() === '') {
1317
- out.push(lines[j]);
1318
- j++;
1319
- }
1320
-
1321
- if (j < lines.length && !/^##\\s+/.test(lines[j])) {
1322
- out.push(newStatus);
1323
- changed = true;
1324
- i = j; // skip original status line
1325
- continue;
1326
- }
1327
-
1328
- out.push(newStatus);
1329
- changed = true;
1330
- i = j - 1;
826
+ function setSection(lines,status){
827
+ const out=[];
828
+ let changed=false;
829
+ for(let index=0;index<lines.length;index++){
830
+ out.push(lines[index]);
831
+ if(!/^##\\s+Status\\s*$/.test(lines[index])) continue;
832
+ let next=index+1;
833
+ while(next<lines.length&&lines[next].trim()===''){out.push(lines[next]);next++;}
834
+ if(next<lines.length&&!/^##\\s+/.test(lines[next])){out.push(status);changed=true;index=next;continue;}
835
+ out.push(status);changed=true;index=next-1;
1331
836
  }
1332
-
1333
- return { lines: out, changed };
837
+ return {lines:out,changed};
1334
838
  }
1335
-
1336
- function main() {
1337
- const args = parseArgs(process.argv);
1338
- const filePath = path.resolve(process.cwd(), args.file);
1339
- if (!fs.existsSync(filePath)) die(\`File not found: \${filePath}\`);
1340
-
1341
- const content = fs.readFileSync(filePath, 'utf8');
1342
- const hadTrailingNewline = content.endsWith('\\n');
1343
- const lines = content.replace(/\\r\\n/g, '\\n').split('\\n');
1344
-
1345
- let r = setYamlFrontMatterStatus(lines, args.status);
1346
- if (!r.changed) r = setBulletStatus(lines, args.status);
1347
- if (!r.changed) r = setSectionStatus(lines, args.status);
1348
- if (!r.changed) {
1349
- die(
1350
- "Could not find a status to update. Expected YAML front matter 'status:', '- Status:'/'* Status:', or a '## Status' section.",
1351
- );
1352
- }
1353
-
1354
- const newContent = r.lines.join('\\n') + (hadTrailingNewline ? '\\n' : '');
1355
- fs.writeFileSync(filePath, newContent, 'utf8');
1356
-
1357
- if (args.json) {
1358
- process.stdout.write(
1359
- \`\${JSON.stringify({
1360
- filePath,
1361
- fileRelPath: toPosix(path.relative(process.cwd(), filePath)),
1362
- status: args.status,
1363
- changed: true,
1364
- })}\\n\`,
1365
- );
1366
- } else {
1367
- process.stdout.write(\`\${filePath}\\n\`);
1368
- }
839
+ function main(){
840
+ const args=parseArgs(process.argv);
841
+ const filePath=path.resolve(process.cwd(),args.file);
842
+ if(!fs.existsSync(filePath)) die(\`File not found: \${filePath}\`);
843
+ const content=fs.readFileSync(filePath,'utf8');
844
+ const hadTrailingNewline=content.endsWith('\\n');
845
+ const lines=content.replace(/\\r\\n/g,'\\n').split('\\n');
846
+ let result=setYaml(lines,args.status);
847
+ if(!result.changed) result=setBullet(lines,args.status);
848
+ if(!result.changed) result=setSection(lines,args.status);
849
+ if(!result.changed) die("Could not find a status to update. Expected YAML front matter 'status:', '- Status:'/'* Status:', or a '## Status' section.");
850
+ fs.writeFileSync(filePath,result.lines.join('\\n')+(hadTrailingNewline?'\\n':''),'utf8');
851
+ if(args.json) process.stdout.write(\`\${JSON.stringify({filePath,fileRelPath:toPosix(path.relative(process.cwd(),filePath)),status:args.status,changed:true})}\\n\`);
852
+ else process.stdout.write(\`\${filePath}\\n\`);
1369
853
  }
1370
854
 
1371
855
  main();
1372
856
  `},{file:`SKILL.md`,content:`---
1373
857
  name: adr-skill
1374
- description: Create and maintain Architecture Decision Records (ADRs) optimized for agentic coding workflows. Use when you need to propose, write, update, accept/reject, deprecate, or supersede an ADR; bootstrap an adr folder and index; consult existing ADRs before implementing changes; or enforce ADR conventions. Trigger on: architecture decisions, technology choices, significant design tradeoffs, decisions that affect >3 files or >1 team, irreversible choices. This skill uses Socratic questioning to capture intent before drafting, and validates output against an agent-readiness checklist.
858
+ description: Create and maintain Architecture Decision Records (ADRs) for agentic coding workflows. Use for proposing, drafting, updating, accepting/rejecting, deprecating, superseding, bootstrapping ADRs, consulting ADRs before impl, and enforcing ADR conventions. Trigger on architecture decisions, tech choices, major tradeoffs, decisions affecting >3 files or >1 team, and hard-to-reverse choices. Uses Socratic questioning plus an agent-readiness checklist.
1375
859
  metadata:
1376
860
  category: cross-cutting
1377
861
  domain: general
@@ -1386,159 +870,118 @@ metadata:
1386
870
 
1387
871
  ## Quick Reference
1388
872
 
1389
- **Purpose:** Create Architecture Decision Records optimized for agentic coding — decisions that agents can implement without follow-up questions.
873
+ **Purpose:** Create ADRs agents can implement from without follow-up.
1390
874
 
1391
- **Write an ADR when:** Decision changes how system is built, is hard to reverse, affects other people/agents, or has real alternatives.
875
+ **Write an ADR when:** decision changes system shape, is hard to reverse, affects other people/agents, or has real alternatives.
1392
876
 
1393
877
  **Four-Phase Workflow:**
1394
- 1. **Discover** — Socratic questioning to surface intent, constraints, and alternatives
1395
- 2. **Draft** — Fill template (Simple or MADR) with concrete, measurable constraints
1396
- 3. **Validate** — Agent-readiness checklist (score ≥ 80%)
1397
- 4. **Record** — Save to \`docs/decisions/\` or \`adr/\`, update index
878
+ 1. **Discover** — surface intent, constraints, alternatives
879
+ 2. **Draft** — fill Simple or MADR template with concrete constraints
880
+ 3. **Validate** — review for agent-readiness (target ≥ 80%)
881
+ 4. **Record** — save to \`docs/decisions/\` or \`adr/\`, update index
1398
882
 
1399
883
  **Two templates:**
1400
884
  - **Simple** (≤3 options, single-team) — Context → Decision → Consequences → Implementation Plan → Alternatives
1401
- - **MADR** (complex, multi-team) — Full template with Decision Drivers, Pros/Cons matrix, detailed Implementation Plan
885
+ - **MADR** (complex, multi-team) — adds drivers, options matrix, deeper plan
1402
886
 
1403
- **Commands:** Create → run 4-phase workflow and save to \`docs/decisions/NNNN-slug.md\`. Consult \`search({ query: "ADR <topic>" })\` before implementing. Update → edit content and YAML status. Deprecate → set \`status: deprecated\` and add a superseded-by link. Bootstrap → create \`docs/decisions/\` plus \`index.md\` if missing.
887
+ **Commands:** Consult existing ADRs before impl. Create/update with scripts below.
1404
888
 
1405
- **Agent-readiness gate:** Every ADR scores ≥ 80% on: Specificity, Testability, Completeness, Independence, Actionability.
889
+ **Agent-readiness gate:** target ≥ 80%.
1406
890
 
1407
891
  ## Mindset
1408
892
 
1409
- ADRs serve two audiences separated by time: the CURRENT team making the decision, and the FUTURE team wondering "why the hell did they do it this way?"
1410
-
1411
- The future audience is more important. They have zero context about your constraints, discussions, and tradeoffs. Write for them.
1412
-
1413
- An ADR is NOT meeting minutes. It captures:
1414
- - The FORCES that drove the decision (constraints, requirements, team skills)
1415
- - The ALTERNATIVES genuinely considered (with honest evaluation)
1416
- - The IRREVERSIBILITY assessment (one-way door vs two-way door)
1417
- - The CONSEQUENCES acknowledged upfront (technical debt accepted, capability traded)
893
+ Write for future readers with zero context. ADR = decision record, not meeting notes.
1418
894
 
1419
895
  ## Philosophy
1420
896
 
1421
- ADRs created with this skill are **executable specifications for coding agents**. A human approves the decision; an agent implements it. The ADR must contain everything the agent needs to write correct code without asking follow-up questions.
1422
-
1423
- - Constraints must be explicit and measurable, not vibes
1424
- - Decisions must be specific enough to act on ("use PostgreSQL 16 with pgvector" not "use a database")
1425
- - Consequences must map to concrete follow-up tasks
1426
- - Non-goals must be stated to prevent scope creep
1427
- - The ADR must be self-contained — no tribal knowledge assumptions
1428
- - **The ADR must include an implementation plan** — which files to touch, which patterns to follow, which tests to write, and how to verify the decision was implemented correctly
897
+ ADR must let an agent implement without follow-up: explicit constraints, concrete decision, follow-up tasks, non-goals, self-contained context, and an implementation plan.
1429
898
 
1430
899
  ## ADR Quality Criteria
1431
900
 
1432
901
  An ADR is agent-ready when it passes:
1433
- - [ ] **Context is self-contained** — a reader unfamiliar with the project can understand the problem
1434
- - [ ] **Options include at least 2 genuine alternatives** (no strawmen)
1435
- - [ ] **Decision rationale cites concrete evidence** — performance numbers, blast radius, team constraints
1436
- - [ ] **Consequences are bidirectional** — both benefits AND costs/risks
1437
- - [ ] **Status is explicit** — proposed/accepted/deprecated/superseded with date
1438
- - [ ] **Superseded ADRs link to their replacement** — no orphans
902
+ - [ ] **Context is self-contained**
903
+ - [ ] **Options include at least 2 genuine alternatives**
904
+ - [ ] **Decision rationale cites concrete evidence**
905
+ - [ ] **Consequences are bidirectional**
906
+ - [ ] **Status is explicit**
907
+ - [ ] **Superseded ADRs link to their replacement**
1439
908
 
1440
909
  ## When to Write an ADR
1441
910
 
1442
911
  Write an ADR when a decision:
1443
912
 
1444
- - **Changes how the system is built or operated** (new dependency, architecture pattern, infrastructure choice, API design)
913
+ - **Changes how system is built or operated** (new dep, pattern, infra choice, API design)
1445
914
  - **Is hard to reverse** once code is written against it
1446
- - **Affects other people or agents** who will work in this codebase later
915
+ - **Affects other people or agents** who will work in codebase later
1447
916
  - **Has real alternatives** that were considered and rejected
1448
917
 
1449
918
  ## NEVER
1450
919
 
1451
- - **NEVER write an ADR after implementation** unless documenting a discovery — ADRs capture the decision moment, not post-hoc rationalization. If the code already exists, you're writing documentation, not a decision record.
1452
- - **NEVER include only one option** — a "decision" with one choice isn't a decision. If there's genuinely only one approach, you don't need an ADR.
1453
- - **NEVER use vague consequences** like "improves maintainability" — cite specific metrics, affected files, or observable outcomes.
1454
- - **NEVER let ADRs accumulate without review** — stale "proposed" ADRs that languish >2 weeks should be accepted or withdrawn.
1455
- - **NEVER modify an accepted ADR** if the decision changes, SUPERSEDE with a new ADR that links back. History matters.
1456
- - **NEVER write an ADR for trivial decisions** — library version bumps, formatting changes, and routine maintenance don't warrant the ceremony. Reserve ADRs for decisions that are hard to reverse or have team-wide impact.
1457
- - **NEVER skip the "rejected alternatives" section** — documenting WHY you didn't pick option B is often more valuable than explaining why you picked A.
920
+ - **NEVER write ADR after impl** unless documenting discovery
921
+ - **NEVER include only one option**
922
+ - **NEVER use vague consequences**
923
+ - **NEVER let ADRs linger in stale proposed state**
924
+ - **NEVER modify an accepted ADR**; supersede instead
925
+ - **NEVER write ADR for trivial decisions**
926
+ - **NEVER skip rejected alternatives**
1458
927
 
1459
- When in doubt: if a future agent working in this codebase would benefit from knowing _why_ this choice was made, write the ADR.
1460
928
 
1461
- ### Proactive ADR Triggers (For Agents)
1462
929
 
1463
- If you are an agent coding in a repo and you encounter any of these situations, **stop and propose an ADR** before continuing:
930
+ ### Proactive ADR Triggers (For Agents)
1464
931
 
1465
- - You are about to introduce a new dependency that doesn't already exist in the project
1466
- - You are about to create a new architectural pattern (new way of handling errors, new data access layer, new API convention) that other code will need to follow
1467
- - You are about to make a choice between two or more real alternatives and the tradeoffs are non-obvious
1468
- - You are about to change something that contradicts an existing accepted ADR
1469
- - You realize you're writing a long code comment explaining "why" — that reasoning belongs in an ADR
932
+ Stop and propose an ADR when you hit a new dependency, a new shared pattern, a non-obvious tradeoff, a conflict with an accepted ADR, or a long code comment explaining "why".
1470
933
 
1471
- **How to propose**: Tell the human what decision you've hit, why it matters, and ask if they want to capture it as an ADR. If yes, run the full four-phase workflow. If no, note the decision in a code comment and move on.
934
+ **How to propose**: state decision, why it matters, ask whether to capture as ADR. If yes, run workflow. If no, note reasoning in code comment and move on.
1472
935
 
1473
936
  ## Creating an ADR: Four-Phase Workflow
1474
937
 
1475
- Every ADR goes through four phases. Do not skip phases.
938
+ Every ADR goes through four phases. Do not skip them.
1476
939
 
1477
940
  ### Phase 0: Scan the Codebase
1478
941
 
1479
- Before asking questions, gather enough repo context to avoid contradictory ADRs:
942
+ Before questions, gather enough repo context to avoid contradictory ADRs:
1480
943
 
1481
- 1. **Find existing ADRs.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\`. Note directory, naming, status style, related decisions, and anything this ADR might supersede.
1482
- 2. **Check the tech stack.** Read \`package.json\`, \`go.mod\`, \`requirements.txt\`, \`Cargo.toml\`, or equivalent for relevant dependencies and versions.
1483
- 3. **Find related code patterns.** Identify the files, directories, and implementation patterns this decision will affect.
1484
- 4. **Look for ADR references in code.** Comments and docs often reveal which decisions already govern an area.
944
+ 1. **Find existing ADRs.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\`. Note dir, naming, status style, related ADRs, possible supersession.
945
+ 2. **Check tech stack.** Read \`package.json\`, \`go.mod\`, \`requirements.txt\`, \`Cargo.toml\`, or equivalent for relevant deps and versions.
946
+ 3. **Find related code patterns.** Identify files, dirs, and impl patterns affected.
947
+ 4. **Look for ADR references in code.** Comments and docs often reveal governing decisions.
1485
948
 
1486
949
  ### Phase 1: Capture Intent (Socratic)
1487
950
 
1488
- Interview the human to understand the decision space. Ask questions **one at a time**, building on previous answers.
1489
-
1490
- **Core questions** (ask in roughly this order, skip what's already clear from context or Phase 0):
951
+ Ask questions **one at a time**.
1491
952
 
1492
- 1. **What are you deciding?** Push for a short verb phrase title ("Choose X", "Adopt Y", "Replace Z with W").
1493
- 2. **Why now?** What changed, broke, or will break if you do nothing?
1494
- 3. **What constraints exist?** Tech stack, timeline, budget, team skills, compliance, and existing decisions.
1495
- 4. **What does success look like?** Turn "it works" into measurable outcomes.
1496
- 5. **What options were considered?** At least two real alternatives, each with a core tradeoff.
1497
- 6. **What's the current lean?** Capture the instinct and the reason.
1498
- 7. **Who decides?** List decision-makers, consulted experts, and informed stakeholders.
1499
- 8. **What would an agent need to implement this?** Affected files, patterns to follow/avoid, config changes, and verification.
953
+ **Core questions** (skip what Phase 0 or context already answered): title, why now, constraints, success criteria, real options, current lean, who decides, and what an agent needs to implement.
1500
954
 
1501
- **Adaptive follow-ups**: probe where the decision is fuzzy. Useful prompts: "What's the worst-case outcome if this is wrong?", "What would make you revisit this in 6 months?", and "I found [existing ADR/pattern] — does this interact with it?"
955
+ **Adaptive follow-ups**: probe fuzzy areas. Useful prompts: "What's worst case if this is wrong?", "What would make you revisit this in 6 months?", "I found [existing ADR/pattern] — does this interact with it?"
1502
956
 
1503
957
  **When to stop**: stop only when every ADR section, especially Implementation Plan and Verification, can be filled without guessing.
1504
958
 
1505
- **Intent Summary Gate**: Before moving to Phase 2, summarize the title, trigger, constraints, options, current lean, non-goals, related ADRs/code, affected files, and verification plan, then ask: **Does this capture your intent? Anything to add or correct?**
959
+ **Intent Summary Gate**: Before Phase 2, summarize title, trigger, constraints, options, lean, non-goals, related ADRs/code, affected files, and verification plan. Ask: **Does this capture your intent? Anything to add or correct?**
1506
960
 
1507
- Do NOT proceed to Phase 2 until the human confirms the summary.
961
+ Do NOT proceed to Phase 2 until human confirms summary.
1508
962
 
1509
963
  ### Phase 2: Draft the ADR
1510
964
 
1511
965
  1. **Choose the ADR directory.**
1512
-
1513
- - If one exists (found in Phase 0), use it.
1514
- - If none exists, create \`contributing/decisions/\` (if \`contributing/\` exists), \`docs/decisions/\` (MADR default), or \`adr/\` (simpler repos).
1515
-
966
+ - If one exists, use it.
967
+ - If none exists, create \`contributing/decisions/\` (if \`contributing/\` exists), \`docs/decisions/\`, or \`adr/\`.
1516
968
  2. **Choose a filename strategy.**
1517
-
1518
969
  - If existing ADRs use date prefixes (\`YYYY-MM-DD-...\`), continue that.
1519
970
  - Otherwise use slug-only filenames (\`choose-database.md\`).
1520
-
1521
971
  3. **Choose a template.**
1522
-
1523
- - Use \`assets/templates/adr-simple.md\` for straightforward decisions (one clear winner, minimal tradeoffs).
1524
- - Use \`assets/templates/adr-madr.md\` when you need to document multiple options with structured pros/cons/drivers.
1525
- - See \`references/template-variants.md\` if unsure.
1526
-
1527
- 4. **Fill every section from the confirmed intent summary.** Do not leave placeholder text. Every section should contain real content or be removed (optional sections only).
1528
-
1529
- 5. **Write the Implementation Plan.** This is the most important section for agent-first ADRs. It tells the next agent exactly what to do. See the template for structure.
1530
-
1531
- 6. **Write Verification criteria as checkboxes.** These must be specific enough that an agent can programmatically or manually check each one.
1532
-
1533
- 7. **Generate the file.**
1534
- - Preferred: run \`scripts/new_adr.js\` (handles directory, naming, and optional index updates).
1535
- - If you can't run scripts, copy a template from \`assets/templates/\` and fill it manually.
972
+ - Use \`assets/templates/adr-simple.md\` for straightforward decisions.
973
+ - Use \`assets/templates/adr-madr.md\` for multiple options with structured tradeoffs.
974
+ - See \`references/template-variants.md\` if unsure.
975
+ 4. **Fill every section from confirmed intent summary.** Remove placeholders.
976
+ 5. **Write the Implementation Plan.** It tells next agent exactly what to do.
977
+ 6. **Write Verification criteria as checkboxes.** They must be specific enough for manual or programmatic checking.
978
+ 7. **Generate the file.** Preferred: run \`scripts/new_adr.js\`. Otherwise copy a template and fill it manually.
1536
979
 
1537
980
  ### Phase 3: Review Against Checklist
1538
981
 
1539
- After drafting, review the ADR against the inline quality criteria above, then use \`references/review-checklist.md\` for the full pass.
982
+ Review against inline quality criteria, then use \`references/review-checklist.md\` for full pass.
1540
983
 
1541
- **Present the review as a summary**, not a raw checklist dump. Format:
984
+ **Present the review as summary**, not raw checklist dump. Format:
1542
985
 
1543
986
  > **ADR Review**
1544
987
  >
@@ -1551,102 +994,85 @@ After drafting, review the ADR against the inline quality criteria above, then u
1551
994
  >
1552
995
  > **Recommendation**: {Ship it / Fix the gaps first / Needs more Phase 1 work}
1553
996
 
1554
- Only surface failures and notable strengths. If there are gaps, propose specific fixes. Do not finalize until the ADR passes the checklist or the human explicitly accepts the gaps.
997
+ Surface failures and notable strengths only. If gaps exist, propose fixes. Do not finalize until ADR passes or human accepts gaps.
1555
998
 
1556
999
  ## Consulting ADRs (Read Workflow)
1557
1000
 
1558
- Agents should read existing ADRs **before implementing changes** in a codebase that has them. This is not part of the create-an-ADR workflow — it's a standalone operation any agent should do.
1559
-
1560
- Consult ADRs:
1001
+ Read existing ADRs **before implementing changes** in repos that have them.
1561
1002
 
1562
- 1. **Before architecture work.** Especially auth, data layer, API design, infra, or any change that could contradict an existing decision.
1563
- 2. **Find the ADR directory and index.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\`, then read \`README.md\` or \`index.md\` if present.
1564
- 3. **Focus on accepted ADRs first.** Those are the active rules unless superseded.
1565
- 4. **Read the full ADR, not just the title.** Context, decision, consequences, non-goals, and Implementation Plan all matter.
1566
- 5. **Follow the Implementation Plan.** If an ADR says new queries go through \`src/db/\`, do that.
1567
- 6. **Escalate conflicts.** If code and ADR disagree, or a new change would contradict an accepted ADR, flag it or draft a superseding ADR.
1568
- 7. **Reference the ADR in your work.** Link it in code comments or PR notes when the decision directly governs the change.
1003
+ 1. **Before architecture work.**
1004
+ 2. **Find ADR dir and index.** Start with accepted ADRs.
1005
+ 3. **Read full ADR, not just title.**
1006
+ 4. **Follow the Implementation Plan.**
1007
+ 5. **Escalate conflicts or contradictions.**
1008
+ 6. **Reference governing ADR in your work.**
1569
1009
 
1570
1010
  ## Code ↔ ADR Linking
1571
1011
 
1572
- ADRs should be bidirectionally linked to the code they govern.
1573
-
1574
- - **ADR → Code**: the Implementation Plan should name affected paths, patterns, config changes, and verification commands.
1575
- - **Code → ADR**: add one lightweight comment at the entry point that links back to the governing ADR.
1576
- - **Why**: this makes the reasoning discoverable in both directions and makes superseded decisions easier to unwind.
1012
+ - **ADR Code**: Implementation Plan names affected paths, patterns, config changes, verification commands.
1013
+ - **Code → ADR**: add one lightweight comment at entry point linking back to governing ADR.
1577
1014
 
1578
1015
  ## Other Operations
1579
1016
 
1580
1017
  ### Update an Existing ADR
1581
1018
 
1582
- 1. Identify the intent:
1583
-
1584
- - **Accept / reject**: change status, add any final context.
1585
- - **Deprecate**: status → \`deprecated\`, explain replacement path.
1586
- - **Supersede**: create a new ADR, link both ways (old → new, new → old).
1587
- - **Add learnings**: append to \`## More Information\` with a date stamp. Do not rewrite history.
1588
-
1589
- 2. Use \`scripts/set_adr_status.js\` for status changes (supports YAML front matter, bullet status, and section status).
1019
+ 1. Identify intent: accept/reject, deprecate, supersede, or add learnings.
1020
+ 2. Use \`scripts/set_adr_status.js\` for status changes.
1590
1021
 
1591
1022
  ### Post-Acceptance Lifecycle
1592
1023
 
1593
1024
  After an ADR is accepted:
1594
1025
 
1595
- 1. **Create implementation tasks.** Each item in the Implementation Plan and each follow-up in Consequences should become a trackable task (issue, ticket, or TODO).
1596
- 2. **Reference the ADR in PRs.** Link to the ADR in PR descriptions, e.g. "Implements \`contributing/decisions/2025-06-15-use-sqlite-for-test-database.md\`."
1597
- 3. **Add code references.** Add ADR path comments at key implementation points.
1598
- 4. **Check verification criteria.** Once implementation is complete, walk through the Verification checkboxes. Update the ADR with results in \`## More Information\`.
1599
- 5. **Revisit when triggers fire.** If the ADR specified revisit conditions, monitor them.
1026
+ 1. **Create implementation tasks.**
1027
+ 2. **Reference ADR in PRs.**
1028
+ 3. **Add code references.**
1029
+ 4. **Check verification criteria.** Update ADR with results in \`## More Information\`.
1030
+ 5. **Revisit when triggers fire.**
1600
1031
 
1601
1032
  ### Index
1602
1033
 
1603
- If the repo has an ADR index/log file (often \`README.md\` or \`index.md\` in the ADR dir), keep it updated.
1034
+ If repo has ADR index/log file, keep it updated.
1604
1035
 
1605
- Preferred: let \`scripts/new_adr.js --update-index\` do it. Otherwise:
1606
-
1607
- - Add a bullet entry for the new ADR.
1608
- - Keep ordering consistent (numeric if numbered; date or alpha if slugs).
1036
+ - Let \`scripts/new_adr.js --update-index\` handle it when possible.
1037
+ - Otherwise add new ADR entry and keep ordering consistent.
1609
1038
 
1610
1039
  ### Bootstrap
1611
1040
 
1612
- When introducing ADRs to a repo that has none:
1041
+ When introducing ADRs to repo that has none:
1613
1042
 
1614
1043
  \`\`\`bash
1615
1044
  node scripts/bootstrap_adr.js
1616
1045
  \`\`\`
1617
1046
 
1618
- This creates the directory, an index file, and a filled-out first ADR ("Adopt architecture decision records") with real content explaining why the team is using ADRs. Use \`--json\` for machine-readable output. Use \`--dir\` to override the directory name.
1047
+ Creates dir, index, and first ADR.
1619
1048
 
1620
1049
  ## Resources
1621
1050
 
1622
1051
  ### scripts/
1623
1052
 
1624
- - \`scripts/new_adr.js\` — create a new ADR file from a template, using repo conventions.
1625
- - \`scripts/set_adr_status.js\` — update ADR status in-place. Use \`--json\` for machine output.
1626
- - \`scripts/bootstrap_adr.js\` — create ADR dir, \`README.md\`, and an initial "Adopt ADRs" decision.
1053
+ - \`scripts/new_adr.js\`
1054
+ - \`scripts/set_adr_status.js\`
1055
+ - \`scripts/bootstrap_adr.js\`
1627
1056
 
1628
1057
  ### references/
1629
1058
 
1630
- - \`references/review-checklist.md\` — full Phase 3 checklist.
1631
- - \`references/adr-conventions.md\` — directory, filename, status, and lifecycle conventions.
1632
- - \`references/template-variants.md\` — when to use simple vs MADR.
1633
- - \`references/examples.md\` — filled-out examples.
1059
+ - \`references/review-checklist.md\`
1060
+ - \`references/adr-conventions.md\`
1061
+ - \`references/template-variants.md\`
1062
+ - \`references/examples.md\`
1634
1063
 
1635
1064
  ### assets/
1636
1065
 
1637
- - \`assets/templates/adr-simple.md\` — lean template for straightforward decisions.
1638
- - \`assets/templates/adr-madr.md\` — MADR 4.0 template for structured tradeoffs.
1639
- - \`assets/templates/adr-readme.md\` — default ADR index scaffold.
1066
+ - \`assets/templates/adr-simple.md\`
1067
+ - \`assets/templates/adr-madr.md\`
1068
+ - \`assets/templates/adr-readme.md\`
1640
1069
 
1641
1070
  ### Script Usage
1642
1071
 
1643
1072
  \`\`\`bash
1644
1073
  node scripts/new_adr.js --title "Choose database" --status proposed
1645
-
1646
- node scripts/new_adr.js --title "Choose database" --status proposed --update-index
1647
- node scripts/new_adr.js --title "Choose database" --template madr --status proposed
1648
1074
  node scripts/bootstrap_adr.js --dir docs/decisions
1649
1075
  \`\`\`
1650
1076
 
1651
- Scripts auto-detect ADR directory and filename strategy. Use \`--dir\`, \`--strategy\`, and \`--json\` when you need overrides or machine-readable output.
1077
+ Use \`--dir\`, \`--strategy\`, and \`--json\` as needed.
1652
1078
  `}];export{e as default};