@izkac/forgekit 0.3.11 → 0.3.13
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/bin/forge.mjs +107 -107
- package/bin/forgekit.mjs +83 -83
- package/bin/review.mjs +81 -81
- package/package.json +1 -1
- package/scripts/prepack.mjs +78 -78
- package/scripts/run-tests.mjs +50 -50
- package/src/adr.mjs +236 -236
- package/src/adr.test.mjs +170 -170
- package/src/change.mjs +327 -234
- package/src/change.test.mjs +145 -83
- package/src/cleanup-sessions.mjs +84 -84
- package/src/config.mjs +103 -103
- package/src/defer.mjs +75 -75
- package/src/doctor.mjs +350 -341
- package/src/doctor.test.mjs +114 -114
- package/src/init.mjs +680 -621
- package/src/install.mjs +815 -815
- package/src/install.test.mjs +180 -180
- package/src/integrity-check.mjs +60 -60
- package/src/integrity.mjs +682 -682
- package/src/integrity.test.mjs +566 -566
- package/src/lib.mjs +143 -143
- package/src/models.defaults.json +41 -41
- package/src/new-session.mjs +99 -99
- package/src/openspec-overlays/README.md +19 -19
- package/src/openspec-overlays/openspec-apply-change-footer.md +14 -14
- package/src/openspec-overlays/opsx-apply-completion-step.md +1 -1
- package/src/openspec-overlays/opsx-apply-implement-step.md +11 -11
- package/src/paths.mjs +92 -92
- package/src/plan-engine.mjs +321 -278
- package/src/plan-engine.test.mjs +447 -283
- package/src/preferences.defaults.json +78 -78
- package/src/preferences.mjs +438 -438
- package/src/preferences.test.mjs +174 -174
- package/src/record-evidence.mjs +204 -204
- package/src/resolve-model.mjs +312 -312
- package/src/resolve-model.test.mjs +194 -194
- package/src/review/cli.test.mjs +117 -117
- package/src/review/export.mjs +172 -172
- package/src/review/export.test.mjs +197 -197
- package/src/review/fixtures/valid-review.json +42 -42
- package/src/review/lib.mjs +894 -894
- package/src/review/lib.test.mjs +266 -266
- package/src/review/schema.json +196 -196
- package/src/review/signals.test.mjs +62 -62
- package/src/score-cli.mjs +68 -68
- package/src/score.mjs +568 -566
- package/src/score.test.mjs +366 -340
- package/src/session-reminder.mjs +207 -207
- package/src/session-status.mjs +70 -70
- package/src/set-models.mjs +186 -186
- package/src/set-phase.mjs +205 -205
- package/src/set-prefs.mjs +294 -294
- package/src/specs-sync.mjs +234 -0
- package/src/specs-sync.test.mjs +114 -0
- package/src/spine.mjs +93 -93
- package/src/triage-prompt.mjs +175 -175
- package/src/triage-prompt.test.mjs +50 -50
- package/src/vendor-openspec-overlays.mjs +176 -176
- package/src/vendor-openspec-overlays.test.mjs +62 -62
- package/vendor/skills/archive-to-adr/SKILL.md +149 -149
- package/vendor/skills/forge/SKILL.md +136 -136
- package/vendor/skills/forge/docs/forge.md +650 -647
- package/vendor/skills/forge/phases/brainstorm.md +23 -23
- package/vendor/skills/forge/phases/finish.md +90 -87
- package/vendor/skills/forge/phases/implement.md +77 -77
- package/vendor/skills/forge/phases/plan-openspec.md +60 -60
- package/vendor/skills/forge/phases/plan-specs.md +163 -117
- package/vendor/skills/forge/phases/review.md +25 -25
- package/vendor/skills/forge/phases/verify.md +124 -124
- package/vendor/skills/forge/references/forge-layout.md +85 -85
- package/vendor/skills/forge/references/pace.md +115 -115
- package/vendor/skills/forge/references/plan-routing.md +52 -51
- package/vendor/skills/forge/references/runtime-integrity.md +232 -225
- package/vendor/skills/forge/references/substantial-work.md +37 -37
- package/vendor/skills/forge/references/test-evidence.md +30 -30
- package/vendor/skills/forge/references/test-strategy.md +68 -68
- package/vendor/skills/forge/skills/subagent-driven-development/SKILL.md +87 -87
- package/vendor/skills/forge/subagents/final-reviewer-prompt.md +56 -56
- package/vendor/skills/forge/subagents/implementer-prompt.md +38 -38
- package/vendor/skills/git-resolve-adr-conflict/SKILL.md +132 -132
- package/vendor/skills/thorough-code-review/SKILL.md +290 -290
- package/vendor/skills/thorough-code-review/examples.md +133 -133
- package/vendor/skills/thorough-code-review/reference/accepted-risks.md +26 -26
- package/vendor/skills/thorough-code-review/reference/lenses.md +96 -96
- package/vendor/skills/thorough-code-review/reference/phase1-scout.md +62 -62
- package/vendor/skills/thorough-code-review/reference/phase2-skeptic.md +105 -105
- package/vendor/skills/thorough-code-review/reference/report-schema.json +222 -222
- package/vendor/skills/thorough-code-review/reference/report-template.md +115 -115
- package/vendor/skills/thorough-code-review/reference/severity-rubric.md +49 -49
- package/vendor/skills/thorough-code-review/reference/signals-preflight.md +55 -55
- package/vendor/templates/adr/README.md +7 -7
- package/vendor/templates/adr/decisions.md +141 -141
- package/vendor/templates/adr/hooks/check-pending-adrs.mjs +74 -74
- package/vendor/templates/adr/hooks/check-pending-adrs.sh +3 -3
- package/vendor/templates/adr/hooks/openspec-archive-agent-message.mjs +52 -52
- package/vendor/templates/adr/hooks/openspec-archive-agent-message.sh +3 -3
- package/vendor/templates/project/claude/commands/forge-apply.md +75 -75
- package/vendor/templates/project/claude/commands/forge-brainstorm.md +7 -7
- package/vendor/templates/project/claude/commands/forge-build.md +17 -17
- package/vendor/templates/project/claude/commands/forge-plan.md +12 -12
- package/vendor/templates/project/claude/commands/forge-skip.md +14 -14
- package/vendor/templates/project/claude/commands/forge-status.md +16 -16
- package/vendor/templates/project/claude/commands/forge.md +16 -16
- package/vendor/templates/project/claude/hooks/forge-prompt-hook.mjs +73 -73
- package/vendor/templates/project/claude/hooks/forge-session-start.mjs +19 -19
- package/vendor/templates/project/claude/hooks/forge-triage-hook.mjs +77 -77
- package/vendor/templates/project/cursor/commands/forge-apply.md +75 -75
- package/vendor/templates/project/cursor/commands/forge-brainstorm.md +10 -10
- package/vendor/templates/project/cursor/commands/forge-build.md +17 -17
- package/vendor/templates/project/cursor/commands/forge-plan.md +15 -15
- package/vendor/templates/project/cursor/commands/forge-skip.md +14 -14
- package/vendor/templates/project/cursor/commands/forge-status.md +16 -16
- package/vendor/templates/project/cursor/commands/forge.md +16 -16
- package/vendor/templates/project/cursor/hooks/forge-session-start.mjs +30 -30
- package/vendor/templates/project/cursor/hooks/forge-session-start.sh +3 -3
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
# Architecture decision records (ADRs)
|
|
2
|
-
|
|
3
|
-
> How this project captures **architectural** decisions. ADRs live next to OpenSpec
|
|
4
|
-
> archives. Scaffolded by Forgekit (`forge init --adr` / `forgekit install` with ADRs on).
|
|
5
|
-
|
|
6
|
-
ADR directory for this project: **`{{ADR_DIR}}`** (configured in `.forge/config.json`).
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 1. Why ADRs *and* OpenSpec archives?
|
|
11
|
-
|
|
12
|
-
They overlap in “why we did something,” but they serve different lookups:
|
|
13
|
-
|
|
14
|
-
| OpenSpec archive (verbose) | ADR (short) |
|
|
15
|
-
| -------------------------- | ----------- |
|
|
16
|
-
| `proposal.md`, `design.md`, `tasks.md`, specs | One file, one sharpened decision |
|
|
17
|
-
| “Replay the implementation story” | “Why is X shaped this way?” |
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 2. When to write an ADR
|
|
22
|
-
|
|
23
|
-
| Write an ADR when the change … | Skip the ADR when … |
|
|
24
|
-
| ------------------------------ | ------------------- |
|
|
25
|
-
| Establishes or moves a boundary | Bug fix / copy / docs-only |
|
|
26
|
-
| Chooses among real alternatives | Refactor that changes no decision |
|
|
27
|
-
| Adds a constraint future code must follow | Chore rename without semantics |
|
|
28
|
-
| Picks a costly-to-swap vendor or protocol | Narrow feature inside a prior ADR |
|
|
29
|
-
| Codifies a convention across the repo | Noise would drown readers |
|
|
30
|
-
|
|
31
|
-
If you’re listing code edits, that belongs in `design.md`—not an ADR.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## 3. Format (`{{ADR_DIR}}/NNNN-short-topic.md`)
|
|
36
|
-
|
|
37
|
-
```markdown
|
|
38
|
-
# NNNN. <Short, decision-shaped title>
|
|
39
|
-
|
|
40
|
-
- **Status**: Proposed | Accepted | Superseded by ADR-NNNN (…)
|
|
41
|
-
- **Date**: YYYY-MM-DD
|
|
42
|
-
- **Area**: <product or layer>
|
|
43
|
-
- **Related**: openspec/changes/archive/<change>/, …
|
|
44
|
-
|
|
45
|
-
## Context
|
|
46
|
-
|
|
47
|
-
One paragraph: pressure or constraint forcing a decision.
|
|
48
|
-
|
|
49
|
-
## Decision
|
|
50
|
-
|
|
51
|
-
One imperative paragraph stating what this project commits to.
|
|
52
|
-
|
|
53
|
-
## Alternatives considered
|
|
54
|
-
|
|
55
|
-
- **Option A** — rejected because …
|
|
56
|
-
- **Chosen** — …
|
|
57
|
-
|
|
58
|
-
## Consequences
|
|
59
|
-
|
|
60
|
-
### Positive
|
|
61
|
-
### Negative
|
|
62
|
-
### Neutral
|
|
63
|
-
|
|
64
|
-
## References
|
|
65
|
-
|
|
66
|
-
- Archive: openspec/changes/archive/<name>/
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Keep ADRs terse (roughly tens to low hundreds of lines each).
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## 4. Numbering
|
|
74
|
-
|
|
75
|
-
- Sequential `NNNN`; never recycle numbers—supersede with a **new** ADR.
|
|
76
|
-
- Prefer decision-shaped filenames: `0004-outbox-only-on-critical-path.md` not `0004-add-worker.md`.
|
|
77
|
-
|
|
78
|
-
All ADRs sit under **`{{ADR_DIR}}/`** even when scoped to one area—the decision
|
|
79
|
-
usually crosses layers.
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## 5. Status index (`{{ADR_DIR}}/README.md`)
|
|
84
|
-
|
|
85
|
-
Maintain the table of `#`, Title, Status, Date. Leave placeholder rows until real ADRs land.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## 6. Cross-link OpenSpec ↔ ADRs
|
|
90
|
-
|
|
91
|
-
ADR **References** should point at the dated archive folder.
|
|
92
|
-
|
|
93
|
-
Archived `proposal.md` should gain:
|
|
94
|
-
|
|
95
|
-
```markdown
|
|
96
|
-
## Decision record
|
|
97
|
-
|
|
98
|
-
This change is recorded as ADR-NNNN ({{ADR_DIR}}/NNNN-short-topic.md).
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Or, for non-architectural archives, the one-line stamp:
|
|
102
|
-
|
|
103
|
-
```text
|
|
104
|
-
No ADR — non-architectural change
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Capability specs may cite ADRs as normative “why.”
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## 7. ADRs in agent retrieval
|
|
112
|
-
|
|
113
|
-
Short, focused ADRs embed well. **Do not** add `{{ADR_DIR}}/` to
|
|
114
|
-
`.cursorindexignore` (or equivalent).
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## 7a. Hooks: archive → ADR
|
|
119
|
-
|
|
120
|
-
When ADR wiring is enabled, archive hooks remind agents to run **archive-to-adr**
|
|
121
|
-
after `openspec archive` or a move into `openspec/changes/archive/YYYY-MM-DD-…`.
|
|
122
|
-
|
|
123
|
-
Session-start backstops list archives whose `proposal.md` lacks `ADR-[0-9]` **and**
|
|
124
|
-
lacks the `No ADR — non-architectural change` stamp.
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## 8. Maintenance habit
|
|
129
|
-
|
|
130
|
-
- On each meaningful archive: decide ADR vs stamp.
|
|
131
|
-
- Periodically skim the index for stale status.
|
|
132
|
-
- Supersede with new ADRs—don’t delete old ones.
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
## 9. TL;DR
|
|
137
|
-
|
|
138
|
-
- ADRs **distill** decisions; OpenSpec archives keep the **full** story.
|
|
139
|
-
- Use this file + `{{ADR_DIR}}/README.md` + `{{ADR_DIR}}/NNNN-*.md`.
|
|
140
|
-
- Cross-link archives and ADRs both directions.
|
|
141
|
-
- After archive: run **archive-to-adr** (or stamp “No ADR”).
|
|
1
|
+
# Architecture decision records (ADRs)
|
|
2
|
+
|
|
3
|
+
> How this project captures **architectural** decisions. ADRs live next to OpenSpec
|
|
4
|
+
> archives. Scaffolded by Forgekit (`forge init --adr` / `forgekit install` with ADRs on).
|
|
5
|
+
|
|
6
|
+
ADR directory for this project: **`{{ADR_DIR}}`** (configured in `.forge/config.json`).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Why ADRs *and* OpenSpec archives?
|
|
11
|
+
|
|
12
|
+
They overlap in “why we did something,” but they serve different lookups:
|
|
13
|
+
|
|
14
|
+
| OpenSpec archive (verbose) | ADR (short) |
|
|
15
|
+
| -------------------------- | ----------- |
|
|
16
|
+
| `proposal.md`, `design.md`, `tasks.md`, specs | One file, one sharpened decision |
|
|
17
|
+
| “Replay the implementation story” | “Why is X shaped this way?” |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. When to write an ADR
|
|
22
|
+
|
|
23
|
+
| Write an ADR when the change … | Skip the ADR when … |
|
|
24
|
+
| ------------------------------ | ------------------- |
|
|
25
|
+
| Establishes or moves a boundary | Bug fix / copy / docs-only |
|
|
26
|
+
| Chooses among real alternatives | Refactor that changes no decision |
|
|
27
|
+
| Adds a constraint future code must follow | Chore rename without semantics |
|
|
28
|
+
| Picks a costly-to-swap vendor or protocol | Narrow feature inside a prior ADR |
|
|
29
|
+
| Codifies a convention across the repo | Noise would drown readers |
|
|
30
|
+
|
|
31
|
+
If you’re listing code edits, that belongs in `design.md`—not an ADR.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 3. Format (`{{ADR_DIR}}/NNNN-short-topic.md`)
|
|
36
|
+
|
|
37
|
+
```markdown
|
|
38
|
+
# NNNN. <Short, decision-shaped title>
|
|
39
|
+
|
|
40
|
+
- **Status**: Proposed | Accepted | Superseded by ADR-NNNN (…)
|
|
41
|
+
- **Date**: YYYY-MM-DD
|
|
42
|
+
- **Area**: <product or layer>
|
|
43
|
+
- **Related**: openspec/changes/archive/<change>/, …
|
|
44
|
+
|
|
45
|
+
## Context
|
|
46
|
+
|
|
47
|
+
One paragraph: pressure or constraint forcing a decision.
|
|
48
|
+
|
|
49
|
+
## Decision
|
|
50
|
+
|
|
51
|
+
One imperative paragraph stating what this project commits to.
|
|
52
|
+
|
|
53
|
+
## Alternatives considered
|
|
54
|
+
|
|
55
|
+
- **Option A** — rejected because …
|
|
56
|
+
- **Chosen** — …
|
|
57
|
+
|
|
58
|
+
## Consequences
|
|
59
|
+
|
|
60
|
+
### Positive
|
|
61
|
+
### Negative
|
|
62
|
+
### Neutral
|
|
63
|
+
|
|
64
|
+
## References
|
|
65
|
+
|
|
66
|
+
- Archive: openspec/changes/archive/<name>/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Keep ADRs terse (roughly tens to low hundreds of lines each).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 4. Numbering
|
|
74
|
+
|
|
75
|
+
- Sequential `NNNN`; never recycle numbers—supersede with a **new** ADR.
|
|
76
|
+
- Prefer decision-shaped filenames: `0004-outbox-only-on-critical-path.md` not `0004-add-worker.md`.
|
|
77
|
+
|
|
78
|
+
All ADRs sit under **`{{ADR_DIR}}/`** even when scoped to one area—the decision
|
|
79
|
+
usually crosses layers.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 5. Status index (`{{ADR_DIR}}/README.md`)
|
|
84
|
+
|
|
85
|
+
Maintain the table of `#`, Title, Status, Date. Leave placeholder rows until real ADRs land.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 6. Cross-link OpenSpec ↔ ADRs
|
|
90
|
+
|
|
91
|
+
ADR **References** should point at the dated archive folder.
|
|
92
|
+
|
|
93
|
+
Archived `proposal.md` should gain:
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
## Decision record
|
|
97
|
+
|
|
98
|
+
This change is recorded as ADR-NNNN ({{ADR_DIR}}/NNNN-short-topic.md).
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or, for non-architectural archives, the one-line stamp:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
No ADR — non-architectural change
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Capability specs may cite ADRs as normative “why.”
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 7. ADRs in agent retrieval
|
|
112
|
+
|
|
113
|
+
Short, focused ADRs embed well. **Do not** add `{{ADR_DIR}}/` to
|
|
114
|
+
`.cursorindexignore` (or equivalent).
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 7a. Hooks: archive → ADR
|
|
119
|
+
|
|
120
|
+
When ADR wiring is enabled, archive hooks remind agents to run **archive-to-adr**
|
|
121
|
+
after `openspec archive` or a move into `openspec/changes/archive/YYYY-MM-DD-…`.
|
|
122
|
+
|
|
123
|
+
Session-start backstops list archives whose `proposal.md` lacks `ADR-[0-9]` **and**
|
|
124
|
+
lacks the `No ADR — non-architectural change` stamp.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 8. Maintenance habit
|
|
129
|
+
|
|
130
|
+
- On each meaningful archive: decide ADR vs stamp.
|
|
131
|
+
- Periodically skim the index for stale status.
|
|
132
|
+
- Supersede with new ADRs—don’t delete old ones.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 9. TL;DR
|
|
137
|
+
|
|
138
|
+
- ADRs **distill** decisions; OpenSpec archives keep the **full** story.
|
|
139
|
+
- Use this file + `{{ADR_DIR}}/README.md` + `{{ADR_DIR}}/NNNN-*.md`.
|
|
140
|
+
- Cross-link archives and ADRs both directions.
|
|
141
|
+
- After archive: run **archive-to-adr** (or stamp “No ADR”).
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Pending-ADR backstop: list archived proposals missing ADR link and No-ADR stamp.
|
|
4
|
-
* Honors .forge/config.json adr.enabled === false.
|
|
5
|
-
*
|
|
6
|
-
* Usage: node check-pending-adrs.mjs [repoRoot]
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { spawnSync } from 'node:child_process';
|
|
12
|
-
|
|
13
|
-
function gitRoot() {
|
|
14
|
-
const r = spawnSync('git', ['rev-parse', '--show-toplevel'], {
|
|
15
|
-
encoding: 'utf8',
|
|
16
|
-
shell: process.platform === 'win32',
|
|
17
|
-
});
|
|
18
|
-
if (r.status !== 0) return null;
|
|
19
|
-
return String(r.stdout || '').trim() || null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function loadConfig(cwd) {
|
|
23
|
-
const p = path.join(cwd, '.forge', 'config.json');
|
|
24
|
-
if (!fs.existsSync(p)) return {};
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
27
|
-
} catch {
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function listProposals(cwd, archiveRoot) {
|
|
33
|
-
const dir = path.join(cwd, ...archiveRoot.split('/'));
|
|
34
|
-
if (!fs.existsSync(dir)) return [];
|
|
35
|
-
/** @type {string[]} */
|
|
36
|
-
const out = [];
|
|
37
|
-
for (const name of fs.readdirSync(dir)) {
|
|
38
|
-
const proposal = path.join(dir, name, 'proposal.md');
|
|
39
|
-
if (fs.existsSync(proposal)) out.push(proposal);
|
|
40
|
-
}
|
|
41
|
-
return out;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const cwd = process.argv[2] || gitRoot();
|
|
45
|
-
if (!cwd) process.exit(0);
|
|
46
|
-
|
|
47
|
-
const cfg = loadConfig(cwd);
|
|
48
|
-
if (cfg.adr && cfg.adr.enabled === false) process.exit(0);
|
|
49
|
-
|
|
50
|
-
const archives = [
|
|
51
|
-
'openspec/changes/archive',
|
|
52
|
-
`${(cfg.plan && typeof cfg.plan === 'object' && cfg.plan.dir) || 'specs'}/changes/archive`,
|
|
53
|
-
];
|
|
54
|
-
/** @type {string[]} */
|
|
55
|
-
const pending = [];
|
|
56
|
-
for (const root of archives) {
|
|
57
|
-
for (const proposal of listProposals(cwd, root)) {
|
|
58
|
-
const body = fs.readFileSync(proposal, 'utf8');
|
|
59
|
-
if (/ADR-[0-9]/.test(body)) continue;
|
|
60
|
-
if (body.includes('No ADR — non-architectural change')) continue;
|
|
61
|
-
pending.push(path.relative(cwd, proposal).replace(/\\/g, '/'));
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (pending.length === 0) process.exit(0);
|
|
66
|
-
|
|
67
|
-
const shown = pending.slice(0, 10).join('\n');
|
|
68
|
-
process.stdout.write(
|
|
69
|
-
`Archived changes whose proposal.md does not reference an ADR yet:\n\n${shown}\n\n` +
|
|
70
|
-
`If any had architectural impact, run the archive-to-adr skill.\n` +
|
|
71
|
-
`Otherwise add a one-line stamp to each proposal.md:\n` +
|
|
72
|
-
` 'No ADR — non-architectural change'\n`,
|
|
73
|
-
);
|
|
74
|
-
process.exit(0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Pending-ADR backstop: list archived proposals missing ADR link and No-ADR stamp.
|
|
4
|
+
* Honors .forge/config.json adr.enabled === false.
|
|
5
|
+
*
|
|
6
|
+
* Usage: node check-pending-adrs.mjs [repoRoot]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
|
|
13
|
+
function gitRoot() {
|
|
14
|
+
const r = spawnSync('git', ['rev-parse', '--show-toplevel'], {
|
|
15
|
+
encoding: 'utf8',
|
|
16
|
+
shell: process.platform === 'win32',
|
|
17
|
+
});
|
|
18
|
+
if (r.status !== 0) return null;
|
|
19
|
+
return String(r.stdout || '').trim() || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function loadConfig(cwd) {
|
|
23
|
+
const p = path.join(cwd, '.forge', 'config.json');
|
|
24
|
+
if (!fs.existsSync(p)) return {};
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
27
|
+
} catch {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function listProposals(cwd, archiveRoot) {
|
|
33
|
+
const dir = path.join(cwd, ...archiveRoot.split('/'));
|
|
34
|
+
if (!fs.existsSync(dir)) return [];
|
|
35
|
+
/** @type {string[]} */
|
|
36
|
+
const out = [];
|
|
37
|
+
for (const name of fs.readdirSync(dir)) {
|
|
38
|
+
const proposal = path.join(dir, name, 'proposal.md');
|
|
39
|
+
if (fs.existsSync(proposal)) out.push(proposal);
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const cwd = process.argv[2] || gitRoot();
|
|
45
|
+
if (!cwd) process.exit(0);
|
|
46
|
+
|
|
47
|
+
const cfg = loadConfig(cwd);
|
|
48
|
+
if (cfg.adr && cfg.adr.enabled === false) process.exit(0);
|
|
49
|
+
|
|
50
|
+
const archives = [
|
|
51
|
+
'openspec/changes/archive',
|
|
52
|
+
`${(cfg.plan && typeof cfg.plan === 'object' && cfg.plan.dir) || 'specs'}/changes/archive`,
|
|
53
|
+
];
|
|
54
|
+
/** @type {string[]} */
|
|
55
|
+
const pending = [];
|
|
56
|
+
for (const root of archives) {
|
|
57
|
+
for (const proposal of listProposals(cwd, root)) {
|
|
58
|
+
const body = fs.readFileSync(proposal, 'utf8');
|
|
59
|
+
if (/ADR-[0-9]/.test(body)) continue;
|
|
60
|
+
if (body.includes('No ADR — non-architectural change')) continue;
|
|
61
|
+
pending.push(path.relative(cwd, proposal).replace(/\\/g, '/'));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (pending.length === 0) process.exit(0);
|
|
66
|
+
|
|
67
|
+
const shown = pending.slice(0, 10).join('\n');
|
|
68
|
+
process.stdout.write(
|
|
69
|
+
`Archived changes whose proposal.md does not reference an ADR yet:\n\n${shown}\n\n` +
|
|
70
|
+
`If any had architectural impact, run the archive-to-adr skill.\n` +
|
|
71
|
+
`Otherwise add a one-line stamp to each proposal.md:\n` +
|
|
72
|
+
` 'No ADR — non-architectural change'\n`,
|
|
73
|
+
);
|
|
74
|
+
process.exit(0);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Thin wrapper around the Node implementation.
|
|
3
|
-
exec node "$(dirname "$0")/check-pending-adrs.mjs" "$@"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Thin wrapper around the Node implementation.
|
|
3
|
+
exec node "$(dirname "$0")/check-pending-adrs.mjs" "$@"
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Agent reminder after a successful change archive (stdout only).
|
|
4
|
-
* Usage: node openspec-archive-agent-message.mjs <change-slug>
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import fs from 'node:fs';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
|
|
10
|
-
const changeName = process.argv[2];
|
|
11
|
-
if (!changeName) {
|
|
12
|
-
process.stderr.write('Usage: openspec-archive-agent-message.mjs <change-slug>\n');
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let adrDir = 'docs/adr';
|
|
17
|
-
let decisionsDoc = 'docs/decisions.md';
|
|
18
|
-
let archiveRoot = 'openspec/changes/archive';
|
|
19
|
-
let adrEnabled = true;
|
|
20
|
-
|
|
21
|
-
const cfgPath = path.join(process.cwd(), '.forge', 'config.json');
|
|
22
|
-
if (fs.existsSync(cfgPath)) {
|
|
23
|
-
try {
|
|
24
|
-
const c = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
25
|
-
if (c.plan?.engine === 'specs') {
|
|
26
|
-
archiveRoot = `${c.plan.dir || 'specs'}/changes/archive`;
|
|
27
|
-
}
|
|
28
|
-
if (c.adr?.enabled === false) adrEnabled = false;
|
|
29
|
-
if (c.adr?.dir) adrDir = c.adr.dir;
|
|
30
|
-
if (c.adr?.decisionsDoc) decisionsDoc = c.adr.decisionsDoc;
|
|
31
|
-
} catch {
|
|
32
|
-
// keep defaults
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!adrEnabled) {
|
|
37
|
-
process.stdout.write(
|
|
38
|
-
`Change '${changeName}' was archived. ADRs are disabled for this project\n` +
|
|
39
|
-
`(\`.forge/config.json\` → adr.enabled: false). No archive-to-adr follow-up.\n\n` +
|
|
40
|
-
`Suggested commit (display only — do NOT commit unless the user asks):\n\n` +
|
|
41
|
-
`openspec: archive ${changeName}\n`,
|
|
42
|
-
);
|
|
43
|
-
process.exit(0);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
process.stdout.write(
|
|
47
|
-
`Change '${changeName}' was just archived.\n\n` +
|
|
48
|
-
`1. archive-to-adr: locate ${archiveRoot}/YYYY-MM-DD-${changeName}/, open proposal.md, and evaluate an ADR (${decisionsDoc}). If yes: ${adrDir}/NNNN-<topic>.md, update ${adrDir}/README.md, add a ## Decision record to proposal.md. If no: add one line to proposal.md: No ADR — non-architectural change.\n\n` +
|
|
49
|
-
`2. Suggested commit (display only — do NOT run git commit / push / gh pr create unless the user explicitly asks):\n\n` +
|
|
50
|
-
`openspec: archive ${changeName}\n`,
|
|
51
|
-
);
|
|
52
|
-
process.exit(0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent reminder after a successful change archive (stdout only).
|
|
4
|
+
* Usage: node openspec-archive-agent-message.mjs <change-slug>
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
|
|
10
|
+
const changeName = process.argv[2];
|
|
11
|
+
if (!changeName) {
|
|
12
|
+
process.stderr.write('Usage: openspec-archive-agent-message.mjs <change-slug>\n');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let adrDir = 'docs/adr';
|
|
17
|
+
let decisionsDoc = 'docs/decisions.md';
|
|
18
|
+
let archiveRoot = 'openspec/changes/archive';
|
|
19
|
+
let adrEnabled = true;
|
|
20
|
+
|
|
21
|
+
const cfgPath = path.join(process.cwd(), '.forge', 'config.json');
|
|
22
|
+
if (fs.existsSync(cfgPath)) {
|
|
23
|
+
try {
|
|
24
|
+
const c = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
25
|
+
if (c.plan?.engine === 'specs') {
|
|
26
|
+
archiveRoot = `${c.plan.dir || 'specs'}/changes/archive`;
|
|
27
|
+
}
|
|
28
|
+
if (c.adr?.enabled === false) adrEnabled = false;
|
|
29
|
+
if (c.adr?.dir) adrDir = c.adr.dir;
|
|
30
|
+
if (c.adr?.decisionsDoc) decisionsDoc = c.adr.decisionsDoc;
|
|
31
|
+
} catch {
|
|
32
|
+
// keep defaults
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!adrEnabled) {
|
|
37
|
+
process.stdout.write(
|
|
38
|
+
`Change '${changeName}' was archived. ADRs are disabled for this project\n` +
|
|
39
|
+
`(\`.forge/config.json\` → adr.enabled: false). No archive-to-adr follow-up.\n\n` +
|
|
40
|
+
`Suggested commit (display only — do NOT commit unless the user asks):\n\n` +
|
|
41
|
+
`openspec: archive ${changeName}\n`,
|
|
42
|
+
);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
process.stdout.write(
|
|
47
|
+
`Change '${changeName}' was just archived.\n\n` +
|
|
48
|
+
`1. archive-to-adr: locate ${archiveRoot}/YYYY-MM-DD-${changeName}/, open proposal.md, and evaluate an ADR (${decisionsDoc}). If yes: ${adrDir}/NNNN-<topic>.md, update ${adrDir}/README.md, add a ## Decision record to proposal.md. If no: add one line to proposal.md: No ADR — non-architectural change.\n\n` +
|
|
49
|
+
`2. Suggested commit (display only — do NOT run git commit / push / gh pr create unless the user explicitly asks):\n\n` +
|
|
50
|
+
`openspec: archive ${changeName}\n`,
|
|
51
|
+
);
|
|
52
|
+
process.exit(0);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Thin wrapper around the Node implementation.
|
|
3
|
-
exec node "$(dirname "$0")/openspec-archive-agent-message.mjs" "$@"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Thin wrapper around the Node implementation.
|
|
3
|
+
exec node "$(dirname "$0")/openspec-archive-agent-message.mjs" "$@"
|