@structured-vibe-coding/core 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/defaults/memory/constitution.md +23 -0
- package/defaults/memory/index.md +2 -1
- package/defaults/templates/brainstorm.md +67 -0
- package/defaults/templates/clarify.md +30 -0
- package/defaults/workflows/enterprise-feature.yaml +2 -9
- package/defaults/workflows/feature.yaml +108 -10
- package/defaults/workflows/hotfix.yaml +1 -5
- package/defaults/workflows/patch.yaml +1 -5
- package/defaults/workflows/spike.yaml +1 -1
- package/dist/{agents-TN7TOYF7.js → agents-BA7WVN35.js} +1 -1
- package/dist/{chunk-4ZDPIYEB.js → chunk-M245MC6E.js} +54 -9
- package/dist/index.d.ts +78 -3
- package/dist/index.js +296 -65
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Constitution
|
|
2
|
+
|
|
3
|
+
<!-- Non-negotiables agents must not violate. Broader style lives in conventions.md. -->
|
|
4
|
+
|
|
5
|
+
## Principles
|
|
6
|
+
|
|
7
|
+
-
|
|
8
|
+
|
|
9
|
+
## Stack & tooling
|
|
10
|
+
|
|
11
|
+
-
|
|
12
|
+
|
|
13
|
+
## Testing
|
|
14
|
+
|
|
15
|
+
-
|
|
16
|
+
|
|
17
|
+
## Security
|
|
18
|
+
|
|
19
|
+
-
|
|
20
|
+
|
|
21
|
+
## Process
|
|
22
|
+
|
|
23
|
+
-
|
package/defaults/memory/index.md
CHANGED
|
@@ -6,7 +6,7 @@ Parent page for **stable** project docs. Humans and coding agents: start here, t
|
|
|
6
6
|
|------|--------|----------|
|
|
7
7
|
| **This map + memory** | `memory/*.md` | Long-lived |
|
|
8
8
|
| **Active change** | `changes/<id>/` (see `.sdd/active-context.md`) | Until complete |
|
|
9
|
-
| **Finished work** | `
|
|
9
|
+
| **Finished work** | `changes/<id>/` with `status: completed` | History (pack stays in place; archive is opt-in) |
|
|
10
10
|
| **Optional domains** | `domains/` | Long-lived by area |
|
|
11
11
|
|
|
12
12
|
## Stable docs
|
|
@@ -14,6 +14,7 @@ Parent page for **stable** project docs. Humans and coding agents: start here, t
|
|
|
14
14
|
| Topic | File |
|
|
15
15
|
|-------|------|
|
|
16
16
|
| Product / vision | [product.md](product.md) |
|
|
17
|
+
| Constitution (non-negotiables) | [constitution.md](constitution.md) |
|
|
17
18
|
| Architecture | [architecture.md](architecture.md) |
|
|
18
19
|
| Coding & process conventions | [conventions.md](conventions.md) |
|
|
19
20
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Brainstorm
|
|
2
|
+
|
|
3
|
+
> **{{title}}** · `{{id}}` · stage `{{stage}}`
|
|
4
|
+
> **Optional stage** — skip if one approach is obvious:
|
|
5
|
+
> `sdd skip brainstorm -r "single approach is clear"`
|
|
6
|
+
|
|
7
|
+
## Goal of exploration
|
|
8
|
+
|
|
9
|
+
-
|
|
10
|
+
|
|
11
|
+
## Approach A
|
|
12
|
+
|
|
13
|
+
### Idea
|
|
14
|
+
|
|
15
|
+
-
|
|
16
|
+
|
|
17
|
+
### Pros
|
|
18
|
+
|
|
19
|
+
-
|
|
20
|
+
|
|
21
|
+
### Cons / risks
|
|
22
|
+
|
|
23
|
+
-
|
|
24
|
+
|
|
25
|
+
## Approach B
|
|
26
|
+
|
|
27
|
+
### Idea
|
|
28
|
+
|
|
29
|
+
-
|
|
30
|
+
|
|
31
|
+
### Pros
|
|
32
|
+
|
|
33
|
+
-
|
|
34
|
+
|
|
35
|
+
### Cons / risks
|
|
36
|
+
|
|
37
|
+
-
|
|
38
|
+
|
|
39
|
+
## Approach C (optional)
|
|
40
|
+
|
|
41
|
+
### Idea
|
|
42
|
+
|
|
43
|
+
-
|
|
44
|
+
|
|
45
|
+
### Pros
|
|
46
|
+
|
|
47
|
+
-
|
|
48
|
+
|
|
49
|
+
### Cons / risks
|
|
50
|
+
|
|
51
|
+
-
|
|
52
|
+
|
|
53
|
+
## Comparison
|
|
54
|
+
|
|
55
|
+
| Criterion | A | B | C |
|
|
56
|
+
|-----------|---|---|---|
|
|
57
|
+
| Complexity | | | |
|
|
58
|
+
| Risk | | | |
|
|
59
|
+
| Fit to constraints | | | |
|
|
60
|
+
|
|
61
|
+
## Recommendation for design stage
|
|
62
|
+
|
|
63
|
+
-
|
|
64
|
+
|
|
65
|
+
## Open questions (feed clarify / design)
|
|
66
|
+
|
|
67
|
+
-
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Clarify
|
|
2
|
+
|
|
3
|
+
> **{{title}}** · `{{id}}` · stage `{{stage}}`
|
|
4
|
+
> **Optional stage** — skip if nothing is ambiguous:
|
|
5
|
+
> `sdd skip {{stage}} -r "no open questions"`
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
What just finished? What is about to start?
|
|
10
|
+
|
|
11
|
+
-
|
|
12
|
+
|
|
13
|
+
## Clarifying questions
|
|
14
|
+
|
|
15
|
+
1. **Q:**
|
|
16
|
+
**A:**
|
|
17
|
+
|
|
18
|
+
2. **Q:**
|
|
19
|
+
**A:**
|
|
20
|
+
|
|
21
|
+
3. **Q:**
|
|
22
|
+
**A:**
|
|
23
|
+
|
|
24
|
+
## Decisions / constraints captured
|
|
25
|
+
|
|
26
|
+
-
|
|
27
|
+
|
|
28
|
+
## Ready for next stage?
|
|
29
|
+
|
|
30
|
+
- [ ] No blocking unknowns remain (or explicitly accepted risks)
|
|
@@ -114,10 +114,7 @@ stages:
|
|
|
114
114
|
title: Implementation
|
|
115
115
|
summary: Execute tasks; respect ARB conditions
|
|
116
116
|
skippable: false
|
|
117
|
-
artifacts:
|
|
118
|
-
- id: evidence
|
|
119
|
-
path: evidence/
|
|
120
|
-
required: false
|
|
117
|
+
artifacts: []
|
|
121
118
|
gate:
|
|
122
119
|
type: soft
|
|
123
120
|
checklist:
|
|
@@ -142,9 +139,6 @@ stages:
|
|
|
142
139
|
path: local-test-results.md
|
|
143
140
|
template: local-test-results.md
|
|
144
141
|
required: true
|
|
145
|
-
- id: evidence
|
|
146
|
-
path: evidence/local/
|
|
147
|
-
required: false
|
|
148
142
|
gate:
|
|
149
143
|
type: hard
|
|
150
144
|
overridable: true
|
|
@@ -154,8 +148,7 @@ stages:
|
|
|
154
148
|
- "Failures fixed or filed before complete"
|
|
155
149
|
verify:
|
|
156
150
|
commands: []
|
|
157
|
-
evidence_dir: evidence/local
|
|
158
151
|
|
|
159
152
|
on_complete:
|
|
160
|
-
archive:
|
|
153
|
+
archive: false
|
|
161
154
|
domain_sync: recommend
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
name: feature
|
|
2
|
-
description:
|
|
3
|
-
version:
|
|
2
|
+
description: "Feature — intent, optional clarify/brainstorm, design, tasks, implement, verify"
|
|
3
|
+
version: 2
|
|
4
4
|
recommendation:
|
|
5
5
|
priority: 30
|
|
6
6
|
when:
|
|
@@ -10,6 +10,7 @@ stages:
|
|
|
10
10
|
- id: intent
|
|
11
11
|
title: Feature intent
|
|
12
12
|
summary: Problem, users, scope, non-goals, success
|
|
13
|
+
kind: standard
|
|
13
14
|
skippable: false
|
|
14
15
|
artifacts:
|
|
15
16
|
- id: feature
|
|
@@ -20,10 +21,55 @@ stages:
|
|
|
20
21
|
type: soft
|
|
21
22
|
checklist:
|
|
22
23
|
- "Scope and non-goals are explicit"
|
|
24
|
+
|
|
25
|
+
# Optional: clarifying questions after intent
|
|
26
|
+
- id: clarify_intent
|
|
27
|
+
title: Clarify intent
|
|
28
|
+
summary: Ask clarifying questions before brainstorm/design (optional)
|
|
29
|
+
kind: clarify
|
|
30
|
+
optional: true
|
|
31
|
+
skippable: true
|
|
32
|
+
artifacts:
|
|
33
|
+
- id: clarify-intent
|
|
34
|
+
path: clarify-intent.md
|
|
35
|
+
template: clarify.md
|
|
36
|
+
required: false
|
|
37
|
+
gate:
|
|
38
|
+
type: soft
|
|
39
|
+
agent_context:
|
|
40
|
+
instructions: |
|
|
41
|
+
OPTIONAL clarify stage. If scope is already clear, tell the human to run:
|
|
42
|
+
sdd skip clarify_intent -r "intent is clear"
|
|
43
|
+
Otherwise ask focused clarifying questions (ambiguities, constraints, success metrics).
|
|
44
|
+
Write Q&A into clarify-intent.md. Do not design or implement yet.
|
|
45
|
+
|
|
46
|
+
# Optional: parallel approaches before design
|
|
47
|
+
- id: brainstorm
|
|
48
|
+
title: Brainstorm approaches
|
|
49
|
+
summary: Explore multiple approaches in parallel (optional)
|
|
50
|
+
kind: brainstorm
|
|
51
|
+
optional: true
|
|
52
|
+
skippable: true
|
|
53
|
+
artifacts:
|
|
54
|
+
- id: brainstorm
|
|
55
|
+
path: brainstorm.md
|
|
56
|
+
template: brainstorm.md
|
|
57
|
+
required: false
|
|
58
|
+
gate:
|
|
59
|
+
type: soft
|
|
60
|
+
agent_context:
|
|
61
|
+
instructions: |
|
|
62
|
+
OPTIONAL brainstorm stage. If a single approach is obvious, human may:
|
|
63
|
+
sdd skip brainstorm -r "single approach is clear"
|
|
64
|
+
Otherwise produce 2–4 approaches in brainstorm.md (trade-offs, risks).
|
|
65
|
+
Prefer parallel exploration: outline each approach independently, then compare.
|
|
66
|
+
Do not pick a final design until design stage. Do not implement code yet.
|
|
67
|
+
|
|
23
68
|
- id: design
|
|
24
69
|
title: Design
|
|
25
|
-
summary:
|
|
26
|
-
|
|
70
|
+
summary: Chosen approach, components, risks (required for features)
|
|
71
|
+
kind: standard
|
|
72
|
+
skippable: false
|
|
27
73
|
artifacts:
|
|
28
74
|
- id: design
|
|
29
75
|
path: design.md
|
|
@@ -31,9 +77,37 @@ stages:
|
|
|
31
77
|
required: true
|
|
32
78
|
gate:
|
|
33
79
|
type: soft
|
|
80
|
+
checklist:
|
|
81
|
+
- "Approach chosen and risks noted"
|
|
82
|
+
agent_context:
|
|
83
|
+
instructions: |
|
|
84
|
+
Produce a concrete design in design.md based on feature.md and brainstorm.md (if any).
|
|
85
|
+
Do not leave template stubs empty — substantive content is required before sdd next.
|
|
86
|
+
Do not implement product code yet.
|
|
87
|
+
|
|
88
|
+
- id: clarify_design
|
|
89
|
+
title: Clarify design
|
|
90
|
+
summary: Clarifying questions after design (optional)
|
|
91
|
+
kind: clarify
|
|
92
|
+
optional: true
|
|
93
|
+
skippable: true
|
|
94
|
+
artifacts:
|
|
95
|
+
- id: clarify-design
|
|
96
|
+
path: clarify-design.md
|
|
97
|
+
template: clarify.md
|
|
98
|
+
required: false
|
|
99
|
+
gate:
|
|
100
|
+
type: soft
|
|
101
|
+
agent_context:
|
|
102
|
+
instructions: |
|
|
103
|
+
OPTIONAL. Skip if design is clear:
|
|
104
|
+
sdd skip clarify_design -r "design is clear"
|
|
105
|
+
Otherwise ask questions that unblock tasks/implement. Write answers to clarify-design.md.
|
|
106
|
+
|
|
34
107
|
- id: tasks
|
|
35
108
|
title: Tasks
|
|
36
109
|
summary: Break work into implementable tasks
|
|
110
|
+
kind: standard
|
|
37
111
|
skippable: true
|
|
38
112
|
artifacts:
|
|
39
113
|
- id: tasks
|
|
@@ -42,9 +116,34 @@ stages:
|
|
|
42
116
|
required: true
|
|
43
117
|
gate:
|
|
44
118
|
type: soft
|
|
119
|
+
agent_context:
|
|
120
|
+
instructions: |
|
|
121
|
+
Break design into concrete tasks in tasks.md. Design must already be substantive.
|
|
122
|
+
Do not implement large product code yet.
|
|
123
|
+
|
|
124
|
+
- id: clarify_tasks
|
|
125
|
+
title: Clarify tasks
|
|
126
|
+
summary: Clarifying questions after tasks (optional)
|
|
127
|
+
kind: clarify
|
|
128
|
+
optional: true
|
|
129
|
+
skippable: true
|
|
130
|
+
artifacts:
|
|
131
|
+
- id: clarify-tasks
|
|
132
|
+
path: clarify-tasks.md
|
|
133
|
+
template: clarify.md
|
|
134
|
+
required: false
|
|
135
|
+
gate:
|
|
136
|
+
type: soft
|
|
137
|
+
agent_context:
|
|
138
|
+
instructions: |
|
|
139
|
+
OPTIONAL. Skip if tasks are clear:
|
|
140
|
+
sdd skip clarify_tasks -r "tasks are clear"
|
|
141
|
+
Otherwise resolve ambiguities before implement.
|
|
142
|
+
|
|
45
143
|
- id: implement
|
|
46
144
|
title: Implement
|
|
47
145
|
summary: Build the feature locally
|
|
146
|
+
kind: standard
|
|
48
147
|
skippable: false
|
|
49
148
|
artifacts: []
|
|
50
149
|
gate:
|
|
@@ -52,10 +151,12 @@ stages:
|
|
|
52
151
|
agent_context:
|
|
53
152
|
instructions: |
|
|
54
153
|
Implement tasks from tasks.md following design.md and feature.md.
|
|
55
|
-
Prefer incremental, testable steps.
|
|
154
|
+
Prefer incremental, testable steps. Honor memory/constitution.md if present.
|
|
155
|
+
|
|
56
156
|
- id: local_verify
|
|
57
157
|
title: Local verify
|
|
58
158
|
summary: Verify the feature on this machine
|
|
159
|
+
kind: standard
|
|
59
160
|
skippable: true
|
|
60
161
|
artifacts:
|
|
61
162
|
- id: local-test-plan
|
|
@@ -66,9 +167,6 @@ stages:
|
|
|
66
167
|
path: local-test-results.md
|
|
67
168
|
template: local-test-results.md
|
|
68
169
|
required: true
|
|
69
|
-
- id: evidence
|
|
70
|
-
path: evidence/local/
|
|
71
|
-
required: false
|
|
72
170
|
gate:
|
|
73
171
|
type: soft
|
|
74
172
|
checklist:
|
|
@@ -76,7 +174,7 @@ stages:
|
|
|
76
174
|
- "Key edge cases covered or waived with notes"
|
|
77
175
|
verify:
|
|
78
176
|
commands: []
|
|
79
|
-
|
|
177
|
+
|
|
80
178
|
on_complete:
|
|
81
|
-
archive:
|
|
179
|
+
archive: false
|
|
82
180
|
domain_sync: recommend
|
|
@@ -40,16 +40,12 @@ stages:
|
|
|
40
40
|
path: local-test-results.md
|
|
41
41
|
template: local-test-results.md
|
|
42
42
|
required: false
|
|
43
|
-
- id: evidence
|
|
44
|
-
path: evidence/local/
|
|
45
|
-
required: false
|
|
46
43
|
gate:
|
|
47
44
|
type: soft
|
|
48
45
|
checklist:
|
|
49
46
|
- "Smoke-checked locally"
|
|
50
47
|
verify:
|
|
51
48
|
commands: []
|
|
52
|
-
evidence_dir: evidence/local
|
|
53
49
|
on_complete:
|
|
54
|
-
archive:
|
|
50
|
+
archive: false
|
|
55
51
|
domain_sync: never
|
|
@@ -55,9 +55,6 @@ stages:
|
|
|
55
55
|
path: local-test-results.md
|
|
56
56
|
template: local-test-results.md
|
|
57
57
|
required: false
|
|
58
|
-
- id: evidence
|
|
59
|
-
path: evidence/local/
|
|
60
|
-
required: false
|
|
61
58
|
gate:
|
|
62
59
|
type: soft
|
|
63
60
|
checklist:
|
|
@@ -65,7 +62,6 @@ stages:
|
|
|
65
62
|
- "No obvious regressions on touched paths"
|
|
66
63
|
verify:
|
|
67
64
|
commands: []
|
|
68
|
-
evidence_dir: evidence/local
|
|
69
65
|
on_complete:
|
|
70
|
-
archive:
|
|
66
|
+
archive: false
|
|
71
67
|
domain_sync: never
|
|
@@ -60,6 +60,25 @@ async function copyDir(src, dest) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
+
async function copyDirSkipExisting(src, dest) {
|
|
64
|
+
await ensureDir(dest);
|
|
65
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const from = `${src}/${entry.name}`;
|
|
68
|
+
const to = `${dest}/${entry.name}`;
|
|
69
|
+
if (entry.isDirectory()) {
|
|
70
|
+
await copyDirSkipExisting(from, to);
|
|
71
|
+
} else if (!await pathExists(to)) {
|
|
72
|
+
await ensureDir(dirname(to));
|
|
73
|
+
await fs.copyFile(from, to);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function writeTextIfMissing(path, content) {
|
|
78
|
+
if (await pathExists(path)) return false;
|
|
79
|
+
await writeText(path, content);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
63
82
|
async function removePath(path) {
|
|
64
83
|
await fs.rm(path, { recursive: true, force: true });
|
|
65
84
|
}
|
|
@@ -87,6 +106,14 @@ var StageSchema = z.object({
|
|
|
87
106
|
id: z.string(),
|
|
88
107
|
title: z.string().optional(),
|
|
89
108
|
summary: z.string().optional(),
|
|
109
|
+
/**
|
|
110
|
+
* standard — normal work stage
|
|
111
|
+
* brainstorm — optional multi-approach exploration (before design)
|
|
112
|
+
* clarify — optional Q&A before leaving a major stage
|
|
113
|
+
*/
|
|
114
|
+
kind: z.enum(["standard", "brainstorm", "clarify"]).default("standard"),
|
|
115
|
+
/** When true, stage is optional (skippable); agents may skip if not needed */
|
|
116
|
+
optional: z.boolean().default(false),
|
|
90
117
|
artifacts: z.array(ArtifactDefSchema).default([]),
|
|
91
118
|
gate: GateSchema.default({}),
|
|
92
119
|
skippable: z.boolean().default(true),
|
|
@@ -117,7 +144,7 @@ var WorkflowSchema = z.object({
|
|
|
117
144
|
priority: z.number().default(0)
|
|
118
145
|
}).optional(),
|
|
119
146
|
on_complete: z.object({
|
|
120
|
-
archive: z.boolean().default(
|
|
147
|
+
archive: z.boolean().default(false),
|
|
121
148
|
domain_sync: z.enum(["never", "recommend", "require"]).default("never")
|
|
122
149
|
}).optional()
|
|
123
150
|
});
|
|
@@ -137,7 +164,7 @@ var ConfigSchema = z.object({
|
|
|
137
164
|
}).default({}),
|
|
138
165
|
persistence: z.object({
|
|
139
166
|
default: z.enum(["change_only", "domain_sync"]).default("change_only"),
|
|
140
|
-
archive_on_complete: z.boolean().default(
|
|
167
|
+
archive_on_complete: z.boolean().default(false),
|
|
141
168
|
domain_sync: z.object({
|
|
142
169
|
mode: z.enum(["never", "recommend", "require"]).default("never"),
|
|
143
170
|
anchored_domains: z.array(z.string()).default([])
|
|
@@ -588,6 +615,7 @@ async function buildAgentPrompt(ctx, config, projectRoot) {
|
|
|
588
615
|
}
|
|
589
616
|
parts.push(`## Constraints`);
|
|
590
617
|
parts.push(`- Follow artifacts in the change directory as source of truth.`);
|
|
618
|
+
parts.push(`- Honor \`memory/constitution.md\` when present (non-negotiables).`);
|
|
591
619
|
parts.push(`- Do not skip ARB/decision constraints if present.`);
|
|
592
620
|
parts.push(`- Local development only \u2014 verify on this machine.`);
|
|
593
621
|
parts.push(`- Prefer small, reviewable commits.`);
|
|
@@ -884,7 +912,8 @@ ${role.roleLine}
|
|
|
884
912
|
|
|
885
913
|
1. \`.sdd/active-context.md\` \u2014 current change and stage
|
|
886
914
|
2. \`.sdd/protocol.md\` \u2014 SDD rules for this repo (single playbook)
|
|
887
|
-
3.
|
|
915
|
+
3. \`memory/constitution.md\` if present \u2014 non-negotiables
|
|
916
|
+
4. Active change files under \`changes/<id>/\` as listed in active-context
|
|
888
917
|
|
|
889
918
|
## Role rules
|
|
890
919
|
|
|
@@ -927,14 +956,15 @@ Also read: \`.sdd/active-context.md\`, \`.sdd/protocol.md\`, \`memory/constituti
|
|
|
927
956
|
return outPath;
|
|
928
957
|
}
|
|
929
958
|
function agentKickoffMessage(opts) {
|
|
959
|
+
const event = opts.event ?? "context updated";
|
|
930
960
|
return [
|
|
931
|
-
`SDD:
|
|
961
|
+
`SDD: ${event}.`,
|
|
932
962
|
`Title: ${opts.title}`,
|
|
933
963
|
`Change: ${opts.changeId}`,
|
|
934
964
|
`Stage: ${opts.stage}`,
|
|
935
965
|
`Read in order: .sdd/active-context.md, .sdd/handoff.md, .sdd/protocol.md, memory/constitution.md (if present).`,
|
|
936
|
-
`
|
|
937
|
-
`When the stage is done, tell the human to run: sdd next
|
|
966
|
+
`Do the work for the current stage only under changes/${opts.changeId}/. Do not expand scope.`,
|
|
967
|
+
`When the stage is done, tell the human to run: sdd next (or sdd verify / sdd complete as appropriate).`
|
|
938
968
|
].join(" ");
|
|
939
969
|
}
|
|
940
970
|
async function refreshActiveAgentContext(projectRoot) {
|
|
@@ -991,6 +1021,7 @@ This repo uses **Structured Vibe Coding** (\`sdd\`). Agents must follow this fil
|
|
|
991
1021
|
|------|--------|
|
|
992
1022
|
| Process state | \`changes/<id>/meta.yaml\` (workflow, stage, gates) |
|
|
993
1023
|
| Current task snapshot | \`.sdd/active-context.md\` |
|
|
1024
|
+
| Non-negotiables | \`memory/constitution.md\` if present |
|
|
994
1025
|
| Stable product/tech rules | \`memory/*.md\` (start at \`memory/index.md\` if present) |
|
|
995
1026
|
| Stage artifacts | files in \`changes/<id>/\` for the active change |
|
|
996
1027
|
|
|
@@ -998,8 +1029,9 @@ This repo uses **Structured Vibe Coding** (\`sdd\`). Agents must follow this fil
|
|
|
998
1029
|
|
|
999
1030
|
1. \`.sdd/active-context.md\`
|
|
1000
1031
|
2. This file (\`.sdd/protocol.md\`)
|
|
1001
|
-
3. \`
|
|
1002
|
-
4. \`
|
|
1032
|
+
3. \`memory/constitution.md\` if present (non-negotiables agents must not violate)
|
|
1033
|
+
4. \`meta.yaml\` + artifacts for the **current stage** (and prior stages if needed)
|
|
1034
|
+
5. \`memory/index.md\` (documentation map) if present, then linked memory pages when architecture or conventions matter
|
|
1003
1035
|
|
|
1004
1036
|
## Stage behavior
|
|
1005
1037
|
|
|
@@ -1010,6 +1042,15 @@ This repo uses **Structured Vibe Coding** (\`sdd\`). Agents must follow this fil
|
|
|
1010
1042
|
| tasks | Break work into implementable checklist |
|
|
1011
1043
|
| implement | Code only for this change; follow tasks/acceptance |
|
|
1012
1044
|
| local_verify | Check acceptance; fix only; prepare for complete |
|
|
1045
|
+
| brainstorm (optional) | Multiple approaches; parallel exploration; no final design yet |
|
|
1046
|
+
| clarify_* (optional) | Ask clarifying questions; write Q&A; or tell human to skip |
|
|
1047
|
+
|
|
1048
|
+
## Optional stages
|
|
1049
|
+
|
|
1050
|
+
- Stages marked **optional** / kind \`brainstorm\` or \`clarify\` may be skipped:
|
|
1051
|
+
\`sdd skip <stage_id> -r "not needed"\`
|
|
1052
|
+
- Do **not** invent a skip by jumping stages. \`sdd next\` blocks on empty required stubs (e.g. design.md).
|
|
1053
|
+
- If the tool falls back to an earlier stage, **complete that stage** before advancing again.
|
|
1013
1054
|
|
|
1014
1055
|
## Hard rules
|
|
1015
1056
|
|
|
@@ -1018,6 +1059,7 @@ This repo uses **Structured Vibe Coding** (\`sdd\`). Agents must follow this fil
|
|
|
1018
1059
|
3. **Honor constraints** in \`arb-decision.md\`, design, and memory non-negotiables.
|
|
1019
1060
|
4. **Local verify** is part of done when the workflow has a verify stage: \`sdd verify\`.
|
|
1020
1061
|
5. Prefer small, reviewable diffs.
|
|
1062
|
+
6. **Required stage artifacts must be substantive** \u2014 not empty templates. Design cannot be skipped via empty design.md.
|
|
1021
1063
|
|
|
1022
1064
|
## Commands (human / shell)
|
|
1023
1065
|
|
|
@@ -1033,7 +1075,7 @@ sdd complete
|
|
|
1033
1075
|
## Out of scope for agents
|
|
1034
1076
|
|
|
1035
1077
|
- Replacing CI/CD or team process tools
|
|
1036
|
-
- Deleting \`.sdd\` /
|
|
1078
|
+
- Deleting \`.sdd\` / completed change history
|
|
1037
1079
|
- Claiming complete without verify when required
|
|
1038
1080
|
`;
|
|
1039
1081
|
function renderAgentsMd(integ) {
|
|
@@ -1047,6 +1089,7 @@ AI coding agents are **not** the same as IDEs: VS Code, Cursor, and IntelliJ hos
|
|
|
1047
1089
|
|------------|--|
|
|
1048
1090
|
| Live task | \`.sdd/active-context.md\` |
|
|
1049
1091
|
| Playbook | \`.sdd/protocol.md\` |
|
|
1092
|
+
| Constitution | \`memory/constitution.md\` if present |
|
|
1050
1093
|
| Doc map (stable) | \`memory/index.md\` if present |
|
|
1051
1094
|
|
|
1052
1095
|
| AI agent | Files |
|
|
@@ -1086,6 +1129,8 @@ export {
|
|
|
1086
1129
|
listDirs,
|
|
1087
1130
|
listFiles,
|
|
1088
1131
|
copyDir,
|
|
1132
|
+
copyDirSkipExisting,
|
|
1133
|
+
writeTextIfMissing,
|
|
1089
1134
|
removePath,
|
|
1090
1135
|
isInitialized,
|
|
1091
1136
|
loadConfig,
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,14 @@ declare const StageSchema: z.ZodObject<{
|
|
|
49
49
|
id: z.ZodString;
|
|
50
50
|
title: z.ZodOptional<z.ZodString>;
|
|
51
51
|
summary: z.ZodOptional<z.ZodString>;
|
|
52
|
+
/**
|
|
53
|
+
* standard — normal work stage
|
|
54
|
+
* brainstorm — optional multi-approach exploration (before design)
|
|
55
|
+
* clarify — optional Q&A before leaving a major stage
|
|
56
|
+
*/
|
|
57
|
+
kind: z.ZodDefault<z.ZodEnum<["standard", "brainstorm", "clarify"]>>;
|
|
58
|
+
/** When true, stage is optional (skippable); agents may skip if not needed */
|
|
59
|
+
optional: z.ZodDefault<z.ZodBoolean>;
|
|
52
60
|
artifacts: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
53
61
|
id: z.ZodString;
|
|
54
62
|
path: z.ZodString;
|
|
@@ -131,6 +139,8 @@ declare const StageSchema: z.ZodObject<{
|
|
|
131
139
|
}>>;
|
|
132
140
|
}, "strip", z.ZodTypeAny, {
|
|
133
141
|
id: string;
|
|
142
|
+
kind: "standard" | "brainstorm" | "clarify";
|
|
143
|
+
optional: boolean;
|
|
134
144
|
artifacts: {
|
|
135
145
|
path: string;
|
|
136
146
|
id: string;
|
|
@@ -165,6 +175,8 @@ declare const StageSchema: z.ZodObject<{
|
|
|
165
175
|
id: string;
|
|
166
176
|
title?: string | undefined;
|
|
167
177
|
summary?: string | undefined;
|
|
178
|
+
kind?: "standard" | "brainstorm" | "clarify" | undefined;
|
|
179
|
+
optional?: boolean | undefined;
|
|
168
180
|
artifacts?: {
|
|
169
181
|
path: string;
|
|
170
182
|
id: string;
|
|
@@ -202,6 +214,14 @@ declare const WorkflowSchema: z.ZodObject<{
|
|
|
202
214
|
id: z.ZodString;
|
|
203
215
|
title: z.ZodOptional<z.ZodString>;
|
|
204
216
|
summary: z.ZodOptional<z.ZodString>;
|
|
217
|
+
/**
|
|
218
|
+
* standard — normal work stage
|
|
219
|
+
* brainstorm — optional multi-approach exploration (before design)
|
|
220
|
+
* clarify — optional Q&A before leaving a major stage
|
|
221
|
+
*/
|
|
222
|
+
kind: z.ZodDefault<z.ZodEnum<["standard", "brainstorm", "clarify"]>>;
|
|
223
|
+
/** When true, stage is optional (skippable); agents may skip if not needed */
|
|
224
|
+
optional: z.ZodDefault<z.ZodBoolean>;
|
|
205
225
|
artifacts: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
206
226
|
id: z.ZodString;
|
|
207
227
|
path: z.ZodString;
|
|
@@ -284,6 +304,8 @@ declare const WorkflowSchema: z.ZodObject<{
|
|
|
284
304
|
}>>;
|
|
285
305
|
}, "strip", z.ZodTypeAny, {
|
|
286
306
|
id: string;
|
|
307
|
+
kind: "standard" | "brainstorm" | "clarify";
|
|
308
|
+
optional: boolean;
|
|
287
309
|
artifacts: {
|
|
288
310
|
path: string;
|
|
289
311
|
id: string;
|
|
@@ -318,6 +340,8 @@ declare const WorkflowSchema: z.ZodObject<{
|
|
|
318
340
|
id: string;
|
|
319
341
|
title?: string | undefined;
|
|
320
342
|
summary?: string | undefined;
|
|
343
|
+
kind?: "standard" | "brainstorm" | "clarify" | undefined;
|
|
344
|
+
optional?: boolean | undefined;
|
|
321
345
|
artifacts?: {
|
|
322
346
|
path: string;
|
|
323
347
|
id: string;
|
|
@@ -397,6 +421,8 @@ declare const WorkflowSchema: z.ZodObject<{
|
|
|
397
421
|
version: number;
|
|
398
422
|
stages: {
|
|
399
423
|
id: string;
|
|
424
|
+
kind: "standard" | "brainstorm" | "clarify";
|
|
425
|
+
optional: boolean;
|
|
400
426
|
artifacts: {
|
|
401
427
|
path: string;
|
|
402
428
|
id: string;
|
|
@@ -448,6 +474,8 @@ declare const WorkflowSchema: z.ZodObject<{
|
|
|
448
474
|
id: string;
|
|
449
475
|
title?: string | undefined;
|
|
450
476
|
summary?: string | undefined;
|
|
477
|
+
kind?: "standard" | "brainstorm" | "clarify" | undefined;
|
|
478
|
+
optional?: boolean | undefined;
|
|
451
479
|
artifacts?: {
|
|
452
480
|
path: string;
|
|
453
481
|
id: string;
|
|
@@ -621,6 +649,14 @@ declare const ChangeMetaSchema: z.ZodObject<{
|
|
|
621
649
|
id: z.ZodString;
|
|
622
650
|
title: z.ZodOptional<z.ZodString>;
|
|
623
651
|
summary: z.ZodOptional<z.ZodString>;
|
|
652
|
+
/**
|
|
653
|
+
* standard — normal work stage
|
|
654
|
+
* brainstorm — optional multi-approach exploration (before design)
|
|
655
|
+
* clarify — optional Q&A before leaving a major stage
|
|
656
|
+
*/
|
|
657
|
+
kind: z.ZodDefault<z.ZodEnum<["standard", "brainstorm", "clarify"]>>;
|
|
658
|
+
/** When true, stage is optional (skippable); agents may skip if not needed */
|
|
659
|
+
optional: z.ZodDefault<z.ZodBoolean>;
|
|
624
660
|
artifacts: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
625
661
|
id: z.ZodString;
|
|
626
662
|
path: z.ZodString;
|
|
@@ -703,6 +739,8 @@ declare const ChangeMetaSchema: z.ZodObject<{
|
|
|
703
739
|
}>>;
|
|
704
740
|
}, "strip", z.ZodTypeAny, {
|
|
705
741
|
id: string;
|
|
742
|
+
kind: "standard" | "brainstorm" | "clarify";
|
|
743
|
+
optional: boolean;
|
|
706
744
|
artifacts: {
|
|
707
745
|
path: string;
|
|
708
746
|
id: string;
|
|
@@ -737,6 +775,8 @@ declare const ChangeMetaSchema: z.ZodObject<{
|
|
|
737
775
|
id: string;
|
|
738
776
|
title?: string | undefined;
|
|
739
777
|
summary?: string | undefined;
|
|
778
|
+
kind?: "standard" | "brainstorm" | "clarify" | undefined;
|
|
779
|
+
optional?: boolean | undefined;
|
|
740
780
|
artifacts?: {
|
|
741
781
|
path: string;
|
|
742
782
|
id: string;
|
|
@@ -771,6 +811,8 @@ declare const ChangeMetaSchema: z.ZodObject<{
|
|
|
771
811
|
skip_stages: string[];
|
|
772
812
|
extra_stages: {
|
|
773
813
|
id: string;
|
|
814
|
+
kind: "standard" | "brainstorm" | "clarify";
|
|
815
|
+
optional: boolean;
|
|
774
816
|
artifacts: {
|
|
775
817
|
path: string;
|
|
776
818
|
id: string;
|
|
@@ -809,6 +851,8 @@ declare const ChangeMetaSchema: z.ZodObject<{
|
|
|
809
851
|
id: string;
|
|
810
852
|
title?: string | undefined;
|
|
811
853
|
summary?: string | undefined;
|
|
854
|
+
kind?: "standard" | "brainstorm" | "clarify" | undefined;
|
|
855
|
+
optional?: boolean | undefined;
|
|
812
856
|
artifacts?: {
|
|
813
857
|
path: string;
|
|
814
858
|
id: string;
|
|
@@ -923,6 +967,8 @@ declare const ChangeMetaSchema: z.ZodObject<{
|
|
|
923
967
|
skip_stages: string[];
|
|
924
968
|
extra_stages: {
|
|
925
969
|
id: string;
|
|
970
|
+
kind: "standard" | "brainstorm" | "clarify";
|
|
971
|
+
optional: boolean;
|
|
926
972
|
artifacts: {
|
|
927
973
|
path: string;
|
|
928
974
|
id: string;
|
|
@@ -999,6 +1045,8 @@ declare const ChangeMetaSchema: z.ZodObject<{
|
|
|
999
1045
|
id: string;
|
|
1000
1046
|
title?: string | undefined;
|
|
1001
1047
|
summary?: string | undefined;
|
|
1048
|
+
kind?: "standard" | "brainstorm" | "clarify" | undefined;
|
|
1049
|
+
optional?: boolean | undefined;
|
|
1002
1050
|
artifacts?: {
|
|
1003
1051
|
path: string;
|
|
1004
1052
|
id: string;
|
|
@@ -1120,17 +1168,34 @@ declare function clearActiveIf(projectRoot: string, config: Config, changeId: st
|
|
|
1120
1168
|
declare function setActiveChange(projectRoot: string, config: Config, changeId: string): Promise<void>;
|
|
1121
1169
|
declare function resolveChangeId(projectRoot: string, config: Config, changeId?: string): Promise<string>;
|
|
1122
1170
|
|
|
1171
|
+
/**
|
|
1172
|
+
* Prior non-skipped stages with required work must be complete before leaving
|
|
1173
|
+
* the current stage (prevents jumping intent → tasks with empty design).
|
|
1174
|
+
*/
|
|
1175
|
+
declare function incompletePriorStageErrors(ctx: ChangeContext): Promise<string[]>;
|
|
1123
1176
|
/** Artifact + gate + verify checks before leaving the current stage. */
|
|
1124
1177
|
declare function canLeaveStage(ctx: ChangeContext, config: Config): Promise<{
|
|
1125
1178
|
ok: boolean;
|
|
1126
1179
|
errors: string[];
|
|
1127
1180
|
warnings: string[];
|
|
1128
1181
|
}>;
|
|
1182
|
+
/**
|
|
1183
|
+
* If meta.stage points past incomplete required work, return the earliest
|
|
1184
|
+
* incomplete stage id to fall back to (e.g. design).
|
|
1185
|
+
*/
|
|
1186
|
+
declare function fallbackIncompleteStageId(ctx: ChangeContext): Promise<string | null>;
|
|
1129
1187
|
declare function approveGate(projectRoot: string, config: Config, changeId: string, stageId: string | undefined, note?: string, status?: "approved" | "waived" | "failed"): Promise<ChangeContext>;
|
|
1130
1188
|
|
|
1131
1189
|
declare function formatStatus(ctx: ChangeContext): string;
|
|
1132
1190
|
declare function buildAgentPrompt(ctx: ChangeContext, config: Config, projectRoot: string): Promise<string>;
|
|
1133
1191
|
|
|
1192
|
+
/**
|
|
1193
|
+
* Artifact content quality — existence alone is not "stage complete"
|
|
1194
|
+
* (templates are materialized on stage entry and would otherwise auto-pass).
|
|
1195
|
+
*/
|
|
1196
|
+
/** Strip comments/headings/empty bullets; require real prose. */
|
|
1197
|
+
declare function isSubstantiveArtifactContent(content: string): boolean;
|
|
1198
|
+
|
|
1134
1199
|
interface CreateChangeInput {
|
|
1135
1200
|
projectRoot: string;
|
|
1136
1201
|
config: Config;
|
|
@@ -1276,11 +1341,13 @@ declare function agentKickoffMessage(opts: {
|
|
|
1276
1341
|
title: string;
|
|
1277
1342
|
stage: string;
|
|
1278
1343
|
changeId: string;
|
|
1344
|
+
/** What just happened, e.g. "new change", "advanced to implement" */
|
|
1345
|
+
event?: string;
|
|
1279
1346
|
}): string;
|
|
1280
1347
|
/** Write .sdd/active-context.md for the current change. */
|
|
1281
1348
|
declare function refreshActiveAgentContext(projectRoot: string): Promise<string | null>;
|
|
1282
1349
|
/** Single playbook — only long agent-facing content we author once. */
|
|
1283
|
-
declare const PROTOCOL_MD = "# SDD protocol\n\nThis repo uses **Structured Vibe Coding** (`sdd`). Agents must follow this file plus `.sdd/active-context.md`. Do not invent a parallel process.\n\n## Source of truth\n\n| What | Where |\n|------|--------|\n| Process state | `changes/<id>/meta.yaml` (workflow, stage, gates) |\n| Current task snapshot | `.sdd/active-context.md` |\n| Stable product/tech rules | `memory/*.md` (start at `memory/index.md` if present) |\n| Stage artifacts | files in `changes/<id>/` for the active change |\n\n## Required read order\n\n1. `.sdd/active-context.md`\n2. This file (`.sdd/protocol.md`)\n3. `meta.yaml` + artifacts for the **current stage** (and prior stages if needed)\
|
|
1350
|
+
declare const PROTOCOL_MD = "# SDD protocol\n\nThis repo uses **Structured Vibe Coding** (`sdd`). Agents must follow this file plus `.sdd/active-context.md`. Do not invent a parallel process.\n\n## Source of truth\n\n| What | Where |\n|------|--------|\n| Process state | `changes/<id>/meta.yaml` (workflow, stage, gates) |\n| Current task snapshot | `.sdd/active-context.md` |\n| Non-negotiables | `memory/constitution.md` if present |\n| Stable product/tech rules | `memory/*.md` (start at `memory/index.md` if present) |\n| Stage artifacts | files in `changes/<id>/` for the active change |\n\n## Required read order\n\n1. `.sdd/active-context.md`\n2. This file (`.sdd/protocol.md`)\n3. `memory/constitution.md` if present (non-negotiables agents must not violate)\n4. `meta.yaml` + artifacts for the **current stage** (and prior stages if needed)\n5. `memory/index.md` (documentation map) if present, then linked memory pages when architecture or conventions matter\n\n## Stage behavior\n\n| Stage kind | Agent focus |\n|------------|-------------|\n| intent / feature / stories | Clarify scope; write short artifacts |\n| design / lld / db / research / hl_arb | Specs and decisions; minimal product code |\n| tasks | Break work into implementable checklist |\n| implement | Code only for this change; follow tasks/acceptance |\n| local_verify | Check acceptance; fix only; prepare for complete |\n| brainstorm (optional) | Multiple approaches; parallel exploration; no final design yet |\n| clarify_* (optional) | Ask clarifying questions; write Q&A; or tell human to skip |\n\n## Optional stages\n\n- Stages marked **optional** / kind `brainstorm` or `clarify` may be skipped: \n `sdd skip <stage_id> -r \"not needed\"`\n- Do **not** invent a skip by jumping stages. `sdd next` blocks on empty required stubs (e.g. design.md).\n- If the tool falls back to an earlier stage, **complete that stage** before advancing again.\n\n## Hard rules\n\n1. **Do not skip hard gates.** If blocked, tell the human to run `sdd gate approve` (or fix the gate).\n2. **Stay in scope** of the active change title/intent. No drive-by refactors or new features.\n3. **Honor constraints** in `arb-decision.md`, design, and memory non-negotiables.\n4. **Local verify** is part of done when the workflow has a verify stage: `sdd verify`.\n5. Prefer small, reviewable diffs.\n6. **Required stage artifacts must be substantive** \u2014 not empty templates. Design cannot be skipped via empty design.md.\n\n## Commands (human / shell)\n\n```bash\nsdd status\nsdd next\nsdd agent # print handoff\nsdd agents refresh # refresh active-context.md\nsdd verify\nsdd complete\n```\n\n## Out of scope for agents\n\n- Replacing CI/CD or team process tools\n- Deleting `.sdd` / completed change history\n- Claiming complete without verify when required\n";
|
|
1284
1351
|
|
|
1285
1352
|
interface InitOptions {
|
|
1286
1353
|
projectRoot: string;
|
|
@@ -1311,7 +1378,8 @@ interface VerifyResult {
|
|
|
1311
1378
|
output: string;
|
|
1312
1379
|
}[];
|
|
1313
1380
|
checklist: string[];
|
|
1314
|
-
|
|
1381
|
+
/** Path to evidence dir when configured; null when no evidence_dir is set */
|
|
1382
|
+
evidencePath: string | null;
|
|
1315
1383
|
/** False if any required command failed or was not run when required */
|
|
1316
1384
|
ok: boolean;
|
|
1317
1385
|
}
|
|
@@ -1346,6 +1414,13 @@ declare function writeYaml(path: string, data: unknown): Promise<void>;
|
|
|
1346
1414
|
declare function listDirs(path: string): Promise<string[]>;
|
|
1347
1415
|
declare function listFiles(path: string): Promise<string[]>;
|
|
1348
1416
|
declare function copyDir(src: string, dest: string): Promise<void>;
|
|
1417
|
+
/**
|
|
1418
|
+
* Copy directory tree but never overwrite existing files (stable docs like
|
|
1419
|
+
* memory/constitution.md survive `sdd init --force`).
|
|
1420
|
+
*/
|
|
1421
|
+
declare function copyDirSkipExisting(src: string, dest: string): Promise<void>;
|
|
1422
|
+
/** Write only when the path does not already exist. */
|
|
1423
|
+
declare function writeTextIfMissing(path: string, content: string): Promise<boolean>;
|
|
1349
1424
|
declare function removePath(path: string): Promise<void>;
|
|
1350
1425
|
|
|
1351
|
-
export { AGENT_INTEGRATIONS, AGENT_TARGET_OPTIONS, ALL_AGENT_TARGETS, type AdvanceResult, type AgentIntegration, type AgentTarget, type AgentTargetOption, type ArtifactDef, ArtifactDefSchema, type ChangeContext, type ChangeMeta, ChangeMetaSchema, type Complexity, type Config, ConfigSchema, type CreateChangeInput, DEFAULT_INIT_INTEGRATION, type Gate, GateSchema, type InitOptions, type InitResult, type InstallAgentOptions, type InstallAgentResult, type InstallAgentsOptions, type InstallAgentsResult, type InstalledAgentSnapshot, PROTOCOL_MD, SDD_AGENT_ROLES, SDD_DIR, type SddAgentRole, type SddAgentRoleId, type Stage, StageSchema, VerifyCommandSchema, type VerifyResult, type Workflow, WorkflowSchema, activePointerPath, activeStages, advanceStage, agentHostPaths, agentKickoffMessage, approveGate, archiveDir, buildAgentPrompt, buildContext, canLeaveStage, changeDirName, changeMetaPath, changePath, changesDir, clearActiveIf, completeChange, configPath, copyDir, createChange, defaultConfig, defaultMemoryDir, defaultTemplatesDir, defaultWorkflowsDir, defaultsRoot, domainsDir, ensureDir, firstActiveStageId, formatStatus, getActiveChangeId, getIntegration, getStage, inferComplexity, initProject, installAgentIntegration, installAgentIntegrations, integrationKeyFor, isInitialized, isStageSkipped, listChanges, listDirs, listFiles, listWorkflowNames, loadChangeMeta, loadConfig, loadInstalledAgent, loadWorkflow, memoryDir, nextStageId, nowIso, optionForTarget, parseAgentTargets, parseIntegration, pathExists, readText, readYaml, recommendWorkflow, refreshActiveAgentContext, removeOtherAgentHosts, removePath, renderThinAgent, resolveChangeId, resolveStages, runLocalVerify, saveChangeMeta, saveConfig, sddRoot, setActiveChange, shouldAutoSkip, skipStage, slugify, switchWorkflow, templatesDir, todayIsoDate, uniqueChangeDirName, workflowsDir, writeAgentHandoff, writeText, writeYaml };
|
|
1426
|
+
export { AGENT_INTEGRATIONS, AGENT_TARGET_OPTIONS, ALL_AGENT_TARGETS, type AdvanceResult, type AgentIntegration, type AgentTarget, type AgentTargetOption, type ArtifactDef, ArtifactDefSchema, type ChangeContext, type ChangeMeta, ChangeMetaSchema, type Complexity, type Config, ConfigSchema, type CreateChangeInput, DEFAULT_INIT_INTEGRATION, type Gate, GateSchema, type InitOptions, type InitResult, type InstallAgentOptions, type InstallAgentResult, type InstallAgentsOptions, type InstallAgentsResult, type InstalledAgentSnapshot, PROTOCOL_MD, SDD_AGENT_ROLES, SDD_DIR, type SddAgentRole, type SddAgentRoleId, type Stage, StageSchema, VerifyCommandSchema, type VerifyResult, type Workflow, WorkflowSchema, activePointerPath, activeStages, advanceStage, agentHostPaths, agentKickoffMessage, approveGate, archiveDir, buildAgentPrompt, buildContext, canLeaveStage, changeDirName, changeMetaPath, changePath, changesDir, clearActiveIf, completeChange, configPath, copyDir, copyDirSkipExisting, createChange, defaultConfig, defaultMemoryDir, defaultTemplatesDir, defaultWorkflowsDir, defaultsRoot, domainsDir, ensureDir, fallbackIncompleteStageId, firstActiveStageId, formatStatus, getActiveChangeId, getIntegration, getStage, incompletePriorStageErrors, inferComplexity, initProject, installAgentIntegration, installAgentIntegrations, integrationKeyFor, isInitialized, isStageSkipped, isSubstantiveArtifactContent, listChanges, listDirs, listFiles, listWorkflowNames, loadChangeMeta, loadConfig, loadInstalledAgent, loadWorkflow, memoryDir, nextStageId, nowIso, optionForTarget, parseAgentTargets, parseIntegration, pathExists, readText, readYaml, recommendWorkflow, refreshActiveAgentContext, removeOtherAgentHosts, removePath, renderThinAgent, resolveChangeId, resolveStages, runLocalVerify, saveChangeMeta, saveConfig, sddRoot, setActiveChange, shouldAutoSkip, skipStage, slugify, switchWorkflow, templatesDir, todayIsoDate, uniqueChangeDirName, workflowsDir, writeAgentHandoff, writeText, writeTextIfMissing, writeYaml };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
clearActiveIf,
|
|
28
28
|
configPath,
|
|
29
29
|
copyDir,
|
|
30
|
+
copyDirSkipExisting,
|
|
30
31
|
defaultConfig,
|
|
31
32
|
domainsDir,
|
|
32
33
|
ensureDir,
|
|
@@ -77,14 +78,84 @@ import {
|
|
|
77
78
|
workflowsDir,
|
|
78
79
|
writeAgentHandoff,
|
|
79
80
|
writeText,
|
|
81
|
+
writeTextIfMissing,
|
|
80
82
|
writeYaml
|
|
81
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-M245MC6E.js";
|
|
82
84
|
|
|
83
85
|
// src/change.ts
|
|
84
86
|
import { join as join2 } from "pathe";
|
|
85
87
|
|
|
86
88
|
// src/stage-gates.ts
|
|
87
89
|
import { join } from "pathe";
|
|
90
|
+
|
|
91
|
+
// src/artifacts.ts
|
|
92
|
+
function isSubstantiveArtifactContent(content) {
|
|
93
|
+
const stripped = content.replace(/<!--[\s\S]*?-->/g, "").replace(/^#{1,6}\s+.*$/gm, "").replace(/^>\s.*$/gm, "").replace(/^\s*[-*+]\s*$/gm, "").replace(/^\s*[-*+]\s*\.\.\.\s*$/gm, "").replace(/^\s*[-*+]\s*TBD\s*$/gim, "").replace(/^\s*[-*+]\s*TODO\s*$/gim, "").replace(/^\s*\|.*\|\s*$/gm, "").replace(/`+/g, "").trim();
|
|
94
|
+
if (stripped.length < 48) return false;
|
|
95
|
+
const meaningful = stripped.replace(/[-–—_.\s]/g, "").replace(/\b(TBD|TODO|N\/A|none|n\/a)\b/gi, "");
|
|
96
|
+
return meaningful.length >= 32;
|
|
97
|
+
}
|
|
98
|
+
function incompleteArtifactReason(path, content) {
|
|
99
|
+
if (content == null) {
|
|
100
|
+
return `Missing required artifact: ${path}`;
|
|
101
|
+
}
|
|
102
|
+
if (!isSubstantiveArtifactContent(content)) {
|
|
103
|
+
return `Required artifact still incomplete (looks like empty template/stub): ${path}. Fill it with real content, or skip this stage with \`sdd skip <stage> -r "\u2026"\` if optional.`;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/stage-gates.ts
|
|
109
|
+
async function checkRequiredArtifacts(changePath2, stageId, artifacts) {
|
|
110
|
+
const errors = [];
|
|
111
|
+
for (const artifact of artifacts) {
|
|
112
|
+
if (!artifact.required) continue;
|
|
113
|
+
if (artifact.path.endsWith("/")) {
|
|
114
|
+
const target2 = join(changePath2, artifact.path);
|
|
115
|
+
if (!await pathExists(target2)) {
|
|
116
|
+
errors.push(`Missing required directory: ${artifact.path} (stage ${stageId})`);
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const target = join(changePath2, artifact.path);
|
|
121
|
+
if (!await pathExists(target)) {
|
|
122
|
+
errors.push(incompleteArtifactReason(artifact.path, null));
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const content = await readText(target);
|
|
126
|
+
const reason = incompleteArtifactReason(artifact.path, content);
|
|
127
|
+
if (reason) {
|
|
128
|
+
errors.push(`${reason} (stage ${stageId})`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return errors;
|
|
132
|
+
}
|
|
133
|
+
async function incompletePriorStageErrors(ctx) {
|
|
134
|
+
const errors = [];
|
|
135
|
+
const stages = activeStages(ctx.workflow, ctx.meta);
|
|
136
|
+
const currentIdx = stages.findIndex((s) => s.id === ctx.meta.stage);
|
|
137
|
+
if (currentIdx <= 0) return errors;
|
|
138
|
+
for (let i = 0; i < currentIdx; i++) {
|
|
139
|
+
const stage = stages[i];
|
|
140
|
+
if (isStageSkipped(ctx.meta, stage.id) || shouldAutoSkip(stage, ctx.meta)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const required = stage.artifacts.filter((a) => a.required);
|
|
144
|
+
if (!required.length && stage.optional) continue;
|
|
145
|
+
const stageErrors = await checkRequiredArtifacts(
|
|
146
|
+
ctx.path,
|
|
147
|
+
stage.id,
|
|
148
|
+
stage.artifacts
|
|
149
|
+
);
|
|
150
|
+
if (stageErrors.length) {
|
|
151
|
+
errors.push(
|
|
152
|
+
`Prior stage "${stage.id}" is not complete \u2014 finish it or \`sdd skip ${stage.id} -r "\u2026"\` if optional.`
|
|
153
|
+
);
|
|
154
|
+
errors.push(...stageErrors);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return errors;
|
|
158
|
+
}
|
|
88
159
|
async function canLeaveStage(ctx, config) {
|
|
89
160
|
const stage = getStage(ctx.workflow, ctx.meta, ctx.meta.stage);
|
|
90
161
|
const errors = [];
|
|
@@ -92,13 +163,10 @@ async function canLeaveStage(ctx, config) {
|
|
|
92
163
|
if (!stage) {
|
|
93
164
|
return { ok: false, errors: [`Unknown stage: ${ctx.meta.stage}`], warnings };
|
|
94
165
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
errors.push(`Missing required artifact: ${artifact.path}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
166
|
+
errors.push(...await incompletePriorStageErrors(ctx));
|
|
167
|
+
errors.push(
|
|
168
|
+
...await checkRequiredArtifacts(ctx.path, stage.id, stage.artifacts)
|
|
169
|
+
);
|
|
102
170
|
const declared = stage.gate?.type ?? "soft";
|
|
103
171
|
let effectiveGate = declared;
|
|
104
172
|
if (config.policy.gates === "hard" && declared === "soft") {
|
|
@@ -132,8 +200,27 @@ async function canLeaveStage(ctx, config) {
|
|
|
132
200
|
);
|
|
133
201
|
}
|
|
134
202
|
}
|
|
203
|
+
if (stage.optional || stage.kind === "clarify" || stage.kind === "brainstorm") {
|
|
204
|
+
warnings.push(
|
|
205
|
+
`Stage "${stage.id}" is optional (${stage.kind ?? "optional"}). Skip with: sdd skip ${stage.id} -r "not needed"`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
135
208
|
return { ok: errors.length === 0, errors, warnings };
|
|
136
209
|
}
|
|
210
|
+
async function fallbackIncompleteStageId(ctx) {
|
|
211
|
+
const stages = activeStages(ctx.workflow, ctx.meta);
|
|
212
|
+
for (const stage of stages) {
|
|
213
|
+
if (stage.id === ctx.meta.stage) break;
|
|
214
|
+
if (isStageSkipped(ctx.meta, stage.id) || shouldAutoSkip(stage, ctx.meta)) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const required = stage.artifacts.filter((a) => a.required);
|
|
218
|
+
if (!required.length && stage.optional) continue;
|
|
219
|
+
const errs = await checkRequiredArtifacts(ctx.path, stage.id, stage.artifacts);
|
|
220
|
+
if (errs.length) return stage.id;
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
137
224
|
async function approveGate(projectRoot, config, changeId, stageId, note, status = "approved") {
|
|
138
225
|
const ctx = await buildContext(projectRoot, config, changeId);
|
|
139
226
|
const id = stageId ?? ctx.meta.stage;
|
|
@@ -190,7 +277,7 @@ async function createChange(input) {
|
|
|
190
277
|
await materializeStageArtifacts(projectRoot, config, id, workflow, meta, firstStage);
|
|
191
278
|
await setActiveChange(projectRoot, config, id);
|
|
192
279
|
try {
|
|
193
|
-
const { refreshActiveAgentContext: refreshActiveAgentContext2 } = await import("./agents-
|
|
280
|
+
const { refreshActiveAgentContext: refreshActiveAgentContext2 } = await import("./agents-BA7WVN35.js");
|
|
194
281
|
await refreshActiveAgentContext2(projectRoot);
|
|
195
282
|
} catch {
|
|
196
283
|
}
|
|
@@ -252,11 +339,45 @@ function defaultArtifactContent(artifactId, meta, stage, workflow) {
|
|
|
252
339
|
`;
|
|
253
340
|
}
|
|
254
341
|
async function advanceStage(projectRoot, config, changeId, opts) {
|
|
255
|
-
|
|
342
|
+
let ctx = await buildContext(projectRoot, config, changeId);
|
|
256
343
|
const warnings = [];
|
|
257
344
|
if (ctx.meta.status === "completed") {
|
|
258
345
|
throw new Error("Change is already completed.");
|
|
259
346
|
}
|
|
347
|
+
if (!opts?.force) {
|
|
348
|
+
const fallback = await fallbackIncompleteStageId(ctx);
|
|
349
|
+
if (fallback && fallback !== ctx.meta.stage) {
|
|
350
|
+
const from2 = ctx.meta.stage;
|
|
351
|
+
ctx.meta.stage = fallback;
|
|
352
|
+
await saveChangeMeta(projectRoot, config, ctx.meta);
|
|
353
|
+
const stage2 = getStage(ctx.workflow, ctx.meta, fallback);
|
|
354
|
+
const artifactsCreated2 = await materializeStageArtifacts(
|
|
355
|
+
projectRoot,
|
|
356
|
+
config,
|
|
357
|
+
changeId,
|
|
358
|
+
ctx.workflow,
|
|
359
|
+
ctx.meta,
|
|
360
|
+
stage2
|
|
361
|
+
);
|
|
362
|
+
try {
|
|
363
|
+
const { refreshActiveAgentContext: refreshActiveAgentContext2 } = await import("./agents-BA7WVN35.js");
|
|
364
|
+
await refreshActiveAgentContext2(projectRoot);
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
const updated2 = await buildContext(projectRoot, config, changeId);
|
|
368
|
+
return {
|
|
369
|
+
ctx: updated2,
|
|
370
|
+
from: from2,
|
|
371
|
+
to: fallback,
|
|
372
|
+
completed: false,
|
|
373
|
+
artifactsCreated: artifactsCreated2,
|
|
374
|
+
warnings: [
|
|
375
|
+
...warnings,
|
|
376
|
+
`Fell back to incomplete stage "${fallback}" (required work not done). Finish it before advancing.`
|
|
377
|
+
]
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
260
381
|
const check = await canLeaveStage(ctx, config);
|
|
261
382
|
warnings.push(...check.warnings);
|
|
262
383
|
if (!check.ok && !opts?.force) {
|
|
@@ -286,7 +407,7 @@ async function advanceStage(projectRoot, config, changeId, opts) {
|
|
|
286
407
|
stage
|
|
287
408
|
);
|
|
288
409
|
try {
|
|
289
|
-
const { refreshActiveAgentContext: refreshActiveAgentContext2 } = await import("./agents-
|
|
410
|
+
const { refreshActiveAgentContext: refreshActiveAgentContext2 } = await import("./agents-BA7WVN35.js");
|
|
290
411
|
await refreshActiveAgentContext2(projectRoot);
|
|
291
412
|
} catch {
|
|
292
413
|
}
|
|
@@ -481,9 +602,9 @@ async function initProject(opts) {
|
|
|
481
602
|
await ensureDir(mDest);
|
|
482
603
|
const mSrc = defaultMemoryDir();
|
|
483
604
|
if (await pathExists(mSrc)) {
|
|
484
|
-
await
|
|
605
|
+
await copyDirSkipExisting(mSrc, mDest);
|
|
485
606
|
} else {
|
|
486
|
-
await
|
|
607
|
+
await writeTextIfMissing(
|
|
487
608
|
join4(mDest, "index.md"),
|
|
488
609
|
`# Documentation map
|
|
489
610
|
|
|
@@ -492,34 +613,93 @@ Parent page for **stable** project docs. Start here, then open only what you nee
|
|
|
492
613
|
| Topic | File |
|
|
493
614
|
|-------|------|
|
|
494
615
|
| Product | [product.md](product.md) |
|
|
616
|
+
| Constitution (non-negotiables) | [constitution.md](constitution.md) |
|
|
495
617
|
| Architecture | [architecture.md](architecture.md) |
|
|
496
618
|
| Conventions | [conventions.md](conventions.md) |
|
|
497
619
|
|
|
498
620
|
Active work: \`.sdd/active-context.md\` and \`changes/<id>/\`.
|
|
499
621
|
`
|
|
500
622
|
);
|
|
501
|
-
await
|
|
623
|
+
await writeTextIfMissing(
|
|
502
624
|
join4(mDest, "product.md"),
|
|
503
625
|
"# Product\n\n<!-- What are you building? -->\n"
|
|
504
626
|
);
|
|
505
|
-
await
|
|
627
|
+
await writeTextIfMissing(
|
|
628
|
+
join4(mDest, "constitution.md"),
|
|
629
|
+
`# Constitution
|
|
630
|
+
|
|
631
|
+
<!-- Non-negotiables agents must not violate. Broader style lives in conventions.md. -->
|
|
632
|
+
|
|
633
|
+
## Principles
|
|
634
|
+
|
|
635
|
+
-
|
|
636
|
+
|
|
637
|
+
## Stack & tooling
|
|
638
|
+
|
|
639
|
+
-
|
|
640
|
+
|
|
641
|
+
## Testing
|
|
642
|
+
|
|
643
|
+
-
|
|
644
|
+
|
|
645
|
+
## Security
|
|
646
|
+
|
|
647
|
+
-
|
|
648
|
+
|
|
649
|
+
## Process
|
|
650
|
+
|
|
651
|
+
-
|
|
652
|
+
`
|
|
653
|
+
);
|
|
654
|
+
await writeTextIfMissing(
|
|
506
655
|
join4(mDest, "architecture.md"),
|
|
507
656
|
"# Architecture\n\n<!-- High-level system shape -->\n"
|
|
508
657
|
);
|
|
509
|
-
await
|
|
658
|
+
await writeTextIfMissing(
|
|
510
659
|
join4(mDest, "conventions.md"),
|
|
511
660
|
"# Conventions\n\n<!-- Coding and process conventions -->\n"
|
|
512
661
|
);
|
|
513
662
|
}
|
|
663
|
+
if (!await pathExists(join4(mDest, "constitution.md"))) {
|
|
664
|
+
const fromDefault = join4(mSrc, "constitution.md");
|
|
665
|
+
if (await pathExists(fromDefault)) {
|
|
666
|
+
await copyDirSkipExisting(mSrc, mDest);
|
|
667
|
+
} else {
|
|
668
|
+
await writeTextIfMissing(
|
|
669
|
+
join4(mDest, "constitution.md"),
|
|
670
|
+
`# Constitution
|
|
671
|
+
|
|
672
|
+
<!-- Non-negotiables agents must not violate. Broader style lives in conventions.md. -->
|
|
673
|
+
|
|
674
|
+
## Principles
|
|
675
|
+
|
|
676
|
+
-
|
|
677
|
+
|
|
678
|
+
## Stack & tooling
|
|
679
|
+
|
|
680
|
+
-
|
|
681
|
+
|
|
682
|
+
## Testing
|
|
683
|
+
|
|
684
|
+
-
|
|
685
|
+
|
|
686
|
+
## Security
|
|
687
|
+
|
|
688
|
+
-
|
|
689
|
+
|
|
690
|
+
## Process
|
|
691
|
+
|
|
692
|
+
-
|
|
693
|
+
`
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
514
697
|
created.push(mDest);
|
|
515
698
|
await ensureDir(changesDir(projectRoot, config));
|
|
516
|
-
await ensureDir(archiveDir(projectRoot, config));
|
|
517
699
|
await ensureDir(domainsDir(projectRoot, config));
|
|
518
700
|
created.push(changesDir(projectRoot, config));
|
|
519
|
-
created.push(archiveDir(projectRoot, config));
|
|
520
701
|
created.push(domainsDir(projectRoot, config));
|
|
521
702
|
await writeText(join4(changesDir(projectRoot, config), ".gitkeep"), "");
|
|
522
|
-
await writeText(join4(archiveDir(projectRoot, config), ".gitkeep"), "");
|
|
523
703
|
await writeText(join4(domainsDir(projectRoot, config), ".gitkeep"), "");
|
|
524
704
|
const sddReadme = join4(root, "README.md");
|
|
525
705
|
if (!await pathExists(sddReadme) || force) {
|
|
@@ -534,7 +714,8 @@ This directory configures local Spec-Driven Development for this repo.
|
|
|
534
714
|
- \`templates/\` \u2014 markdown templates for stage artifacts
|
|
535
715
|
- \`protocol.md\` / \`active-context.md\` \u2014 agent playbook + live task (after agent install)
|
|
536
716
|
|
|
537
|
-
Work lives in \`../changes
|
|
717
|
+
Work lives in \`../changes/<id>/\`. Completed packs stay there with \`status: completed\`.
|
|
718
|
+
Promote lasting decisions into \`../memory/\`. (Archive-on-complete is opt-in.)
|
|
538
719
|
|
|
539
720
|
**Documentation map (stable):** \`../memory/index.md\`
|
|
540
721
|
**Change-scoped docs:** \`../changes/<id>/\`
|
|
@@ -590,14 +771,59 @@ function runCommand(command, cwd) {
|
|
|
590
771
|
});
|
|
591
772
|
});
|
|
592
773
|
}
|
|
774
|
+
function isToolGeneratedResults(content) {
|
|
775
|
+
const trimmed = content.trim();
|
|
776
|
+
if (!trimmed) return true;
|
|
777
|
+
if (trimmed.includes("{{") || trimmed.includes("TODO")) return true;
|
|
778
|
+
if (trimmed.startsWith("# Local verify results")) return true;
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
function buildSummaryLines(title, stageId, ok, results, checklist) {
|
|
782
|
+
const lines = [
|
|
783
|
+
`# Local verify results`,
|
|
784
|
+
``,
|
|
785
|
+
`> Change: ${title}`,
|
|
786
|
+
`> Stage: ${stageId}`,
|
|
787
|
+
`> At: ${nowIso()}`,
|
|
788
|
+
`> Overall: ${ok ? "PASS" : "FAIL"}`,
|
|
789
|
+
``,
|
|
790
|
+
`## Commands`,
|
|
791
|
+
``
|
|
792
|
+
];
|
|
793
|
+
if (results.length === 0) {
|
|
794
|
+
lines.push(`_No commands configured for this stage. Complete the checklist manually._`);
|
|
795
|
+
lines.push("");
|
|
796
|
+
} else {
|
|
797
|
+
for (const r of results) {
|
|
798
|
+
lines.push(`### ${r.name}`);
|
|
799
|
+
lines.push(`- Command: \`${r.command}\``);
|
|
800
|
+
lines.push(`- Exit: ${r.exitCode}`);
|
|
801
|
+
lines.push("");
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
lines.push(`## Checklist`);
|
|
805
|
+
lines.push("");
|
|
806
|
+
for (const item of checklist) {
|
|
807
|
+
lines.push(`- [ ] ${item}`);
|
|
808
|
+
}
|
|
809
|
+
lines.push("");
|
|
810
|
+
lines.push(`## Notes`);
|
|
811
|
+
lines.push("");
|
|
812
|
+
lines.push(`-`);
|
|
813
|
+
lines.push("");
|
|
814
|
+
return lines;
|
|
815
|
+
}
|
|
593
816
|
async function runLocalVerify(projectRoot, config, changeId, opts) {
|
|
594
817
|
const ctx = await buildContext(projectRoot, config, changeId);
|
|
595
818
|
const stage = getStage(ctx.workflow, ctx.meta, ctx.meta.stage);
|
|
596
819
|
if (!stage) {
|
|
597
820
|
throw new Error(`Unknown stage: ${ctx.meta.stage}`);
|
|
598
821
|
}
|
|
599
|
-
const
|
|
600
|
-
|
|
822
|
+
const evidenceDirRaw = stage.verify?.evidence_dir?.trim();
|
|
823
|
+
const evidenceDir = evidenceDirRaw && evidenceDirRaw.length > 0 ? join5(ctx.path, evidenceDirRaw) : null;
|
|
824
|
+
if (evidenceDir) {
|
|
825
|
+
await ensureDir(evidenceDir);
|
|
826
|
+
}
|
|
601
827
|
const results = [];
|
|
602
828
|
const runCommands = opts?.runCommands !== false;
|
|
603
829
|
const commands = stage.verify?.commands ?? [];
|
|
@@ -610,15 +836,17 @@ async function runLocalVerify(projectRoot, config, changeId, opts) {
|
|
|
610
836
|
exitCode,
|
|
611
837
|
output: output.slice(0, 5e4)
|
|
612
838
|
});
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
839
|
+
if (evidenceDir) {
|
|
840
|
+
const logPath = join5(evidenceDir, `${cmd.name.replace(/\s+/g, "-")}.log`);
|
|
841
|
+
await writeText(
|
|
842
|
+
logPath,
|
|
843
|
+
`# ${cmd.name}
|
|
617
844
|
$ ${cmd.run}
|
|
618
845
|
exit: ${exitCode}
|
|
619
846
|
|
|
620
847
|
${output}`
|
|
621
|
-
|
|
848
|
+
);
|
|
849
|
+
}
|
|
622
850
|
}
|
|
623
851
|
}
|
|
624
852
|
const required = commands.filter((c) => c.required);
|
|
@@ -637,46 +865,44 @@ ${output}`
|
|
|
637
865
|
}
|
|
638
866
|
const checklist = stage.gate?.checklist ?? [];
|
|
639
867
|
const summaryPath = join5(ctx.path, "local-test-results.md");
|
|
640
|
-
const lines =
|
|
641
|
-
|
|
642
|
-
``,
|
|
643
|
-
`> Change: ${ctx.meta.title}`,
|
|
644
|
-
`> Stage: ${stage.id}`,
|
|
645
|
-
`> At: ${nowIso()}`,
|
|
646
|
-
`> Overall: ${ok ? "PASS" : "FAIL"}`,
|
|
647
|
-
``,
|
|
648
|
-
`## Commands`,
|
|
649
|
-
``
|
|
650
|
-
];
|
|
651
|
-
if (results.length === 0) {
|
|
652
|
-
lines.push(`_No commands configured for this stage. Complete the checklist manually._`);
|
|
653
|
-
lines.push("");
|
|
654
|
-
} else {
|
|
655
|
-
for (const r of results) {
|
|
656
|
-
lines.push(`### ${r.name}`);
|
|
657
|
-
lines.push(`- Command: \`${r.command}\``);
|
|
658
|
-
lines.push(`- Exit: ${r.exitCode}`);
|
|
659
|
-
lines.push("");
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
lines.push(`## Checklist`);
|
|
663
|
-
lines.push("");
|
|
664
|
-
for (const item of checklist) {
|
|
665
|
-
lines.push(`- [ ] ${item}`);
|
|
666
|
-
}
|
|
667
|
-
lines.push("");
|
|
668
|
-
lines.push(`## Notes`);
|
|
669
|
-
lines.push("");
|
|
670
|
-
lines.push(`-`);
|
|
671
|
-
lines.push("");
|
|
868
|
+
const lines = buildSummaryLines(ctx.meta.title, stage.id, ok, results, checklist);
|
|
869
|
+
const summaryBody = lines.join("\n");
|
|
672
870
|
if (await pathExists(summaryPath)) {
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
871
|
+
const existing = await readText(summaryPath);
|
|
872
|
+
if (isToolGeneratedResults(existing)) {
|
|
873
|
+
await writeText(summaryPath, summaryBody);
|
|
874
|
+
} else {
|
|
875
|
+
const stamp = nowIso();
|
|
876
|
+
const append = [
|
|
877
|
+
``,
|
|
878
|
+
`---`,
|
|
879
|
+
``,
|
|
880
|
+
`## Latest run (${stamp})`,
|
|
881
|
+
``,
|
|
882
|
+
`> Overall: ${ok ? "PASS" : "FAIL"}`,
|
|
883
|
+
`> Stage: ${stage.id}`,
|
|
884
|
+
``
|
|
885
|
+
];
|
|
886
|
+
if (results.length === 0) {
|
|
887
|
+
append.push(`_No commands configured for this stage._`);
|
|
888
|
+
append.push("");
|
|
889
|
+
} else {
|
|
890
|
+
for (const r of results) {
|
|
891
|
+
append.push(`- **${r.name}**: exit ${r.exitCode} (\`${r.command}\`)`);
|
|
892
|
+
}
|
|
893
|
+
append.push("");
|
|
894
|
+
}
|
|
895
|
+
await writeText(summaryPath, existing.trimEnd() + "\n" + append.join("\n"));
|
|
896
|
+
}
|
|
897
|
+
if (evidenceDir) {
|
|
898
|
+
const stamp = nowIso();
|
|
899
|
+
await writeText(
|
|
900
|
+
join5(evidenceDir, `run-${stamp.replace(/[:.]/g, "-")}.md`),
|
|
901
|
+
summaryBody
|
|
902
|
+
);
|
|
903
|
+
}
|
|
678
904
|
} else {
|
|
679
|
-
await writeText(summaryPath,
|
|
905
|
+
await writeText(summaryPath, summaryBody);
|
|
680
906
|
}
|
|
681
907
|
ctx.meta.verify_results = {
|
|
682
908
|
...ctx.meta.verify_results,
|
|
@@ -728,6 +954,7 @@ export {
|
|
|
728
954
|
completeChange,
|
|
729
955
|
configPath,
|
|
730
956
|
copyDir,
|
|
957
|
+
copyDirSkipExisting,
|
|
731
958
|
createChange,
|
|
732
959
|
defaultConfig,
|
|
733
960
|
defaultMemoryDir,
|
|
@@ -736,11 +963,13 @@ export {
|
|
|
736
963
|
defaultsRoot,
|
|
737
964
|
domainsDir,
|
|
738
965
|
ensureDir,
|
|
966
|
+
fallbackIncompleteStageId,
|
|
739
967
|
firstActiveStageId,
|
|
740
968
|
formatStatus,
|
|
741
969
|
getActiveChangeId,
|
|
742
970
|
getIntegration,
|
|
743
971
|
getStage,
|
|
972
|
+
incompletePriorStageErrors,
|
|
744
973
|
inferComplexity,
|
|
745
974
|
initProject,
|
|
746
975
|
installAgentIntegration,
|
|
@@ -748,6 +977,7 @@ export {
|
|
|
748
977
|
integrationKeyFor,
|
|
749
978
|
isInitialized,
|
|
750
979
|
isStageSkipped,
|
|
980
|
+
isSubstantiveArtifactContent,
|
|
751
981
|
listChanges,
|
|
752
982
|
listDirs,
|
|
753
983
|
listFiles,
|
|
@@ -787,5 +1017,6 @@ export {
|
|
|
787
1017
|
workflowsDir,
|
|
788
1018
|
writeAgentHandoff,
|
|
789
1019
|
writeText,
|
|
1020
|
+
writeTextIfMissing,
|
|
790
1021
|
writeYaml
|
|
791
1022
|
};
|