@vpxa/aikit 0.1.74 → 0.1.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -1
- package/packages/cli/dist/index.js +2 -2
- package/packages/cli/dist/{init-DQkar6Es.js → init-CuRXmyD9.js} +1 -1
- package/packages/cli/dist/scaffold-WMQ2uQ48.js +2 -0
- package/packages/cli/dist/{user-CopNWxHP.js → user-vbJwa7x2.js} +1 -1
- package/scaffold/dist/adapters/claude-code.mjs +4 -0
- package/scaffold/dist/adapters/copilot.mjs +75 -0
- package/scaffold/dist/adapters/flows.mjs +1 -0
- package/scaffold/dist/adapters/skills.mjs +1 -0
- package/scaffold/{compiled → dist/compiled}/flows-data.mjs +304 -446
- package/scaffold/{compiled → dist/compiled}/skills-data.mjs +554 -2281
- package/scaffold/dist/definitions/agents.mjs +9 -0
- package/scaffold/{definitions → dist/definitions}/bodies.mjs +6 -229
- package/scaffold/dist/definitions/exclusions.mjs +1 -0
- package/scaffold/dist/definitions/hooks.mjs +1 -0
- package/scaffold/dist/definitions/models.mjs +1 -0
- package/scaffold/dist/definitions/plugins.mjs +1 -0
- package/scaffold/{definitions → dist/definitions}/prompts.mjs +9 -149
- package/scaffold/{definitions → dist/definitions}/protocols.mjs +9 -37
- package/scaffold/dist/definitions/tools.mjs +1 -0
- package/packages/cli/dist/scaffold-ukCDW3wQ.js +0 -2
- package/scaffold/_preview/agents/Architect-Reviewer-Alpha.agent.md +0 -132
- package/scaffold/_preview/agents/Architect-Reviewer-Beta.agent.md +0 -132
- package/scaffold/_preview/agents/Code-Reviewer-Alpha.agent.md +0 -112
- package/scaffold/_preview/agents/Code-Reviewer-Beta.agent.md +0 -112
- package/scaffold/_preview/agents/Debugger.agent.md +0 -412
- package/scaffold/_preview/agents/Documenter.agent.md +0 -468
- package/scaffold/_preview/agents/Explorer.agent.md +0 -76
- package/scaffold/_preview/agents/Frontend.agent.md +0 -440
- package/scaffold/_preview/agents/Implementer.agent.md +0 -425
- package/scaffold/_preview/agents/Orchestrator.agent.md +0 -452
- package/scaffold/_preview/agents/Planner.agent.md +0 -481
- package/scaffold/_preview/agents/README.md +0 -57
- package/scaffold/_preview/agents/Refactor.agent.md +0 -435
- package/scaffold/_preview/agents/Researcher-Alpha.agent.md +0 -151
- package/scaffold/_preview/agents/Researcher-Beta.agent.md +0 -152
- package/scaffold/_preview/agents/Researcher-Delta.agent.md +0 -153
- package/scaffold/_preview/agents/Researcher-Gamma.agent.md +0 -152
- package/scaffold/_preview/agents/Security.agent.md +0 -433
- package/scaffold/_preview/agents/_shared/architect-reviewer-base.md +0 -104
- package/scaffold/_preview/agents/_shared/code-agent-base.md +0 -366
- package/scaffold/_preview/agents/_shared/code-reviewer-base.md +0 -87
- package/scaffold/_preview/agents/_shared/decision-protocol.md +0 -27
- package/scaffold/_preview/agents/_shared/forge-protocol.md +0 -90
- package/scaffold/_preview/agents/_shared/researcher-base.md +0 -114
- package/scaffold/_preview/agents/templates/adr-template.md +0 -28
- package/scaffold/_preview/agents/templates/execution-state.md +0 -26
- package/scaffold/_preview/flows/_epilogue/steps/docs-sync/README.md +0 -120
- package/scaffold/_preview/flows/aikit-advanced/README.md +0 -70
- package/scaffold/_preview/flows/aikit-advanced/steps/design/README.md +0 -178
- package/scaffold/_preview/flows/aikit-advanced/steps/execute/README.md +0 -145
- package/scaffold/_preview/flows/aikit-advanced/steps/plan/README.md +0 -122
- package/scaffold/_preview/flows/aikit-advanced/steps/spec/README.md +0 -121
- package/scaffold/_preview/flows/aikit-advanced/steps/task/README.md +0 -119
- package/scaffold/_preview/flows/aikit-advanced/steps/verify/README.md +0 -145
- package/scaffold/_preview/flows/aikit-basic/README.md +0 -51
- package/scaffold/_preview/flows/aikit-basic/steps/assess/README.md +0 -109
- package/scaffold/_preview/flows/aikit-basic/steps/design/README.md +0 -116
- package/scaffold/_preview/flows/aikit-basic/steps/implement/README.md +0 -131
- package/scaffold/_preview/flows/aikit-basic/steps/verify/README.md +0 -123
- package/scaffold/_preview/prompts/aikit-ask.prompt.md +0 -13
- package/scaffold/_preview/prompts/aikit-debug.prompt.md +0 -15
- package/scaffold/_preview/prompts/aikit-design.prompt.md +0 -15
- package/scaffold/_preview/prompts/aikit-flow-add.prompt.md +0 -84
- package/scaffold/_preview/prompts/aikit-flow-create.prompt.md +0 -80
- package/scaffold/_preview/prompts/aikit-flow-manage.prompt.md +0 -24
- package/scaffold/_preview/prompts/aikit-implement.prompt.md +0 -17
- package/scaffold/_preview/prompts/aikit-plan.prompt.md +0 -15
- package/scaffold/_preview/prompts/aikit-review.prompt.md +0 -24
- package/scaffold/_preview/skills/adr-skill/SKILL.md +0 -335
- package/scaffold/_preview/skills/adr-skill/assets/templates/adr-madr.md +0 -89
- package/scaffold/_preview/skills/adr-skill/assets/templates/adr-readme.md +0 -20
- package/scaffold/_preview/skills/adr-skill/assets/templates/adr-simple.md +0 -46
- package/scaffold/_preview/skills/adr-skill/references/adr-conventions.md +0 -95
- package/scaffold/_preview/skills/adr-skill/references/examples.md +0 -193
- package/scaffold/_preview/skills/adr-skill/references/review-checklist.md +0 -77
- package/scaffold/_preview/skills/adr-skill/references/template-variants.md +0 -52
- package/scaffold/_preview/skills/adr-skill/scripts/bootstrap_adr.js +0 -259
- package/scaffold/_preview/skills/adr-skill/scripts/new_adr.js +0 -391
- package/scaffold/_preview/skills/adr-skill/scripts/set_adr_status.js +0 -169
- package/scaffold/_preview/skills/aikit/SKILL.md +0 -754
- package/scaffold/_preview/skills/brainstorming/SKILL.md +0 -265
- package/scaffold/_preview/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
- package/scaffold/_preview/skills/c4-architecture/SKILL.md +0 -389
- package/scaffold/_preview/skills/c4-architecture/references/advanced-patterns.md +0 -552
- package/scaffold/_preview/skills/c4-architecture/references/c4-syntax.md +0 -510
- package/scaffold/_preview/skills/c4-architecture/references/common-mistakes.md +0 -437
- package/scaffold/_preview/skills/c4-architecture/references/html-design-system.md +0 -337
- package/scaffold/_preview/skills/c4-architecture/references/html-template.html +0 -627
- package/scaffold/_preview/skills/docs/SKILL.md +0 -553
- package/scaffold/_preview/skills/docs/references/diataxis-anti-patterns.md +0 -147
- package/scaffold/_preview/skills/docs/references/diataxis-compass.md +0 -123
- package/scaffold/_preview/skills/docs/references/diataxis-quadrants.md +0 -192
- package/scaffold/_preview/skills/docs/references/diataxis-quality.md +0 -76
- package/scaffold/_preview/skills/docs/references/diataxis-templates.md +0 -120
- package/scaffold/_preview/skills/docs/references/flow-artifacts-guide.md +0 -70
- package/scaffold/_preview/skills/docs/references/project-knowledge-gotchas.md +0 -32
- package/scaffold/_preview/skills/docs/references/project-knowledge-templates.md +0 -281
- package/scaffold/_preview/skills/docs/references/project-knowledge-workflow.md +0 -80
- package/scaffold/_preview/skills/frontend-design/SKILL.md +0 -237
- package/scaffold/_preview/skills/lesson-learned/SKILL.md +0 -113
- package/scaffold/_preview/skills/lesson-learned/references/anti-patterns.md +0 -55
- package/scaffold/_preview/skills/lesson-learned/references/se-principles.md +0 -109
- package/scaffold/_preview/skills/multi-agents-development/SKILL.md +0 -448
- package/scaffold/_preview/skills/multi-agents-development/architecture-review-prompt.md +0 -81
- package/scaffold/_preview/skills/multi-agents-development/code-quality-review-prompt.md +0 -91
- package/scaffold/_preview/skills/multi-agents-development/implementer-prompt.md +0 -93
- package/scaffold/_preview/skills/multi-agents-development/parallel-dispatch-example.md +0 -167
- package/scaffold/_preview/skills/multi-agents-development/spec-review-prompt.md +0 -81
- package/scaffold/_preview/skills/present/SKILL.md +0 -616
- package/scaffold/_preview/skills/react/SKILL.md +0 -309
- package/scaffold/_preview/skills/repo-access/SKILL.md +0 -178
- package/scaffold/_preview/skills/repo-access/references/error-patterns.md +0 -116
- package/scaffold/_preview/skills/repo-access/references/platform-matrix.md +0 -142
- package/scaffold/_preview/skills/requirements-clarity/SKILL.md +0 -333
- package/scaffold/_preview/skills/session-handoff/SKILL.md +0 -199
- package/scaffold/_preview/skills/session-handoff/references/handoff-template.md +0 -139
- package/scaffold/_preview/skills/session-handoff/references/resume-checklist.md +0 -80
- package/scaffold/_preview/skills/session-handoff/scripts/check_staleness.js +0 -269
- package/scaffold/_preview/skills/session-handoff/scripts/create_handoff.js +0 -299
- package/scaffold/_preview/skills/session-handoff/scripts/list_handoffs.js +0 -113
- package/scaffold/_preview/skills/session-handoff/scripts/validate_handoff.js +0 -241
- package/scaffold/_preview/skills/typescript/SKILL.md +0 -405
- package/scaffold/adapters/claude-code.mjs +0 -73
- package/scaffold/adapters/copilot.mjs +0 -292
- package/scaffold/adapters/flows.mjs +0 -27
- package/scaffold/adapters/skills.mjs +0 -25
- package/scaffold/definitions/agents.mjs +0 -266
- package/scaffold/definitions/exclusions.mjs +0 -58
- package/scaffold/definitions/hooks.mjs +0 -43
- package/scaffold/definitions/models.mjs +0 -84
- package/scaffold/definitions/plugins.mjs +0 -147
- package/scaffold/definitions/tools.mjs +0 -250
- package/scaffold/generate.mjs +0 -92
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Create a new ADR markdown file using repo conventions and a template.
|
|
4
|
-
*
|
|
5
|
-
* Design goals:
|
|
6
|
-
* - Safe defaults (auto-detect adr directory + numbering)
|
|
7
|
-
* - No external deps
|
|
8
|
-
* - Works even if the repo has no ADRs yet
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('node:fs');
|
|
12
|
-
const path = require('node:path');
|
|
13
|
-
|
|
14
|
-
function die(msg) {
|
|
15
|
-
process.stderr.write(`${msg}\n`);
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function slugify(text) {
|
|
20
|
-
const t = String(text || '')
|
|
21
|
-
.trim()
|
|
22
|
-
.toLowerCase();
|
|
23
|
-
const noQuotes = t.replace(/['"`]/g, '');
|
|
24
|
-
const dashed = noQuotes.replace(/[^a-z0-9]+/g, '-').replace(/-{2,}/g, '-');
|
|
25
|
-
const trimmed = dashed.replace(/^-+/, '').replace(/-+$/, '');
|
|
26
|
-
return trimmed || 'decision';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function toPosix(p) {
|
|
30
|
-
return p.split(path.sep).join('/');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function parseArgs(argv) {
|
|
34
|
-
const out = {
|
|
35
|
-
repoRoot: '.',
|
|
36
|
-
dir: null,
|
|
37
|
-
noCreateDir: false,
|
|
38
|
-
title: null,
|
|
39
|
-
status: 'proposed',
|
|
40
|
-
template: 'simple', // simple | madr
|
|
41
|
-
strategy: 'auto', // auto | date | slug
|
|
42
|
-
deciders: '',
|
|
43
|
-
consulted: '',
|
|
44
|
-
informed: '',
|
|
45
|
-
technicalStory: '',
|
|
46
|
-
chosenOption: '',
|
|
47
|
-
updateIndex: false,
|
|
48
|
-
indexFile: null,
|
|
49
|
-
json: false,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
for (let i = 2; i < argv.length; i++) {
|
|
53
|
-
const a = argv[i];
|
|
54
|
-
const next = () => {
|
|
55
|
-
if (i + 1 >= argv.length) die(`Missing value for ${a}`);
|
|
56
|
-
return argv[++i];
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
if (a === '--repo-root') out.repoRoot = next();
|
|
60
|
-
else if (a === '--dir') out.dir = next();
|
|
61
|
-
else if (a === '--no-create-dir') out.noCreateDir = true;
|
|
62
|
-
else if (a === '--title') out.title = next();
|
|
63
|
-
else if (a === '--status') out.status = next();
|
|
64
|
-
else if (a === '--template') out.template = next();
|
|
65
|
-
else if (a === '--strategy') out.strategy = next();
|
|
66
|
-
else if (a === '--deciders') out.deciders = next();
|
|
67
|
-
else if (a === '--consulted') out.consulted = next();
|
|
68
|
-
else if (a === '--informed') out.informed = next();
|
|
69
|
-
else if (a === '--technical-story') out.technicalStory = next();
|
|
70
|
-
else if (a === '--chosen-option') out.chosenOption = next();
|
|
71
|
-
else if (a === '--update-index') out.updateIndex = true;
|
|
72
|
-
else if (a === '--index-file') out.indexFile = next();
|
|
73
|
-
else if (a === '--json') out.json = true;
|
|
74
|
-
else if (a === '--help' || a === '-h') {
|
|
75
|
-
process.stdout.write(
|
|
76
|
-
[
|
|
77
|
-
'Usage: node new_adr.js --title "Choose database" [options]',
|
|
78
|
-
'',
|
|
79
|
-
'Options:',
|
|
80
|
-
' --repo-root <path> Repo root (default: .)',
|
|
81
|
-
' --dir <path> ADR directory (default: auto-detect, else adr/)',
|
|
82
|
-
' --no-create-dir Do not create ADR directory if missing',
|
|
83
|
-
' --status <value> ADR status (default: proposed)',
|
|
84
|
-
' --template simple|madr Template (default: simple)',
|
|
85
|
-
' --strategy auto|date|slug Filename strategy (default: auto)',
|
|
86
|
-
' --deciders "a,b" Deciders list',
|
|
87
|
-
' --consulted "a,b" Consulted experts (RACI)',
|
|
88
|
-
' --informed "a,b" Informed stakeholders (RACI)',
|
|
89
|
-
' --technical-story <x> Issue/ticket/PR link or short ref',
|
|
90
|
-
' --chosen-option <x> MADR template: chosen option label',
|
|
91
|
-
' --update-index Update adr/README.md (or existing index)',
|
|
92
|
-
' --index-file <path> Override index file (relative to repo root unless absolute)',
|
|
93
|
-
' --json Output machine-readable JSON (default: off)',
|
|
94
|
-
'',
|
|
95
|
-
].join('\n'),
|
|
96
|
-
);
|
|
97
|
-
process.exit(0);
|
|
98
|
-
} else {
|
|
99
|
-
die(`Unknown arg: ${a}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (!out.title) die('Missing required --title');
|
|
104
|
-
|
|
105
|
-
if (!['simple', 'madr'].includes(out.template)) die(`Invalid --template: ${out.template}`);
|
|
106
|
-
if (!['auto', 'date', 'slug'].includes(out.strategy)) die(`Invalid --strategy: ${out.strategy}`);
|
|
107
|
-
|
|
108
|
-
return out;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function detectAdrDir(repoRoot) {
|
|
112
|
-
const candidates = [
|
|
113
|
-
path.join(repoRoot, 'contributing', 'decisions'),
|
|
114
|
-
path.join(repoRoot, 'docs', 'decisions'),
|
|
115
|
-
path.join(repoRoot, 'adr'),
|
|
116
|
-
path.join(repoRoot, 'docs', 'adr'),
|
|
117
|
-
path.join(repoRoot, 'docs', 'adrs'),
|
|
118
|
-
path.join(repoRoot, 'decisions'),
|
|
119
|
-
];
|
|
120
|
-
for (const p of candidates) {
|
|
121
|
-
try {
|
|
122
|
-
if (fs.statSync(p).isDirectory()) return p;
|
|
123
|
-
} catch {
|
|
124
|
-
// ignore
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function listMdFiles(dir) {
|
|
131
|
-
let entries = [];
|
|
132
|
-
try {
|
|
133
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
134
|
-
} catch {
|
|
135
|
-
return [];
|
|
136
|
-
}
|
|
137
|
-
return entries
|
|
138
|
-
.filter((e) => e.isFile() && e.name.toLowerCase().endsWith('.md'))
|
|
139
|
-
.map((e) => e.name);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function detectStrategy(adrDir) {
|
|
143
|
-
const md = listMdFiles(adrDir);
|
|
144
|
-
for (const name of md) {
|
|
145
|
-
if (/^\d{4}-\d{2}-\d{2}-/.test(name)) return 'date';
|
|
146
|
-
}
|
|
147
|
-
if (md.length > 0) return 'slug';
|
|
148
|
-
return 'date';
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function todayISO() {
|
|
152
|
-
return new Date().toISOString().slice(0, 10);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function loadTemplate(templateName) {
|
|
156
|
-
const skillRoot = path.resolve(__dirname, '..');
|
|
157
|
-
const templatePath = path.join(skillRoot, 'assets', 'templates', `adr-${templateName}.md`);
|
|
158
|
-
if (!fs.existsSync(templatePath)) die(`Template not found: ${templatePath}`);
|
|
159
|
-
return fs.readFileSync(templatePath, 'utf8');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function renderTemplate(raw, vars) {
|
|
163
|
-
// Handle YAML front matter placeholders (quoted and unquoted)
|
|
164
|
-
let out = raw;
|
|
165
|
-
|
|
166
|
-
// YAML front matter fields — replace the whole placeholder pattern
|
|
167
|
-
// e.g. status: "{proposed | accepted | ...}" → status: proposed
|
|
168
|
-
out = out.replace(/^(status:\s*)["']?\{[^}]*\}["']?\s*$/m, `$1${vars.status}`);
|
|
169
|
-
out = out.replace(/^(date:\s*)\{[^}]*\}\s*$/m, `$1${vars.date}`);
|
|
170
|
-
out = out.replace(/^(decision-makers:\s*)["']?\{[^}]*\}["']?\s*$/m, `$1${vars.deciders || ''}`);
|
|
171
|
-
|
|
172
|
-
// consulted / informed: replace if a value was provided, otherwise remove the
|
|
173
|
-
// entire line so we don't leak placeholder text like "{list everyone...}"
|
|
174
|
-
if (vars.consulted) {
|
|
175
|
-
out = out.replace(/^(consulted:\s*)["']?\{[^}]*\}["']?\s*$/m, `$1${vars.consulted}`);
|
|
176
|
-
} else {
|
|
177
|
-
out = out.replace(/^consulted:\s*["']?\{[^}]*\}["']?\s*\n/m, '');
|
|
178
|
-
}
|
|
179
|
-
if (vars.informed) {
|
|
180
|
-
out = out.replace(/^(informed:\s*)["']?\{[^}]*\}["']?\s*$/m, `$1${vars.informed}`);
|
|
181
|
-
} else {
|
|
182
|
-
out = out.replace(/^informed:\s*["']?\{[^}]*\}["']?\s*\n/m, '');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Replace MADR-style heading placeholder
|
|
186
|
-
out = out.replace(/^(#\s+)\{short title[^}]*\}\s*$/m, `$1${vars.title}`);
|
|
187
|
-
|
|
188
|
-
// Inline placeholders (title in heading, etc.)
|
|
189
|
-
out = out
|
|
190
|
-
.replaceAll('{TITLE}', vars.title)
|
|
191
|
-
.replaceAll('{STATUS}', vars.status)
|
|
192
|
-
.replaceAll('{DATE}', vars.date)
|
|
193
|
-
.replaceAll('{DECIDERS}', vars.deciders)
|
|
194
|
-
.replaceAll('{TECHNICAL_STORY}', vars.technicalStory)
|
|
195
|
-
.replaceAll('{CHOSEN_OPTION}', vars.chosenOption);
|
|
196
|
-
|
|
197
|
-
return out;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function chooseIndexFile(adrDir) {
|
|
201
|
-
for (const name of ['README.md', 'index.md']) {
|
|
202
|
-
const p = path.join(adrDir, name);
|
|
203
|
-
if (fs.existsSync(p)) return p;
|
|
204
|
-
}
|
|
205
|
-
return path.join(adrDir, 'README.md');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function insertIndexEntryUnderHeading(lines, headingRegex, entryLine) {
|
|
209
|
-
// Returns { lines, inserted }
|
|
210
|
-
const headingIndex = lines.findIndex((l) => headingRegex.test(l));
|
|
211
|
-
if (headingIndex === -1) return { lines, inserted: false };
|
|
212
|
-
|
|
213
|
-
let sectionEnd = lines.length;
|
|
214
|
-
for (let i = headingIndex + 1; i < lines.length; i++) {
|
|
215
|
-
if (/^##\s+/.test(lines[i])) {
|
|
216
|
-
sectionEnd = i;
|
|
217
|
-
break;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Prefer inserting at end of list in this section if there is a list.
|
|
222
|
-
let lastListItem = -1;
|
|
223
|
-
for (let i = sectionEnd - 1; i > headingIndex; i--) {
|
|
224
|
-
if (/^[-*]\s+/.test(lines[i])) {
|
|
225
|
-
lastListItem = i;
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const insertAt = lastListItem !== -1 ? lastListItem + 1 : sectionEnd;
|
|
231
|
-
|
|
232
|
-
const out = [...lines];
|
|
233
|
-
|
|
234
|
-
// Ensure there's a blank line after the heading if we're inserting immediately after it.
|
|
235
|
-
if (insertAt === headingIndex + 1 && out[insertAt] !== '') {
|
|
236
|
-
out.splice(insertAt, 0, '');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
out.splice(insertAt, 0, entryLine);
|
|
240
|
-
return { lines: out, inserted: true };
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function updateIndex(indexFile, { relLink, title, status, date }) {
|
|
244
|
-
let content = '';
|
|
245
|
-
if (fs.existsSync(indexFile)) content = fs.readFileSync(indexFile, 'utf8');
|
|
246
|
-
else content = '# ADR Log\n\n';
|
|
247
|
-
|
|
248
|
-
if (content.includes(relLink)) return false;
|
|
249
|
-
|
|
250
|
-
const normalized = content.replace(/\r\n/g, '\n');
|
|
251
|
-
const hadTrailingNewline = normalized.endsWith('\n');
|
|
252
|
-
let lines = normalized.split('\n');
|
|
253
|
-
// Normalize away the trailing empty split element so insertion math is sane.
|
|
254
|
-
if (hadTrailingNewline && lines.length > 0 && lines[lines.length - 1] === '') {
|
|
255
|
-
lines = lines.slice(0, -1);
|
|
256
|
-
}
|
|
257
|
-
const entryLine = `- [${title}](${relLink}) (${status}, ${date})`;
|
|
258
|
-
|
|
259
|
-
// Prefer inserting under "## ADRs" if it exists, otherwise append at EOF.
|
|
260
|
-
const r = insertIndexEntryUnderHeading(lines, /^##\s+ADRs\s*$/i, entryLine);
|
|
261
|
-
const nextLines = r.inserted ? r.lines : [...lines, entryLine];
|
|
262
|
-
|
|
263
|
-
let next = nextLines.join('\n');
|
|
264
|
-
if (hadTrailingNewline) next += '\n';
|
|
265
|
-
|
|
266
|
-
fs.mkdirSync(path.dirname(indexFile), { recursive: true });
|
|
267
|
-
fs.writeFileSync(indexFile, next, 'utf8');
|
|
268
|
-
return true;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function main() {
|
|
272
|
-
const args = parseArgs(process.argv);
|
|
273
|
-
|
|
274
|
-
const repoRoot = path.resolve(process.cwd(), args.repoRoot);
|
|
275
|
-
if (!fs.existsSync(repoRoot)) die(`Repo root does not exist: ${repoRoot}`);
|
|
276
|
-
|
|
277
|
-
let adrDir;
|
|
278
|
-
if (args.dir) adrDir = path.resolve(repoRoot, args.dir);
|
|
279
|
-
else adrDir = detectAdrDir(repoRoot) || path.join(repoRoot, 'adr');
|
|
280
|
-
|
|
281
|
-
if (!fs.existsSync(adrDir)) {
|
|
282
|
-
if (args.noCreateDir) die(`ADR directory does not exist: ${adrDir}`);
|
|
283
|
-
fs.mkdirSync(adrDir, { recursive: true });
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
let strategy = args.strategy;
|
|
287
|
-
if (strategy === 'auto') strategy = detectStrategy(adrDir);
|
|
288
|
-
|
|
289
|
-
const title = String(args.title).trim();
|
|
290
|
-
const slug = slugify(title);
|
|
291
|
-
|
|
292
|
-
const today = todayISO();
|
|
293
|
-
|
|
294
|
-
let filename;
|
|
295
|
-
if (strategy === 'date') {
|
|
296
|
-
filename = `${today}-${slug}.md`;
|
|
297
|
-
} else {
|
|
298
|
-
filename = `${slug}.md`;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
let out = path.join(adrDir, filename);
|
|
302
|
-
if (fs.existsSync(out)) {
|
|
303
|
-
if (strategy === 'date') die(`ADR already exists: ${out}`);
|
|
304
|
-
let i = 2;
|
|
305
|
-
while (true) {
|
|
306
|
-
const candidate = path.join(adrDir, `${slug}-${i}.md`);
|
|
307
|
-
if (!fs.existsSync(candidate)) {
|
|
308
|
-
out = candidate;
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
311
|
-
i++;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const deciders = String(args.deciders || '')
|
|
316
|
-
.split(',')
|
|
317
|
-
.map((s) => s.trim())
|
|
318
|
-
.filter(Boolean)
|
|
319
|
-
.join(', ');
|
|
320
|
-
|
|
321
|
-
const consulted = String(args.consulted || '')
|
|
322
|
-
.split(',')
|
|
323
|
-
.map((s) => s.trim())
|
|
324
|
-
.filter(Boolean)
|
|
325
|
-
.join(', ');
|
|
326
|
-
const informed = String(args.informed || '')
|
|
327
|
-
.split(',')
|
|
328
|
-
.map((s) => s.trim())
|
|
329
|
-
.filter(Boolean)
|
|
330
|
-
.join(', ');
|
|
331
|
-
|
|
332
|
-
const raw = loadTemplate(args.template);
|
|
333
|
-
const rendered = renderTemplate(raw, {
|
|
334
|
-
title,
|
|
335
|
-
status: String(args.status).trim(),
|
|
336
|
-
date: today,
|
|
337
|
-
deciders,
|
|
338
|
-
consulted,
|
|
339
|
-
informed,
|
|
340
|
-
technicalStory: String(args.technicalStory || '').trim(),
|
|
341
|
-
chosenOption: String(args.chosenOption || '').trim(),
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
fs.writeFileSync(out, `${rendered.trimEnd()}\n`, 'utf8');
|
|
345
|
-
|
|
346
|
-
let updatedIndexPath = null;
|
|
347
|
-
let indexChanged = false;
|
|
348
|
-
|
|
349
|
-
if (args.updateIndex) {
|
|
350
|
-
let indexFile;
|
|
351
|
-
if (args.indexFile) {
|
|
352
|
-
indexFile = path.isAbsolute(args.indexFile)
|
|
353
|
-
? args.indexFile
|
|
354
|
-
: path.resolve(repoRoot, args.indexFile);
|
|
355
|
-
} else {
|
|
356
|
-
indexFile = chooseIndexFile(adrDir);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const relLink = toPosix(path.relative(path.dirname(indexFile), out));
|
|
360
|
-
indexChanged = updateIndex(indexFile, {
|
|
361
|
-
relLink,
|
|
362
|
-
title,
|
|
363
|
-
status: String(args.status).trim(),
|
|
364
|
-
date: today,
|
|
365
|
-
});
|
|
366
|
-
updatedIndexPath = indexFile;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (args.json) {
|
|
370
|
-
const payload = {
|
|
371
|
-
repoRoot,
|
|
372
|
-
adrDir,
|
|
373
|
-
createdAdrPath: out,
|
|
374
|
-
createdAdrRelPath: toPosix(path.relative(repoRoot, out)),
|
|
375
|
-
title,
|
|
376
|
-
status: String(args.status).trim(),
|
|
377
|
-
template: args.template,
|
|
378
|
-
strategy,
|
|
379
|
-
date: today,
|
|
380
|
-
indexUpdated: Boolean(updatedIndexPath),
|
|
381
|
-
indexChanged,
|
|
382
|
-
indexPath: updatedIndexPath,
|
|
383
|
-
indexRelPath: updatedIndexPath ? toPosix(path.relative(repoRoot, updatedIndexPath)) : null,
|
|
384
|
-
};
|
|
385
|
-
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
386
|
-
} else {
|
|
387
|
-
process.stdout.write(`${out}\n`);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
main();
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Update an ADR's status in-place.
|
|
4
|
-
*
|
|
5
|
-
* Supported patterns:
|
|
6
|
-
* - Bullet status: "- Status: proposed" or "* Status: proposed"
|
|
7
|
-
* - Nygard-style section: "## Status" followed by a single-line status value
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const fs = require('node:fs');
|
|
11
|
-
const path = require('node:path');
|
|
12
|
-
|
|
13
|
-
function die(msg) {
|
|
14
|
-
process.stderr.write(`${msg}\n`);
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function toPosix(p) {
|
|
19
|
-
return p.split(path.sep).join('/');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function parseArgs(argv) {
|
|
23
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
24
|
-
process.stdout.write(
|
|
25
|
-
[
|
|
26
|
-
'Usage: node set_adr_status.js <path> --status <value> [--json]',
|
|
27
|
-
'',
|
|
28
|
-
'Example:',
|
|
29
|
-
' node set_adr_status.js adr/2025-06-15-foo.md --status accepted',
|
|
30
|
-
'',
|
|
31
|
-
].join('\n'),
|
|
32
|
-
);
|
|
33
|
-
process.exit(0);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (argv.length < 3) die('Missing <path>');
|
|
37
|
-
const file = argv[2];
|
|
38
|
-
|
|
39
|
-
let status = null;
|
|
40
|
-
let json = false;
|
|
41
|
-
for (let i = 3; i < argv.length; i++) {
|
|
42
|
-
const a = argv[i];
|
|
43
|
-
if (a === '--status') {
|
|
44
|
-
if (i + 1 >= argv.length) die('Missing value for --status');
|
|
45
|
-
status = argv[++i];
|
|
46
|
-
} else if (a === '--json') {
|
|
47
|
-
json = true;
|
|
48
|
-
} else {
|
|
49
|
-
die(`Unknown arg: ${a}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (!status) die('Missing required --status');
|
|
53
|
-
return { file, status: String(status).trim(), json };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function setYamlFrontMatterStatus(lines, newStatus) {
|
|
57
|
-
// YAML front matter: starts with '---', ends with next '---'
|
|
58
|
-
if (lines.length < 2 || lines[0].trim() !== '---') return { lines, changed: false };
|
|
59
|
-
|
|
60
|
-
let changed = false;
|
|
61
|
-
const out = [];
|
|
62
|
-
let inFrontMatter = true;
|
|
63
|
-
let passedOpening = false;
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < lines.length; i++) {
|
|
66
|
-
const line = lines[i];
|
|
67
|
-
|
|
68
|
-
if (i === 0 && line.trim() === '---') {
|
|
69
|
-
passedOpening = true;
|
|
70
|
-
out.push(line);
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (passedOpening && inFrontMatter && line.trim() === '---') {
|
|
75
|
-
inFrontMatter = false;
|
|
76
|
-
out.push(line);
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (passedOpening && inFrontMatter && /^status\s*:/.test(line)) {
|
|
81
|
-
out.push(`status: ${newStatus}`);
|
|
82
|
-
changed = true;
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
out.push(line);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return { lines: out, changed };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function setBulletStatus(lines, newStatus) {
|
|
93
|
-
let changed = false;
|
|
94
|
-
const out = lines.map((line) => {
|
|
95
|
-
const m = line.match(/^([*-])\s*Status:\s*(.*)$/);
|
|
96
|
-
if (!m) return line;
|
|
97
|
-
changed = true;
|
|
98
|
-
return `${m[1]} Status: ${newStatus}`;
|
|
99
|
-
});
|
|
100
|
-
return { lines: out, changed };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function setSectionStatus(lines, newStatus) {
|
|
104
|
-
let changed = false;
|
|
105
|
-
const out = [];
|
|
106
|
-
|
|
107
|
-
for (let i = 0; i < lines.length; i++) {
|
|
108
|
-
out.push(lines[i]);
|
|
109
|
-
|
|
110
|
-
if (!/^##\s+Status\s*$/.test(lines[i])) continue;
|
|
111
|
-
|
|
112
|
-
// Replace next non-empty, non-heading line. If not found, insert.
|
|
113
|
-
let j = i + 1;
|
|
114
|
-
while (j < lines.length && lines[j].trim() === '') {
|
|
115
|
-
out.push(lines[j]);
|
|
116
|
-
j++;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (j < lines.length && !/^##\s+/.test(lines[j])) {
|
|
120
|
-
out.push(newStatus);
|
|
121
|
-
changed = true;
|
|
122
|
-
i = j; // skip original status line
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
out.push(newStatus);
|
|
127
|
-
changed = true;
|
|
128
|
-
i = j - 1;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return { lines: out, changed };
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function main() {
|
|
135
|
-
const args = parseArgs(process.argv);
|
|
136
|
-
const filePath = path.resolve(process.cwd(), args.file);
|
|
137
|
-
if (!fs.existsSync(filePath)) die(`File not found: ${filePath}`);
|
|
138
|
-
|
|
139
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
140
|
-
const hadTrailingNewline = content.endsWith('\n');
|
|
141
|
-
const lines = content.replace(/\r\n/g, '\n').split('\n');
|
|
142
|
-
|
|
143
|
-
let r = setYamlFrontMatterStatus(lines, args.status);
|
|
144
|
-
if (!r.changed) r = setBulletStatus(lines, args.status);
|
|
145
|
-
if (!r.changed) r = setSectionStatus(lines, args.status);
|
|
146
|
-
if (!r.changed) {
|
|
147
|
-
die(
|
|
148
|
-
"Could not find a status to update. Expected YAML front matter 'status:', '- Status:'/'* Status:', or a '## Status' section.",
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const newContent = r.lines.join('\n') + (hadTrailingNewline ? '\n' : '');
|
|
153
|
-
fs.writeFileSync(filePath, newContent, 'utf8');
|
|
154
|
-
|
|
155
|
-
if (args.json) {
|
|
156
|
-
process.stdout.write(
|
|
157
|
-
`${JSON.stringify({
|
|
158
|
-
filePath,
|
|
159
|
-
fileRelPath: toPosix(path.relative(process.cwd(), filePath)),
|
|
160
|
-
status: args.status,
|
|
161
|
-
changed: true,
|
|
162
|
-
})}\n`,
|
|
163
|
-
);
|
|
164
|
-
} else {
|
|
165
|
-
process.stdout.write(`${filePath}\n`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
main();
|