var e=[{file:`assets/templates/adr-madr.md`,content:`---
status: '{proposed | accepted | rejected | deprecated | superseded by [title](YYYY-MM-DD-title.md)}'
date: { YYYY-MM-DD }
decision-makers: '{list everyone who owns the decision}'
consulted: '{list everyone whose expertise was sought — two-way communication}'
informed: '{list everyone kept up-to-date — one-way communication}'
---

# {short title, representative of solved problem and found solution}

## Context and Problem Statement

{Describe the context and problem statement. Frame it as a question when possible. Link to relevant issues, tickets, or prior ADRs. Include enough background that someone (or an agent) encountering this for the first time can understand why this decision exists without asking follow-up questions.}

<!-- Optional — remove if not needed -->

## Decision Drivers

- {decision driver 1, e.g., a constraint, requirement, or force}
- {decision driver 2}
- …

## Considered Options

- {title of option 1}
- {title of option 2}
- {title of option 3}
- …

## Decision Outcome

Chosen option: "{title of option 1}", because {justification — reference drivers and tradeoffs}.

### Consequences

- Good, because {positive consequence}
- Bad, because {negative consequence}
- Neutral, because {consequence that is neither positive nor negative}
- …

## Implementation Plan

{This section tells an agent exactly what to do to implement this decision. Be specific — an agent should be able to start coding from this without asking follow-up questions.}

- **Affected paths**: {list files and directories that need to change, e.g., \`src/db/\`, \`src/config/database.ts\`, \`tests/integration/\`}
- **Dependencies**: {packages to add/remove/update, e.g., "add \`sql.js@1.x\`, remove \`pg-mem\`"}
- **Patterns to follow**: {reference existing code patterns, e.g., "follow the repository pattern in \`src/db/repositories/\`"}
- **Patterns to avoid**: {what NOT to do, e.g., "do not use raw SQL outside the data-access layer"}
- **Configuration**: {env vars, config files, feature flags to add/change}
- **Migration steps**: {if replacing something, what's the migration path? Can it be done incrementally?}

### Verification

{Checkboxes an agent can validate after implementation. Each must be specific and testable.}

- [ ] {verification criterion 1, e.g., "\`npm test\` passes with SQLite as the test database"}
- [ ] {verification criterion 2, e.g., "no direct \`pg\` imports outside \`src/db/client.ts\`"}
- [ ] {verification criterion 3, e.g., "CI pipeline completes in under 60 seconds"}
- …

<!-- Optional — remove if not needed -->

## Pros and Cons of the Options

### {title of option 1}

{Brief description or link to more information}

- Good, because {argument a}
- Good, because {argument b}
- Neutral, because {argument c}
- Bad, because {argument d}
- …

### {title of option 2}

{Brief description or link to more information}

- Good, because {argument a}
- Good, because {argument b}
- Neutral, because {argument c}
- Bad, because {argument d}
- …

<!-- Optional — remove if not needed -->

## More Information

{Additional context, links to related ADRs, team agreements, implementation notes, or conditions that would trigger revisiting this decision. This section is a catch-all for anything that helps future readers (human or agent) understand the full picture.}
`},{file:`assets/templates/adr-readme.md`,content:"# Architecture Decision Records (ADR)\n\nAn Architecture Decision Record (ADR) captures an important architecture decision along with its context and consequences.\n\n## Conventions\n\n- Directory: `{ADR_DIR}`\n- Naming:\n  - Use date-prefixed files: `YYYY-MM-DD-choose-database.md`\n  - If the repo already uses slug-only names, keep that: `choose-database.md`\n- Status values: `proposed`, `accepted`, `rejected`, `deprecated`, `superseded`\n\n## Workflow\n\n- Create a new ADR as `proposed`.\n- Discuss and iterate.\n- When the team commits: mark it `accepted` (or `rejected`).\n- If replaced later: create a new ADR and mark the old one `superseded` with a link.\n\n## ADRs\n"},{file:`assets/templates/adr-simple.md`,content:`---
status: '{proposed | accepted | rejected | deprecated | superseded by [title](YYYY-MM-DD-title.md)}'
date: { YYYY-MM-DD }
decision-makers: '{list everyone who owns the decision}'
---

# {short title, representative of solved problem and found solution}

## Context and Problem Statement

{Why does this decision need to happen now? What constraints exist? Include enough background that someone (or an agent) reading this for the first time can understand without follow-up questions.}

## Decision

{What are we choosing to do? Be specific — include scope and non-goals.}

## Consequences

- Good, because {positive consequence}
- Bad, because {negative consequence}
- …

## Implementation Plan

- **Affected paths**: {files and directories that change}
- **Dependencies**: {packages to add/remove/update}
- **Patterns to follow**: {existing code patterns to match}
- **Patterns to avoid**: {what NOT to do}

### Verification

- [ ] {how to confirm the decision was implemented correctly}
- [ ] {another verification criterion}

<!-- Optional — remove if not needed -->

## Alternatives Considered

- {Alternative 1}: {Why it was rejected, in one or two sentences.}
- {Alternative 2}: {Why it was rejected.}

<!-- Optional — remove if not needed -->

## More Information

{Related ADRs, PRs, issues, docs, or conditions that would trigger revisiting this decision.}
`},{file:`references/adr-conventions.md`,content:`# ADR Conventions (Reference)

## Directory

If repo already has ADR dir, keep it.

If not:

- **\`docs/decisions/\`** — MADR default when repo already uses \`docs/\`.
- **\`adr/\`** — simpler small-repo default.

Detection order: \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`docs/adrs/\`, \`decisions/\`.

## Filename Conventions

Pattern: \`YYYY-MM-DD-title-with-dashes.md\`

- Date matches \`date\` frontmatter.
- Slug: lowercase, dash-separated, imperative verb phrase.
- Same-day ADRs are fine; slug disambiguates.
- If repo already uses slug-only filenames, keep that convention.

## Minimal Sections

Every ADR needs:

1. **Context**: why now; which constraints/drivers apply.
2. **Decision**: what is chosen.
3. **Consequences**: what gets easier/harder, plus risks, costs, follow-ups.

For agent-first ADRs also state explicit constraints, non-goals, and follow-up tasks.

## Status Values

Track status in YAML front matter:

\`\`\`yaml
---
status: proposed
date: 2025-06-15
decision-makers: Alice, Bob
---
\`\`\`

Common statuses:

| Status                        | Meaning                                                       |
| ----------------------------- | ------------------------------------------------------------- |
| \`proposed\`                    | Under discussion |
| \`accepted\`                    | Active decision |
| \`rejected\`                    | Considered, not adopted |
| \`deprecated\`                  | No longer applies |
| \`superseded by [title](link)\` | Replaced by newer ADR |

## YAML Front Matter Fields

| Field             | Required | Description                                              |
| ----------------- | -------- | -------------------------------------------------------- |
| \`status\`          | Yes      | Current lifecycle state                                  |
| \`date\`            | Yes      | Date of last status change (YYYY-MM-DD)                  |
| \`decision-makers\` | Yes      | People who own the decision                              |
| \`consulted\`       | No       | Experts consulted |
| \`informed\`        | No       | Stakeholders updated |

\`consulted\` and \`informed\` follow RACI.

## Mutability

- Append dated notes instead of rewriting prior reasoning.
- If decision changes, create new ADR and supersede old one.
- Status changes and after-action notes can be edited in place.

## Categories (Large Projects)

For many ADRs, subdirectories are fine. Date prefixes stay local to category; pick one scheme early and document it in index.
`},{file:`references/examples.md`,content:`# ADR Examples

Filled examples of same decision at two detail levels. Reference only; never ship placeholder text.

## Short Version (Simple Template)

Use \`assets/templates/adr-simple.md\` for short-form drafting. The filled example below covers the same decision space in more detail.

## Long Version (MADR Template)

Same decision with full options analysis:

\`\`\`markdown
---
status: accepted
date: 2025-06-15
decision-makers: Sarah Chen, Joel
consulted: Alex (DBA), Platform team
informed: Frontend team, QA
---

# Use SQLite for local development database

## Context and Problem Statement

Our integration tests require a database but currently hit a shared PostgreSQL instance. This causes two problems:

1. Flaky tests from concurrent writes (multiple developers and CI jobs sharing one DB)
2. Slow CI — each run spends 3+ minutes provisioning and seeding the database

How can we provide a fast, isolated database for local development and CI without sacrificing confidence in production compatibility?

Related: [Use PostgreSQL for production](2025-05-01-use-postgresql-for-production.md) — this decision must not compromise production database choice.

## Decision Drivers

- CI speed: current 3+ minute DB setup is the bottleneck in our 5-minute pipeline
- Test isolation: zero shared state between parallel test runs
- Production parity: must catch SQL dialect issues before they hit production
- Developer experience: should work offline, no external dependencies for \`npm test\`
- Maintenance cost: solution should not require a dedicated owner

## Considered Options

- SQLite via sql.js
- Docker PostgreSQL per CI run
- In-memory PostgreSQL (pg-mem)

## Decision Outcome

Chosen option: "SQLite via sql.js", because it eliminates the CI bottleneck (2s vs 3+ min), provides full isolation, works offline, and has minimal maintenance cost. The dialect-drift risk is mitigated by a weekly CI job against real PostgreSQL.

### Consequences

- Good, because CI database setup drops from 3+ minutes to ~2 seconds
- Good, because each test run is fully isolated (file-based DB, no shared state)
- Good, because developers can run the full test suite offline with zero infrastructure
- Bad, because we must maintain a data-access abstraction layer to paper over SQL dialect differences
- Bad, because PostgreSQL-specific features (JSONB operators, array columns, advisory locks) cannot be tested locally
- Neutral, because the abstraction layer adds ~200 lines of code but also makes future DB migrations easier

## Implementation Plan

- **Affected paths**:
  - \`src/db/client.ts\` — new: unified database interface (DatabaseClient type + factory function)
  - \`src/db/sqlite-client.ts\` — new: SQLite implementation of DatabaseClient
  - \`src/db/pg-client.ts\` — refactor: extract current inline pg usage into DatabaseClient implementation
  - \`src/db/repositories/*.ts\` — update: use DatabaseClient instead of direct pg calls
  - \`tests/setup.ts\` — update: initialize SQLite by default, read \`DB_ENGINE\` env var
  - \`tests/fixtures/seed.sql\` — update: ensure all seed SQL is dialect-neutral
  - \`.env.test\` — new: \`DB_ENGINE=sqlite\`
  - \`.github/workflows/ci.yml\` — update: remove PostgreSQL service container from main CI
  - \`package.json\` — add devDependencies
- **Dependencies**: add \`sql.js@1.x\`, \`@types/sql.js@1.x\` as devDependencies
- **Patterns to follow**:
  - Repository pattern in \`src/db/repositories/\` — all database access goes through repository methods
  - Use parameterized queries exclusively (no string interpolation)
  - Reference implementation: \`src/db/repositories/users.ts\` for the expected style
- **Patterns to avoid**:
  - Do NOT import \`sql.js\` or \`pg\` directly outside \`src/db/\`
  - Do NOT use PostgreSQL-specific SQL in shared queries: no \`JSONB\` operators (\`->\`, \`->>\`), no \`ANY(ARRAY[...])\`, no \`ON CONFLICT ... DO UPDATE\`
  - Do NOT use SQLite-specific SQL either — keep queries portable
- **Configuration**: \`DB_ENGINE\` env var (\`sqlite\` | \`postgres\`), defaults to \`sqlite\` in test, \`postgres\` in production
- **Migration steps**:
  1. Create \`DatabaseClient\` interface and SQLite implementation
  2. Refactor existing pg code into pg implementation
  3. Update repositories one at a time (each can be a separate PR)
  4. Update test setup last, once all repositories use the abstraction
  5. Remove PostgreSQL service container from CI workflow

### Verification

- [ ] \`DB_ENGINE=sqlite npm test\` passes (all integration tests)
- [ ] \`DB_ENGINE=postgres npm test\` passes against a real PostgreSQL 16 instance
- [ ] \`grep -r "from 'sql.js'" src/ --include='*.ts' | grep -v 'src/db/'\` returns no results
- [ ] \`grep -r "from 'pg'" src/ --include='*.ts' | grep -v 'src/db/'\` returns no results
- [ ] CI pipeline completes in under 90 seconds (measured on main branch)
- [ ] \`src/db/client.ts\` exports \`DatabaseClient\` interface and \`createClient()\` factory
- [ ] \`.env.test\` sets \`DB_ENGINE=sqlite\`
- [ ] Weekly PostgreSQL compatibility CI job exists in \`.github/workflows/\`

## Pros and Cons of the Options

### SQLite via sql.js

[sql.js](https://sql.js.org/) — WASM-based SQLite for Node.js and browsers.

- Good, because zero infrastructure — just an npm dependency
- Good, because synchronous API makes test setup/teardown trivial
- Good, because file-based DBs enable parallelism (one file per test worker)
- Neutral, because requires a thin abstraction layer (~200 LOC)
- Bad, because SQL dialect differences (no JSONB, different date handling, no arrays)
- Bad, because does not exercise PostgreSQL-specific query plans or extensions

### Docker PostgreSQL per CI run

Spin up a fresh PostgreSQL container for each CI job.

- Good, because perfect production parity — same engine, same version
- Good, because no abstraction layer needed
- Bad, because adds 90+ seconds to every CI run (image pull + startup + healthcheck)
- Bad, because requires Docker-in-Docker on CI, adding complexity and security surface
- Bad, because developers need Docker running locally for \`npm test\`

### In-memory PostgreSQL (pg-mem)

[pg-mem](https://github.com/oguimbal/pg-mem) — in-memory PostgreSQL emulator for testing.

- Good, because better SQL compatibility than SQLite
- Good, because no infrastructure needed
- Bad, because incomplete support for our schema features (triggers, CTEs, lateral joins)
- Bad, because last published release is 8+ months old — maintenance risk
- Bad, because debugging failures requires understanding pg-mem's emulation quirks

## More Information

- Follow-up task: create data-access abstraction layer — #347
- Follow-up task: set up weekly PostgreSQL CI job — #348
- Related: [Use PostgreSQL for production](2025-05-01-use-postgresql-for-production.md)
- Revisit trigger: if dialect-drift bugs exceed 2 per quarter, reconsider Docker PostgreSQL approach
- Code references: after implementation, key files will have \`// ADR: 2025-06-15-use-sqlite-for-test-database\` comments at entry points
\`\`\`
`},{file:`references/review-checklist.md`,content:`# ADR Review Checklist

Use in Phase 3. Goal: **could a coding agent read this ADR and implement without follow-up questions?**

## Agent-Readiness Checks

### Context & Problem

- [ ] Reader with no context can understand why decision exists
- [ ] Trigger is clear
- [ ] No tribal knowledge assumed; acronyms and systems are explicit
- [ ] Relevant issues, PRs, or ADRs are linked

### Decision

- [ ] Decision is specific enough to act on
- [ ] Scope is bounded; non-goals are stated
- [ ] Constraints are explicit and measurable where possible

### Consequences

- [ ] Consequences are concrete and actionable
- [ ] Follow-up tasks are identified
- [ ] Risks include mitigation or acceptance rationale

### Implementation Plan

- [ ] Affected files/directories are explicit
- [ ] Dependency changes include version constraints
- [ ] Patterns to follow reference existing code
- [ ] Patterns to avoid are explicit
- [ ] Config and migration steps are listed when needed

### Verification

- [ ] Criteria are checkboxes
- [ ] Each criterion is testable
- [ ] Criteria cover functional and structural outcomes

### Options (MADR template)

- [ ] At least two genuine options were considered
- [ ] Each option has real pros and cons
- [ ] Chosen option cites specific drivers or tradeoffs

### Meta

- [ ] Status is correct
- [ ] Date is set
- [ ] Decision-makers are listed
- [ ] Title is a decision verb phrase
- [ ] Filename follows repo conventions

## Quick Scoring

Count checked items.

- **All checked**: Ship it.
- **1–3 unchecked**: Discuss gaps.
- **4+ unchecked**: Return to Phase 1.

## Common Failure Modes

| Symptom | Root Cause | Fix |
| --- | --- | --- |
| "Improve performance" as a consequence | Vague intent | Ask: "which metric, by how much, measured how?" |
| Only one option listed | Decision already made | Ask: "what did you reject and why?" |
| Implementation Plan says "update the code" | Too abstract | Ask: "which files, which functions, what pattern?" |
| Verification says "it works" | Not testable | Ask: "what command proves it works?" |
`},{file:`references/template-variants.md`,content:`# Template Variants

This skill ships two templates in \`assets/templates/\`.

## Simple

File: \`assets/templates/adr-simple.md\`

Use when:

- Decision is straightforward
- You need "why, what, consequences, impl"
- Alternatives are few and brief
- Speed matters more than exhaustive comparison

Sections: Context and Problem Statement → Decision → Consequences → Implementation Plan → Verification → Alternatives Considered (optional) → More Information (optional).

## MADR (Options-Heavy)

File: \`assets/templates/adr-madr.md\`

Use when:

- Multiple real options need structured tradeoffs
- Decision drivers must be explicit
- Decision may be revisited later
- Stakeholders need reasoning, not only outcome

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).

Aligns with [MADR 4.0](https://adr.github.io/madr/) plus agent-first sections.

## Both Templates Share

- **YAML front matter** for status, date, decision-makers, consulted, informed
- **Implementation Plan** — affected paths, deps, patterns to follow/avoid, config, migration steps
- **Verification as checkboxes** — testable criteria after impl
- **Agent-first framing** — placeholders push specificity and measurable constraints
- **"More Information"** for cross-links, follow-ups, revisit triggers

## Choosing Between Them

| Signal                   | Use Simple      | Use MADR     |
| ------------------------ | --------------- | ------------ |
| Number of real options   | 1–2             | 3+           |
| Team size affected       | Small / solo    | Cross-team   |
| Reversibility            | Easily reversed | Hard to undo |
| Expected lifetime        | Months          | Years        |
| Needs stakeholder review | No              | Yes          |

When in doubt, start with Simple. Expand to MADR if needed.
`},{file:`scripts/bootstrap_adr.js`,content:`#!/usr/bin/env node
const fs=require('node:fs');
const path=require('node:path');

function die(msg){process.stderr.write(\`\${msg}\\n\`);process.exit(1);}
function toPosix(value){return value.split(path.sep).join('/');}
function slugify(text){
  const value=String(text||'').trim().toLowerCase();
  return value.replace(/['"\`]/g,'').replace(/[^a-z0-9]+/g,'-').replace(/-{2,}/g,'-').replace(/^-+|-+$/g,'')||'decision';
}
function parseArgs(argv){
  const out={repoRoot:'.',dir:'adr',forceIndex:false,indexFile:null,firstTitle:'Adopt architecture decision records',firstStatus:'accepted',deciders:'',technicalStory:'',strategy:'date',json:false};
  for(let index=2;index<argv.length;index++){
    const arg=argv[index];
    const next=()=>{if(index+1>=argv.length) die(\`Missing value for \${arg}\`);return argv[++index];};
    if(arg==='--repo-root') out.repoRoot=next();
    else if(arg==='--dir') out.dir=next();
    else if(arg==='--force-index') out.forceIndex=true;
    else if(arg==='--index-file') out.indexFile=next();
    else if(arg==='--first-title') out.firstTitle=next();
    else if(arg==='--first-status') out.firstStatus=next();
    else if(arg==='--deciders') out.deciders=next();
    else if(arg==='--technical-story') out.technicalStory=next();
    else if(arg==='--strategy') out.strategy=next();
    else if(arg==='--json') out.json=true;
    else if(arg==='--help'||arg==='-h'){
      process.stdout.write([
        'Usage: node bootstrap_adr.js [options]',
        '',
        'Options:',
        '  --repo-root <path>     Repo root (default: .)',
        '  --dir <path>           ADR dir (default: adr)',
        '  --index-file <path>    Index path override',
        '  --force-index          Overwrite existing index',
        '  --first-title <text>   Initial ADR title',
        '  --first-status <text>  Initial ADR status (default: accepted)',
        '  --strategy date|slug|auto  Filename strategy (default: date)',
        '  --json                 Emit JSON',
        '',
      ].join('\\n'));
      process.exit(0);
    }else die(\`Unknown arg: \${arg}\`);
  }
  if(!['auto','date','slug'].includes(out.strategy)) die(\`Invalid --strategy: \${out.strategy}\`);
  return out;
}
function loadReadmeTemplate(){
  const templatePath=path.join(path.resolve(__dirname,'..'),'assets','templates','adr-readme.md');
  if(!fs.existsSync(templatePath)) die(\`README template not found: \${templatePath}\`);
  return fs.readFileSync(templatePath,'utf8');
}
function writeIndex(indexFile,adrDirName,{force}){
  if(fs.existsSync(indexFile)&&!force) return;
  const content=loadReadmeTemplate().replaceAll('{ADR_DIR}',adrDirName);
  fs.mkdirSync(path.dirname(indexFile),{recursive:true});
  fs.writeFileSync(indexFile,\`\${content.trimEnd()}\\n\`,'utf8');
}
function generateFirstAdr({title,status,date,deciders,adrDir}){
  const decisionMakers=deciders?String(deciders).split(',').map((name)=>name.trim()).filter(Boolean).join(', '):'';
  return \`---
status: \${status}
date: \${date}
decision-makers: \${decisionMakers}
---

# \${title}

## Context and Problem Statement

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.

## Decision

Adopt Architecture Decision Records (ADRs) using MADR 4.0, stored in \\\`\${adrDir}/\\\`.

Conventions:
- One ADR per file, named \\\`YYYY-MM-DD-title-with-dashes.md\\\`
- New ADRs start as \\\`proposed\\\`, then move to \\\`accepted\\\` or \\\`rejected\\\`
- Superseded ADRs link to replacements
- ADRs are self-contained enough for coding agents to implement from them

## Consequences

* Good, because decisions stay discoverable and version-controlled with code
* Good, because contributors can recover why architecture choices were made
* Bad, because writing ADRs costs time
* Neutral, because ADRs need periodic review for deprecation or supersession

## Alternatives Considered

* No formal records: Rejected because context is lost and decisions get relitigated.
* Wiki or Notion pages: Rejected because they drift from code and lack repo history.
* Lightweight RFCs: Rejected as too heavy for most decisions; ADRs can still scale up when needed.
\`;
}
function updateIndexFile(indexFile,{relLink,title,status,date}){
  if(!fs.existsSync(indexFile)) return;
  const content=fs.readFileSync(indexFile,'utf8');
  if(content.includes(relLink)) return;
  const entryLine=\`- [\${title}](\${relLink}) (\${status}, \${date})\`;
  const lines=content.replace(/\\r\\n/g,'\\n').split('\\n');
  const headingIndex=lines.findIndex((line)=>/^##\\s+ADRs\\s*$/i.test(line));
  if(headingIndex!==-1){
    let insertAt=headingIndex+1;
    while(insertAt<lines.length&&lines[insertAt].trim()==='') insertAt++;
    lines.splice(insertAt,0,entryLine);
  }else lines.push(entryLine);
  fs.writeFileSync(indexFile,lines.join('\\n'),'utf8');
}
function main(){
  const args=parseArgs(process.argv);
  const repoRoot=path.resolve(process.cwd(),args.repoRoot);
  if(!fs.existsSync(repoRoot)) die(\`Repo root does not exist: \${repoRoot}\`);
  const adrDir=path.resolve(repoRoot,args.dir);
  fs.mkdirSync(adrDir,{recursive:true});
  const indexFile=args.indexFile?(path.isAbsolute(args.indexFile)?args.indexFile:path.resolve(repoRoot,args.indexFile)):path.join(adrDir,'README.md');
  const indexExistedBefore=fs.existsSync(indexFile);
  writeIndex(indexFile,args.dir,{force:args.forceIndex});
  const indexWritten=fs.existsSync(indexFile)&&(!indexExistedBefore||args.forceIndex);
  const relIndex=path.isAbsolute(indexFile)?path.relative(repoRoot,indexFile):indexFile;
  const date=new Date().toISOString().slice(0,10);
  const strategy=args.strategy==='auto'?'date':args.strategy;
  const fileName=strategy==='date'?\`\${date}-\${slugify(args.firstTitle)}.md\`:\`\${slugify(args.firstTitle)}.md\`;
  const firstAdrPath=path.join(adrDir,fileName);
  fs.writeFileSync(firstAdrPath,\`\${generateFirstAdr({title:args.firstTitle,status:args.firstStatus,date,deciders:args.deciders,adrDir:args.dir}).trimEnd()}\\n\`,'utf8');
  updateIndexFile(indexFile,{relLink:toPosix(path.relative(path.dirname(indexFile),firstAdrPath)),title:args.firstTitle,status:args.firstStatus,date});
  if(args.json){
    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\`);
    return;
  }
  process.stdout.write(\`\${firstAdrPath}\\n\`);
  process.stdout.write(\`Bootstrapped ADRs at \${adrDir} (\${date})\\n\`);
  process.stdout.write(\`Index: \${indexFile}\\n\`);
}

main();
`},{file:`scripts/new_adr.js`,content:`#!/usr/bin/env node
const fs=require('node:fs');
const path=require('node:path');

function die(msg){process.stderr.write(\`\${msg}\\n\`);process.exit(1);}
function toPosix(value){return value.split(path.sep).join('/');}
function slugify(text){
  const value=String(text||'').trim().toLowerCase();
  return value.replace(/['"\`]/g,'').replace(/[^a-z0-9]+/g,'-').replace(/-{2,}/g,'-').replace(/^-+|-+$/g,'')||'decision';
}
function parseArgs(argv){
  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};
  for(let index=2;index<argv.length;index++){
    const arg=argv[index];
    const next=()=>{if(index+1>=argv.length) die(\`Missing value for \${arg}\`);return argv[++index];};
    if(arg==='--repo-root') out.repoRoot=next();
    else if(arg==='--dir') out.dir=next();
    else if(arg==='--no-create-dir') out.noCreateDir=true;
    else if(arg==='--title') out.title=next();
    else if(arg==='--status') out.status=next();
    else if(arg==='--template') out.template=next();
    else if(arg==='--strategy') out.strategy=next();
    else if(arg==='--deciders') out.deciders=next();
    else if(arg==='--consulted') out.consulted=next();
    else if(arg==='--informed') out.informed=next();
    else if(arg==='--technical-story') out.technicalStory=next();
    else if(arg==='--chosen-option') out.chosenOption=next();
    else if(arg==='--update-index') out.updateIndex=true;
    else if(arg==='--index-file') out.indexFile=next();
    else if(arg==='--json') out.json=true;
    else if(arg==='--help'||arg==='-h'){
      process.stdout.write([
        'Usage: node new_adr.js --title "Choose database" [options]',
        '',
        'Options:',
        '  --repo-root <path>     Repo root (default: .)',
        '  --dir <path>           ADR dir (default: auto-detect, else adr/)',
        '  --no-create-dir        Do not create ADR dir',
        '  --status <value>       ADR status (default: proposed)',
        '  --template simple|madr Template (default: simple)',
        '  --strategy auto|date|slug  Filename strategy (default: auto)',
        '  --deciders "a,b"      Deciders list',
        '  --consulted "a,b"     Consulted list (RACI)',
        '  --informed "a,b"      Informed list (RACI)',
        '  --technical-story <x>  Issue/ticket/PR ref',
        '  --chosen-option <x>    MADR chosen option label',
        '  --update-index         Update adr/README.md (or existing index)',
        '  --index-file <path>    Index file override',
        '  --json                 Emit JSON',
        '',
      ].join('\\n'));
      process.exit(0);
    }else die(\`Unknown arg: \${arg}\`);
  }
  if(!out.title) die('Missing required --title');
  if(!['simple','madr'].includes(out.template)) die(\`Invalid --template: \${out.template}\`);
  if(!['auto','date','slug'].includes(out.strategy)) die(\`Invalid --strategy: \${out.strategy}\`);
  return out;
}
function detectAdrDir(repoRoot){
  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')]){
    try{if(fs.statSync(candidate).isDirectory()) return candidate;}catch{}
  }
  return null;
}
function listMdFiles(dir){
  try{return fs.readdirSync(dir,{withFileTypes:true}).filter((entry)=>entry.isFile()&&entry.name.toLowerCase().endsWith('.md')).map((entry)=>entry.name);}catch{return [];}
}
function detectStrategy(adrDir){
  const files=listMdFiles(adrDir);
  if(files.some((name)=>/^\\d{4}-\\d{2}-\\d{2}-/.test(name))) return 'date';
  return files.length?'slug':'date';
}
function todayISO(){return new Date().toISOString().slice(0,10);}
function loadTemplate(name){
  const templatePath=path.join(path.resolve(__dirname,'..'),'assets','templates',\`adr-\${name}.md\`);
  if(!fs.existsSync(templatePath)) die(\`Template not found: \${templatePath}\`);
  return fs.readFileSync(templatePath,'utf8');
}
function renderTemplate(raw,vars){
  let out=raw;
  out=out.replace(/^(status:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.status}\`);
  out=out.replace(/^(date:\\s*)\\{[^}]*\\}\\s*$/m,\`$1\${vars.date}\`);
  out=out.replace(/^(decision-makers:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.deciders||''}\`);
  out=vars.consulted?out.replace(/^(consulted:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.consulted}\`):out.replace(/^consulted:\\s*["']?\\{[^}]*\\}["']?\\s*\\n/m,'');
  out=vars.informed?out.replace(/^(informed:\\s*)["']?\\{[^}]*\\}["']?\\s*$/m,\`$1\${vars.informed}\`):out.replace(/^informed:\\s*["']?\\{[^}]*\\}["']?\\s*\\n/m,'');
  return out
    .replace(/^(#\\s+)\\{short title[^}]*\\}\\s*$/m,\`$1\${vars.title}\`)
    .replaceAll('{TITLE}',vars.title)
    .replaceAll('{STATUS}',vars.status)
    .replaceAll('{DATE}',vars.date)
    .replaceAll('{DECIDERS}',vars.deciders)
    .replaceAll('{TECHNICAL_STORY}',vars.technicalStory)
    .replaceAll('{CHOSEN_OPTION}',vars.chosenOption);
}
function chooseIndexFile(adrDir){
  for(const name of ['README.md','index.md']){const candidate=path.join(adrDir,name);if(fs.existsSync(candidate)) return candidate;}
  return path.join(adrDir,'README.md');
}
function insertIndexEntryUnderHeading(lines,headingRegex,entryLine){
  const headingIndex=lines.findIndex((line)=>headingRegex.test(line));
  if(headingIndex===-1) return {lines,inserted:false};
  let sectionEnd=lines.length;
  for(let index=headingIndex+1;index<lines.length;index++){if(/^##\\s+/.test(lines[index])){sectionEnd=index;break;}}
  let insertAt=sectionEnd;
  for(let index=sectionEnd-1;index>headingIndex;index--){if(/^[-*]\\s+/.test(lines[index])){insertAt=index+1;break;}}
  const out=[...lines];
  if(insertAt===headingIndex+1&&out[insertAt]!=='') out.splice(insertAt,0,'');
  out.splice(insertAt,0,entryLine);
  return {lines:out,inserted:true};
}
function updateIndex(indexFile,{relLink,title,status,date}){
  const source=fs.existsSync(indexFile)?fs.readFileSync(indexFile,'utf8'):'# ADR Log\\n\\n';
  if(source.includes(relLink)) return false;
  const normalized=source.replace(/\\r\\n/g,'\\n');
  const hadTrailingNewline=normalized.endsWith('\\n');
  const lines=normalized.split('\\n');
  if(hadTrailingNewline&&lines[lines.length-1]==='') lines.pop();
  const entryLine=\`- [\${title}](\${relLink}) (\${status}, \${date})\`;
  const result=insertIndexEntryUnderHeading(lines,/^##\\s+ADRs\\s*$/i,entryLine);
  let next=(result.inserted?result.lines:[...lines,entryLine]).join('\\n');
  if(hadTrailingNewline) next+='\\n';
  fs.mkdirSync(path.dirname(indexFile),{recursive:true});
  fs.writeFileSync(indexFile,next,'utf8');
  return true;
}
function main(){
  const args=parseArgs(process.argv);
  const repoRoot=path.resolve(process.cwd(),args.repoRoot);
  if(!fs.existsSync(repoRoot)) die(\`Repo root does not exist: \${repoRoot}\`);
  const adrDir=args.dir?path.resolve(repoRoot,args.dir):detectAdrDir(repoRoot)||path.join(repoRoot,'adr');
  if(!fs.existsSync(adrDir)){if(args.noCreateDir) die(\`ADR directory does not exist: \${adrDir}\`);fs.mkdirSync(adrDir,{recursive:true});}
  const strategy=args.strategy==='auto'?detectStrategy(adrDir):args.strategy;
  const title=String(args.title).trim();
  const slug=slugify(title);
  const date=todayISO();
  let out=path.join(adrDir,strategy==='date'?\`\${date}-\${slug}.md\`:\`\${slug}.md\`);
  if(fs.existsSync(out)){
    if(strategy==='date') die(\`ADR already exists: \${out}\`);
    let suffix=2;
    while(true){const candidate=path.join(adrDir,\`\${slug}-\${suffix}.md\`);if(!fs.existsSync(candidate)){out=candidate;break;}suffix++;}
  }
  const deciders=String(args.deciders||'').split(',').map((value)=>value.trim()).filter(Boolean).join(', ');
  const consulted=String(args.consulted||'').split(',').map((value)=>value.trim()).filter(Boolean).join(', ');
  const informed=String(args.informed||'').split(',').map((value)=>value.trim()).filter(Boolean).join(', ');
  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()});
  fs.writeFileSync(out,\`\${rendered.trimEnd()}\\n\`,'utf8');
  let updatedIndexPath=null;
  let indexChanged=false;
  if(args.updateIndex){
    const indexFile=args.indexFile?(path.isAbsolute(args.indexFile)?args.indexFile:path.resolve(repoRoot,args.indexFile)):chooseIndexFile(adrDir);
    indexChanged=updateIndex(indexFile,{relLink:toPosix(path.relative(path.dirname(indexFile),out)),title,status:String(args.status).trim(),date});
    updatedIndexPath=indexFile;
  }
  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\`);
  else process.stdout.write(\`\${out}\\n\`);
}

main();
`},{file:`scripts/set_adr_status.js`,content:`#!/usr/bin/env node
const fs=require('node:fs');
const path=require('node:path');

function die(msg){process.stderr.write(\`\${msg}\\n\`);process.exit(1);}
function toPosix(value){return value.split(path.sep).join('/');}
function parseArgs(argv){
  if(argv.includes('--help')||argv.includes('-h')){
    process.stdout.write([
      'Usage: node set_adr_status.js <path> --status <value> [--json]',
      '',
      'Example:',
      '  node set_adr_status.js adr/2025-06-15-foo.md --status accepted',
      '',
    ].join('\\n'));
    process.exit(0);
  }
  if(argv.length<3) die('Missing <path>');
  let status=null;
  let json=false;
  for(let index=3;index<argv.length;index++){
    const arg=argv[index];
    if(arg==='--status'){
      if(index+1>=argv.length) die('Missing value for --status');
      status=argv[++index];
    }else if(arg==='--json') json=true;
    else die(\`Unknown arg: \${arg}\`);
  }
  if(!status) die('Missing required --status');
  return {file:argv[2],status:String(status).trim(),json};
}
function setYaml(lines,status){
  if(lines.length<2||lines[0].trim()!=='---') return {lines,changed:false};
  const out=[];
  let inFrontMatter=true;
  let passedOpening=false;
  let changed=false;
  for(let index=0;index<lines.length;index++){
    const line=lines[index];
    if(index===0&&line.trim()==='---'){passedOpening=true;out.push(line);continue;}
    if(passedOpening&&inFrontMatter&&line.trim()==='---'){inFrontMatter=false;out.push(line);continue;}
    if(passedOpening&&inFrontMatter&&/^status\\s*:/.test(line)){out.push(\`status: \${status}\`);changed=true;continue;}
    out.push(line);
  }
  return {lines:out,changed};
}
function setBullet(lines,status){
  let changed=false;
  return {
    changed,
    lines:lines.map((line)=>{
      const match=line.match(/^([*-])\\s*Status:\\s*(.*)$/);
      if(!match) return line;
      changed=true;
      return \`\${match[1]} Status: \${status}\`;
    }),
  };
}
function setSection(lines,status){
  const out=[];
  let changed=false;
  for(let index=0;index<lines.length;index++){
    out.push(lines[index]);
    if(!/^##\\s+Status\\s*$/.test(lines[index])) continue;
    let next=index+1;
    while(next<lines.length&&lines[next].trim()===''){out.push(lines[next]);next++;}
    if(next<lines.length&&!/^##\\s+/.test(lines[next])){out.push(status);changed=true;index=next;continue;}
    out.push(status);changed=true;index=next-1;
  }
  return {lines:out,changed};
}
function main(){
  const args=parseArgs(process.argv);
  const filePath=path.resolve(process.cwd(),args.file);
  if(!fs.existsSync(filePath)) die(\`File not found: \${filePath}\`);
  const content=fs.readFileSync(filePath,'utf8');
  const hadTrailingNewline=content.endsWith('\\n');
  const lines=content.replace(/\\r\\n/g,'\\n').split('\\n');
  let result=setYaml(lines,args.status);
  if(!result.changed) result=setBullet(lines,args.status);
  if(!result.changed) result=setSection(lines,args.status);
  if(!result.changed) die("Could not find a status to update. Expected YAML front matter 'status:', '- Status:'/'* Status:', or a '## Status' section.");
  fs.writeFileSync(filePath,result.lines.join('\\n')+(hadTrailingNewline?'\\n':''),'utf8');
  if(args.json) process.stdout.write(\`\${JSON.stringify({filePath,fileRelPath:toPosix(path.relative(process.cwd(),filePath)),status:args.status,changed:true})}\\n\`);
  else process.stdout.write(\`\${filePath}\\n\`);
}

main();
`},{file:`SKILL.md`,content:`---
name: adr-skill
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.
metadata:
  category: cross-cutting
  domain: general
  applicability: on-demand
  inputs: [decisions, context]
  outputs: [adr-document]
  relatedSkills: [c4-architecture]
  internal: true
---

# ADR Skill

## Quick Reference

**Purpose:** Create ADRs agents can implement from without follow-up.

**Write an ADR when:** decision changes system shape, is hard to reverse, affects other people/agents, or has real alternatives.

**Four-Phase Workflow:**
1. **Discover** — surface intent, constraints, alternatives
2. **Draft** — fill Simple or MADR template with concrete constraints
3. **Validate** — review for agent-readiness (target ≥ 80%)
4. **Record** — save to \`docs/decisions/\` or \`adr/\`, update index

**Two templates:**
- **Simple** (≤3 options, single-team) — Context → Decision → Consequences → Implementation Plan → Alternatives
- **MADR** (complex, multi-team) — adds drivers, options matrix, deeper plan

**Commands:** Consult existing ADRs before impl. Create/update with scripts below.

**Agent-readiness gate:** target ≥ 80%.

## Mindset

Write for future readers with zero context. ADR = decision record, not meeting notes.

## Philosophy

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.

## ADR Quality Criteria

An ADR is agent-ready when it passes:
- [ ] **Context is self-contained**
- [ ] **Options include at least 2 genuine alternatives**
- [ ] **Decision rationale cites concrete evidence**
- [ ] **Consequences are bidirectional**
- [ ] **Status is explicit**
- [ ] **Superseded ADRs link to their replacement**

## When to Write an ADR

Write an ADR when a decision:

- **Changes how system is built or operated** (new dep, pattern, infra choice, API design)
- **Is hard to reverse** once code is written against it
- **Affects other people or agents** who will work in codebase later
- **Has real alternatives** that were considered and rejected

## NEVER

- **NEVER write ADR after impl** unless documenting discovery
- **NEVER include only one option**
- **NEVER use vague consequences**
- **NEVER let ADRs linger in stale proposed state**
- **NEVER modify an accepted ADR**; supersede instead
- **NEVER write ADR for trivial decisions**
- **NEVER skip rejected alternatives**



### Proactive ADR Triggers (For Agents)

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".

**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.

## Creating an ADR: Four-Phase Workflow

Every ADR goes through four phases. Do not skip them.

### Phase 0: Scan the Codebase

Before questions, gather enough repo context to avoid contradictory ADRs:

1. **Find existing ADRs.** Check \`contributing/decisions/\`, \`docs/decisions/\`, \`adr/\`, \`docs/adr/\`, \`decisions/\`. Note dir, naming, status style, related ADRs, possible supersession.
2. **Check tech stack.** Read \`package.json\`, \`go.mod\`, \`requirements.txt\`, \`Cargo.toml\`, or equivalent for relevant deps and versions.
3. **Find related code patterns.** Identify files, dirs, and impl patterns affected.
4. **Look for ADR references in code.** Comments and docs often reveal governing decisions.

### Phase 1: Capture Intent (Socratic)

Ask questions **one at a time**.

**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.

**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?"

**When to stop**: stop only when every ADR section, especially Implementation Plan and Verification, can be filled without guessing.

**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?**

Do NOT proceed to Phase 2 until human confirms summary.

### Phase 2: Draft the ADR

1. **Choose the ADR directory.**
   - If one exists, use it.
   - If none exists, create \`contributing/decisions/\` (if \`contributing/\` exists), \`docs/decisions/\`, or \`adr/\`.
2. **Choose a filename strategy.**
   - If existing ADRs use date prefixes (\`YYYY-MM-DD-...\`), continue that.
   - Otherwise use slug-only filenames (\`choose-database.md\`).
3. **Choose a template.**
   - Use \`assets/templates/adr-simple.md\` for straightforward decisions.
   - Use \`assets/templates/adr-madr.md\` for multiple options with structured tradeoffs.
   - See \`references/template-variants.md\` if unsure.
4. **Fill every section from confirmed intent summary.** Remove placeholders.
5. **Write the Implementation Plan.** It tells next agent exactly what to do.
6. **Write Verification criteria as checkboxes.** They must be specific enough for manual or programmatic checking.
7. **Generate the file.** Preferred: run \`scripts/new_adr.js\`. Otherwise copy a template and fill it manually.

### Phase 3: Review Against Checklist

Review against inline quality criteria, then use \`references/review-checklist.md\` for full pass.

**Present the review as summary**, not raw checklist dump. Format:

> **ADR Review**
>
> ✅ **Passes**: {list what's solid — e.g., "context is self-contained, implementation plan covers affected files, verification criteria are checkable"}
>
> ⚠️ **Gaps found**:
>
> - {specific gap 1 — e.g., "Implementation Plan doesn't mention test files — which test suite should cover this?"}
> - {specific gap 2}
>
> **Recommendation**: {Ship it / Fix the gaps first / Needs more Phase 1 work}

Surface failures and notable strengths only. If gaps exist, propose fixes. Do not finalize until ADR passes or human accepts gaps.

## Consulting ADRs (Read Workflow)

Read existing ADRs **before implementing changes** in repos that have them.

1. **Before architecture work.**
2. **Find ADR dir and index.** Start with accepted ADRs.
3. **Read full ADR, not just title.**
4. **Follow the Implementation Plan.**
5. **Escalate conflicts or contradictions.**
6. **Reference governing ADR in your work.**

## Code ↔ ADR Linking

- **ADR → Code**: Implementation Plan names affected paths, patterns, config changes, verification commands.
- **Code → ADR**: add one lightweight comment at entry point linking back to governing ADR.

## Other Operations

### Update an Existing ADR

1. Identify intent: accept/reject, deprecate, supersede, or add learnings.
2. Use \`scripts/set_adr_status.js\` for status changes.

### Post-Acceptance Lifecycle

After an ADR is accepted:

1. **Create implementation tasks.**
2. **Reference ADR in PRs.**
3. **Add code references.**
4. **Check verification criteria.** Update ADR with results in \`## More Information\`.
5. **Revisit when triggers fire.**

### Index

If repo has ADR index/log file, keep it updated.

- Let \`scripts/new_adr.js --update-index\` handle it when possible.
- Otherwise add new ADR entry and keep ordering consistent.

### Bootstrap

When introducing ADRs to repo that has none:

\`\`\`bash
node scripts/bootstrap_adr.js
\`\`\`

Creates dir, index, and first ADR.

## Resources

### scripts/

- \`scripts/new_adr.js\`
- \`scripts/set_adr_status.js\`
- \`scripts/bootstrap_adr.js\`

### references/

- \`references/review-checklist.md\`
- \`references/adr-conventions.md\`
- \`references/template-variants.md\`
- \`references/examples.md\`

### assets/

- \`assets/templates/adr-simple.md\`
- \`assets/templates/adr-madr.md\`
- \`assets/templates/adr-readme.md\`

### Script Usage

\`\`\`bash
node scripts/new_adr.js --title "Choose database" --status proposed
node scripts/bootstrap_adr.js --dir docs/decisions
\`\`\`

Use \`--dir\`, \`--strategy\`, and \`--json\` as needed.
`}];export{e as default};