@structured-vibe-coding/core 0.9.0 → 0.10.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/README.md +1 -4
- package/dist/{agents-BA7WVN35.js → agents-IB37YJX6.js} +4 -1
- package/dist/chunk-7MJYPJMR.js +51 -0
- package/dist/chunk-EPFURATC.js +96 -0
- package/dist/chunk-KZHNYGD6.js +187 -0
- package/dist/{chunk-M245MC6E.js → chunk-UAOFNSLQ.js} +74 -335
- package/dist/config-4QZMRSL3.js +14 -0
- package/dist/fs-J6DIV6CX.js +28 -0
- package/dist/index.d.ts +276 -2
- package/dist/index.js +1649 -38
- package/dist/paths-64GA32IZ.js +28 -0
- package/package.json +2 -1
|
@@ -1,287 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
async function readYaml(path) {
|
|
27
|
-
const raw = await readText(path);
|
|
28
|
-
return parseYaml(raw);
|
|
29
|
-
}
|
|
30
|
-
async function writeYaml(path, data) {
|
|
31
|
-
const content = stringifyYaml(data, {
|
|
32
|
-
lineWidth: 100,
|
|
33
|
-
defaultStringType: "PLAIN",
|
|
34
|
-
defaultKeyType: "PLAIN"
|
|
35
|
-
});
|
|
36
|
-
await writeText(path, content.endsWith("\n") ? content : `${content}
|
|
37
|
-
`);
|
|
38
|
-
}
|
|
39
|
-
async function listDirs(path) {
|
|
40
|
-
if (!await pathExists(path)) return [];
|
|
41
|
-
const entries = await fs.readdir(path, { withFileTypes: true });
|
|
42
|
-
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
43
|
-
}
|
|
44
|
-
async function listFiles(path) {
|
|
45
|
-
if (!await pathExists(path)) return [];
|
|
46
|
-
const entries = await fs.readdir(path, { withFileTypes: true });
|
|
47
|
-
return entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
48
|
-
}
|
|
49
|
-
async function copyDir(src, dest) {
|
|
50
|
-
await ensureDir(dest);
|
|
51
|
-
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
52
|
-
for (const entry of entries) {
|
|
53
|
-
const from = `${src}/${entry.name}`;
|
|
54
|
-
const to = `${dest}/${entry.name}`;
|
|
55
|
-
if (entry.isDirectory()) {
|
|
56
|
-
await copyDir(from, to);
|
|
57
|
-
} else {
|
|
58
|
-
await ensureDir(dirname(to));
|
|
59
|
-
await fs.copyFile(from, to);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
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
|
-
}
|
|
82
|
-
async function removePath(path) {
|
|
83
|
-
await fs.rm(path, { recursive: true, force: true });
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// src/schemas.ts
|
|
87
|
-
import { z } from "zod";
|
|
88
|
-
var GateSchema = z.object({
|
|
89
|
-
type: z.enum(["none", "soft", "hard"]).default("soft"),
|
|
90
|
-
mode: z.enum(["none", "human_approval", "checklist"]).default("checklist"),
|
|
91
|
-
checklist: z.array(z.string()).default([]),
|
|
92
|
-
overridable: z.boolean().default(true)
|
|
93
|
-
});
|
|
94
|
-
var ArtifactDefSchema = z.object({
|
|
95
|
-
id: z.string(),
|
|
96
|
-
path: z.string(),
|
|
97
|
-
template: z.string().optional(),
|
|
98
|
-
required: z.boolean().default(true)
|
|
99
|
-
});
|
|
100
|
-
var VerifyCommandSchema = z.object({
|
|
101
|
-
name: z.string(),
|
|
102
|
-
run: z.string(),
|
|
103
|
-
required: z.boolean().default(false)
|
|
104
|
-
});
|
|
105
|
-
var StageSchema = z.object({
|
|
106
|
-
id: z.string(),
|
|
107
|
-
title: z.string().optional(),
|
|
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),
|
|
117
|
-
artifacts: z.array(ArtifactDefSchema).default([]),
|
|
118
|
-
gate: GateSchema.default({}),
|
|
119
|
-
skippable: z.boolean().default(true),
|
|
120
|
-
skip_when: z.object({
|
|
121
|
-
flags: z.array(z.string()).optional()
|
|
122
|
-
}).optional(),
|
|
123
|
-
verify: z.object({
|
|
124
|
-
commands: z.array(VerifyCommandSchema).default([]),
|
|
125
|
-
evidence_dir: z.string().optional()
|
|
126
|
-
}).optional(),
|
|
127
|
-
agent_context: z.object({
|
|
128
|
-
include: z.array(z.string()).default([]),
|
|
129
|
-
instructions: z.string().optional()
|
|
130
|
-
}).optional()
|
|
131
|
-
});
|
|
132
|
-
var WorkflowSchema = z.object({
|
|
133
|
-
name: z.string(),
|
|
134
|
-
description: z.string().optional(),
|
|
135
|
-
version: z.number().default(1),
|
|
136
|
-
stages: z.array(StageSchema).min(1),
|
|
137
|
-
recommendation: z.object({
|
|
138
|
-
when: z.object({
|
|
139
|
-
labels: z.array(z.string()).optional(),
|
|
140
|
-
keywords: z.array(z.string()).optional(),
|
|
141
|
-
max_complexity: z.enum(["low", "medium", "high"]).optional(),
|
|
142
|
-
min_complexity: z.enum(["low", "medium", "high"]).optional()
|
|
143
|
-
}).optional(),
|
|
144
|
-
priority: z.number().default(0)
|
|
145
|
-
}).optional(),
|
|
146
|
-
on_complete: z.object({
|
|
147
|
-
archive: z.boolean().default(false),
|
|
148
|
-
domain_sync: z.enum(["never", "recommend", "require"]).default("never")
|
|
149
|
-
}).optional()
|
|
150
|
-
});
|
|
151
|
-
var ConfigSchema = z.object({
|
|
152
|
-
version: z.number().default(1),
|
|
153
|
-
memory_path: z.string().default("memory"),
|
|
154
|
-
changes_path: z.string().default("changes"),
|
|
155
|
-
archive_path: z.string().default("archive"),
|
|
156
|
-
domains_path: z.string().default("domains"),
|
|
157
|
-
default_workflow: z.string().default("recommend"),
|
|
158
|
-
allowed_workflows: z.array(z.string()).optional(),
|
|
159
|
-
per_change: z.object({
|
|
160
|
-
allow_skip: z.boolean().default(true),
|
|
161
|
-
allow_gate_override: z.enum(["true", "false", "soft_only"]).default("soft_only"),
|
|
162
|
-
allow_custom_stages: z.boolean().default(true),
|
|
163
|
-
require_reason_on_skip: z.boolean().default(true)
|
|
164
|
-
}).default({}),
|
|
165
|
-
persistence: z.object({
|
|
166
|
-
default: z.enum(["change_only", "domain_sync"]).default("change_only"),
|
|
167
|
-
archive_on_complete: z.boolean().default(false),
|
|
168
|
-
domain_sync: z.object({
|
|
169
|
-
mode: z.enum(["never", "recommend", "require"]).default("never"),
|
|
170
|
-
anchored_domains: z.array(z.string()).default([])
|
|
171
|
-
}).default({})
|
|
172
|
-
}).default({}),
|
|
173
|
-
policy: z.object({
|
|
174
|
-
gates: z.enum(["soft", "hard"]).default("soft")
|
|
175
|
-
}).default({})
|
|
176
|
-
});
|
|
177
|
-
var ChangeMetaSchema = z.object({
|
|
178
|
-
id: z.string(),
|
|
179
|
-
title: z.string(),
|
|
180
|
-
workflow: z.string(),
|
|
181
|
-
created: z.string(),
|
|
182
|
-
updated: z.string().optional(),
|
|
183
|
-
status: z.enum(["in_progress", "blocked", "completed", "abandoned"]).default("in_progress"),
|
|
184
|
-
stage: z.string(),
|
|
185
|
-
branch: z.string().optional(),
|
|
186
|
-
domain: z.string().optional(),
|
|
187
|
-
flags: z.record(z.union([z.boolean(), z.string(), z.number()])).default({}),
|
|
188
|
-
overrides: z.object({
|
|
189
|
-
skip_stages: z.array(z.string()).default([]),
|
|
190
|
-
gates: z.record(z.enum(["none", "soft", "hard"])).default({}),
|
|
191
|
-
extra_stages: z.array(StageSchema).default([])
|
|
192
|
-
}).default({}),
|
|
193
|
-
skipped: z.array(
|
|
194
|
-
z.object({
|
|
195
|
-
stage: z.string(),
|
|
196
|
-
reason: z.string(),
|
|
197
|
-
at: z.string()
|
|
198
|
-
})
|
|
199
|
-
).default([]),
|
|
200
|
-
gates: z.record(
|
|
201
|
-
z.object({
|
|
202
|
-
status: z.enum(["pending", "approved", "failed", "waived"]),
|
|
203
|
-
note: z.string().optional(),
|
|
204
|
-
at: z.string().optional()
|
|
205
|
-
})
|
|
206
|
-
).default({}),
|
|
207
|
-
/** Results from `sdd verify` keyed by stage id */
|
|
208
|
-
verify_results: z.record(
|
|
209
|
-
z.object({
|
|
210
|
-
ok: z.boolean(),
|
|
211
|
-
at: z.string(),
|
|
212
|
-
results: z.array(
|
|
213
|
-
z.object({
|
|
214
|
-
name: z.string(),
|
|
215
|
-
exitCode: z.number().nullable()
|
|
216
|
-
})
|
|
217
|
-
).default([])
|
|
218
|
-
})
|
|
219
|
-
).default({}),
|
|
220
|
-
pr: z.object({
|
|
221
|
-
number: z.number().optional(),
|
|
222
|
-
url: z.string().optional()
|
|
223
|
-
}).optional(),
|
|
224
|
-
completed_at: z.string().optional()
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// src/paths.ts
|
|
228
|
-
import { join } from "pathe";
|
|
229
|
-
var SDD_DIR = ".sdd";
|
|
230
|
-
function sddRoot(projectRoot) {
|
|
231
|
-
return join(projectRoot, SDD_DIR);
|
|
232
|
-
}
|
|
233
|
-
function configPath(projectRoot) {
|
|
234
|
-
return join(sddRoot(projectRoot), "config.yaml");
|
|
235
|
-
}
|
|
236
|
-
function workflowsDir(projectRoot) {
|
|
237
|
-
return join(sddRoot(projectRoot), "workflows");
|
|
238
|
-
}
|
|
239
|
-
function templatesDir(projectRoot) {
|
|
240
|
-
return join(sddRoot(projectRoot), "templates");
|
|
241
|
-
}
|
|
242
|
-
function memoryDir(projectRoot, config) {
|
|
243
|
-
return join(projectRoot, config.memory_path);
|
|
244
|
-
}
|
|
245
|
-
function changesDir(projectRoot, config) {
|
|
246
|
-
return join(projectRoot, config.changes_path);
|
|
247
|
-
}
|
|
248
|
-
function archiveDir(projectRoot, config) {
|
|
249
|
-
return join(projectRoot, config.archive_path);
|
|
250
|
-
}
|
|
251
|
-
function domainsDir(projectRoot, config) {
|
|
252
|
-
return join(projectRoot, config.domains_path);
|
|
253
|
-
}
|
|
254
|
-
function changePath(projectRoot, config, changeId) {
|
|
255
|
-
return join(changesDir(projectRoot, config), changeId);
|
|
256
|
-
}
|
|
257
|
-
function changeMetaPath(projectRoot, config, changeId) {
|
|
258
|
-
return join(changePath(projectRoot, config, changeId), "meta.yaml");
|
|
259
|
-
}
|
|
260
|
-
function activePointerPath(projectRoot, config) {
|
|
261
|
-
return join(changesDir(projectRoot, config), ".active");
|
|
262
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
ChangeMetaSchema,
|
|
3
|
+
WorkflowSchema,
|
|
4
|
+
loadConfig
|
|
5
|
+
} from "./chunk-KZHNYGD6.js";
|
|
6
|
+
import {
|
|
7
|
+
activePointerPath,
|
|
8
|
+
changeMetaPath,
|
|
9
|
+
changePath,
|
|
10
|
+
changesDir,
|
|
11
|
+
sddRoot,
|
|
12
|
+
workflowsDir
|
|
13
|
+
} from "./chunk-7MJYPJMR.js";
|
|
14
|
+
import {
|
|
15
|
+
ensureDir,
|
|
16
|
+
listDirs,
|
|
17
|
+
listFiles,
|
|
18
|
+
pathExists,
|
|
19
|
+
readText,
|
|
20
|
+
readYaml,
|
|
21
|
+
removePath,
|
|
22
|
+
writeText,
|
|
23
|
+
writeYaml
|
|
24
|
+
} from "./chunk-EPFURATC.js";
|
|
263
25
|
|
|
264
|
-
// src/
|
|
265
|
-
|
|
266
|
-
return pathExists(configPath(projectRoot));
|
|
267
|
-
}
|
|
268
|
-
async function loadConfig(projectRoot) {
|
|
269
|
-
const path = configPath(projectRoot);
|
|
270
|
-
if (!await pathExists(path)) {
|
|
271
|
-
throw new Error(
|
|
272
|
-
`SDD is not initialized in ${projectRoot}. Run \`sdd init\` first.`
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
const raw = await readYaml(path);
|
|
276
|
-
return ConfigSchema.parse(raw);
|
|
277
|
-
}
|
|
278
|
-
async function saveConfig(projectRoot, config) {
|
|
279
|
-
await ensureDir(sddRoot(projectRoot));
|
|
280
|
-
await writeYaml(configPath(projectRoot), ConfigSchema.parse(config));
|
|
281
|
-
}
|
|
282
|
-
function defaultConfig() {
|
|
283
|
-
return ConfigSchema.parse({});
|
|
284
|
-
}
|
|
26
|
+
// src/agents.ts
|
|
27
|
+
import { join as join3 } from "pathe";
|
|
285
28
|
|
|
286
29
|
// src/slug.ts
|
|
287
30
|
function slugify(input) {
|
|
@@ -305,7 +48,7 @@ function uniqueChangeDirName(base, taken) {
|
|
|
305
48
|
}
|
|
306
49
|
|
|
307
50
|
// src/workflow.ts
|
|
308
|
-
import { join
|
|
51
|
+
import { join } from "pathe";
|
|
309
52
|
async function listWorkflowNames(projectRoot) {
|
|
310
53
|
const dir = workflowsDir(projectRoot);
|
|
311
54
|
const files = await listFiles(dir);
|
|
@@ -315,7 +58,7 @@ async function loadWorkflow(projectRoot, name) {
|
|
|
315
58
|
const dir = workflowsDir(projectRoot);
|
|
316
59
|
const candidates = [`${name}.yaml`, `${name}.yml`];
|
|
317
60
|
for (const file of candidates) {
|
|
318
|
-
const path =
|
|
61
|
+
const path = join(dir, file);
|
|
319
62
|
if (await pathExists(path)) {
|
|
320
63
|
const raw = await readYaml(path);
|
|
321
64
|
const workflow = WorkflowSchema.parse(raw);
|
|
@@ -553,7 +296,7 @@ async function resolveChangeId(projectRoot, config, changeId) {
|
|
|
553
296
|
}
|
|
554
297
|
|
|
555
298
|
// src/agent-handoff.ts
|
|
556
|
-
import { join as
|
|
299
|
+
import { join as join2 } from "pathe";
|
|
557
300
|
function formatStatus(ctx) {
|
|
558
301
|
const lines = [];
|
|
559
302
|
lines.push(`Change: ${ctx.meta.title}`);
|
|
@@ -621,12 +364,12 @@ async function buildAgentPrompt(ctx, config, projectRoot) {
|
|
|
621
364
|
parts.push(`- Prefer small, reviewable commits.`);
|
|
622
365
|
parts.push("");
|
|
623
366
|
parts.push(`## Artifacts to read`);
|
|
624
|
-
const memoryHint =
|
|
367
|
+
const memoryHint = join2(projectRoot, config.memory_path);
|
|
625
368
|
parts.push(`- Documentation map (if present): ${memoryHint}/index.md`);
|
|
626
369
|
parts.push(`- Memory: ${memoryHint}/`);
|
|
627
370
|
for (const s of ctx.stages) {
|
|
628
371
|
for (const a of s.artifacts) {
|
|
629
|
-
parts.push(`- ${
|
|
372
|
+
parts.push(`- ${join2(ctx.path, a.path)}`);
|
|
630
373
|
}
|
|
631
374
|
}
|
|
632
375
|
parts.push("");
|
|
@@ -637,6 +380,27 @@ async function buildAgentPrompt(ctx, config, projectRoot) {
|
|
|
637
380
|
}
|
|
638
381
|
}
|
|
639
382
|
parts.push("");
|
|
383
|
+
const stageId = ctx.meta.stage;
|
|
384
|
+
if (stageId === "implement" || stageId === "local_verify") {
|
|
385
|
+
parts.push(`## Code context (product code)`);
|
|
386
|
+
parts.push("");
|
|
387
|
+
parts.push(
|
|
388
|
+
`Process context above is not enough for product code structure.`
|
|
389
|
+
);
|
|
390
|
+
parts.push(
|
|
391
|
+
`When you need symbols, callers, or package-boundary context:`
|
|
392
|
+
);
|
|
393
|
+
parts.push(
|
|
394
|
+
`1. Run: \`sdd context\` (optional: \`--path \u2026\`, \`--symbol \u2026\`, \`--out change\`)`
|
|
395
|
+
);
|
|
396
|
+
parts.push(
|
|
397
|
+
`2. If present, read: \`changes/${ctx.id}/code-context.md\` (regenerate if stale)`
|
|
398
|
+
);
|
|
399
|
+
parts.push(
|
|
400
|
+
`Do not dump unbounded source into the prompt; prefer the sliced output.`
|
|
401
|
+
);
|
|
402
|
+
parts.push("");
|
|
403
|
+
}
|
|
640
404
|
parts.push(
|
|
641
405
|
`When done with this stage, the human will run \`sdd next\` or \`sdd verify\` / \`sdd complete\`.`
|
|
642
406
|
);
|
|
@@ -732,7 +496,7 @@ async function removeOtherAgentHosts(projectRoot, keep) {
|
|
|
732
496
|
for (const integ of AGENT_INTEGRATIONS) {
|
|
733
497
|
if (integ.id === keep) continue;
|
|
734
498
|
for (const rel of agentHostPaths(integ.id)) {
|
|
735
|
-
const full =
|
|
499
|
+
const full = join3(projectRoot, rel);
|
|
736
500
|
if (await pathExists(full)) {
|
|
737
501
|
await removePath(full);
|
|
738
502
|
removed.push(rel);
|
|
@@ -740,7 +504,7 @@ async function removeOtherAgentHosts(projectRoot, keep) {
|
|
|
740
504
|
}
|
|
741
505
|
}
|
|
742
506
|
for (const rel of LEGACY_AGENT_PATHS) {
|
|
743
|
-
const full =
|
|
507
|
+
const full = join3(projectRoot, rel);
|
|
744
508
|
if (await pathExists(full)) {
|
|
745
509
|
await removePath(full);
|
|
746
510
|
removed.push(rel);
|
|
@@ -748,7 +512,7 @@ async function removeOtherAgentHosts(projectRoot, keep) {
|
|
|
748
512
|
}
|
|
749
513
|
for (const parent of [".claude", ".idea", ".grok"]) {
|
|
750
514
|
if (keep === "grok" && parent === ".grok") continue;
|
|
751
|
-
const full =
|
|
515
|
+
const full = join3(projectRoot, parent);
|
|
752
516
|
if (!await pathExists(full)) continue;
|
|
753
517
|
try {
|
|
754
518
|
const { readdir } = await import("fs/promises");
|
|
@@ -803,7 +567,8 @@ var SDD_AGENT_ROLES = [
|
|
|
803
567
|
roleRules: [
|
|
804
568
|
"If stage is intent/feature/design/tasks/stories/research: act as planner (artifacts, little or no product code).",
|
|
805
569
|
"If stage is implement: act as implementer (scoped code only).",
|
|
806
|
-
"If stage is local_verify: act as reviewer (gaps vs acceptance; fixes only)."
|
|
570
|
+
"If stage is local_verify: act as reviewer (gaps vs acceptance; fixes only).",
|
|
571
|
+
"If kickoff/brief says refine or analyze: follow changes/<id>/refine-brief.md \u2014 stage-scoped refine; never edit constitution; do not sdd next."
|
|
807
572
|
]
|
|
808
573
|
},
|
|
809
574
|
{
|
|
@@ -842,7 +607,7 @@ async function installAgentIntegration(opts) {
|
|
|
842
607
|
const force = opts.force ?? false;
|
|
843
608
|
await removeOtherAgentHosts(root, opts.target);
|
|
844
609
|
const write = async (rel, content) => {
|
|
845
|
-
const full =
|
|
610
|
+
const full = join3(root, rel);
|
|
846
611
|
if (await pathExists(full) && !force) {
|
|
847
612
|
skipped.push(rel);
|
|
848
613
|
return;
|
|
@@ -850,7 +615,7 @@ async function installAgentIntegration(opts) {
|
|
|
850
615
|
await writeText(full, content);
|
|
851
616
|
created.push(rel);
|
|
852
617
|
};
|
|
853
|
-
await write(
|
|
618
|
+
await write(join3(".sdd", "protocol.md"), PROTOCOL_MD);
|
|
854
619
|
await refreshActiveAgentContext(root);
|
|
855
620
|
const roles = integ.rolesToInstall?.length ? SDD_AGENT_ROLES.filter((r) => integ.rolesToInstall.includes(r.id)) : SDD_AGENT_ROLES;
|
|
856
621
|
for (const role of roles) {
|
|
@@ -858,7 +623,7 @@ async function installAgentIntegration(opts) {
|
|
|
858
623
|
}
|
|
859
624
|
await write("AGENTS.md", renderAgentsMd(integ));
|
|
860
625
|
await write(
|
|
861
|
-
|
|
626
|
+
join3(".sdd", "agents.json"),
|
|
862
627
|
JSON.stringify(
|
|
863
628
|
{
|
|
864
629
|
version: 3,
|
|
@@ -926,7 +691,7 @@ Do not claim the change is complete without local verification when the workflow
|
|
|
926
691
|
`;
|
|
927
692
|
}
|
|
928
693
|
async function loadInstalledAgent(projectRoot) {
|
|
929
|
-
const path =
|
|
694
|
+
const path = join3(sddRoot(projectRoot), "agents.json");
|
|
930
695
|
if (!await pathExists(path)) return null;
|
|
931
696
|
try {
|
|
932
697
|
const raw = JSON.parse(await readText(path));
|
|
@@ -941,7 +706,7 @@ async function loadInstalledAgent(projectRoot) {
|
|
|
941
706
|
async function writeAgentHandoff(projectRoot, config, changeId) {
|
|
942
707
|
const ctx = await buildContext(projectRoot, config, changeId);
|
|
943
708
|
const body = await buildAgentPrompt(ctx, config, projectRoot);
|
|
944
|
-
const outPath =
|
|
709
|
+
const outPath = join3(sddRoot(projectRoot), "handoff.md");
|
|
945
710
|
const header = `# SDD handoff (auto)
|
|
946
711
|
|
|
947
712
|
> Generated for the AI coding agent configured at init.
|
|
@@ -957,6 +722,15 @@ Also read: \`.sdd/active-context.md\`, \`.sdd/protocol.md\`, \`memory/constituti
|
|
|
957
722
|
}
|
|
958
723
|
function agentKickoffMessage(opts) {
|
|
959
724
|
const event = opts.event ?? "context updated";
|
|
725
|
+
if (opts.instructions?.trim()) {
|
|
726
|
+
return [
|
|
727
|
+
`SDD: ${event}.`,
|
|
728
|
+
`Title: ${opts.title}`,
|
|
729
|
+
`Change: ${opts.changeId}`,
|
|
730
|
+
`Stage: ${opts.stage}`,
|
|
731
|
+
opts.instructions.trim()
|
|
732
|
+
].join(" ");
|
|
733
|
+
}
|
|
960
734
|
return [
|
|
961
735
|
`SDD: ${event}.`,
|
|
962
736
|
`Title: ${opts.title}`,
|
|
@@ -969,16 +743,16 @@ function agentKickoffMessage(opts) {
|
|
|
969
743
|
}
|
|
970
744
|
async function refreshActiveAgentContext(projectRoot) {
|
|
971
745
|
const markerDir = sddRoot(projectRoot);
|
|
972
|
-
if (!await pathExists(
|
|
746
|
+
if (!await pathExists(join3(markerDir, "config.yaml"))) {
|
|
973
747
|
return null;
|
|
974
748
|
}
|
|
975
|
-
const protocolPath =
|
|
749
|
+
const protocolPath = join3(markerDir, "protocol.md");
|
|
976
750
|
if (!await pathExists(protocolPath)) {
|
|
977
751
|
await writeText(protocolPath, PROTOCOL_MD);
|
|
978
752
|
}
|
|
979
753
|
const config = await loadConfig(projectRoot);
|
|
980
754
|
const activeId = await getActiveChangeId(projectRoot, config);
|
|
981
|
-
const outPath =
|
|
755
|
+
const outPath = join3(markerDir, "active-context.md");
|
|
982
756
|
if (!activeId) {
|
|
983
757
|
const empty = `# SDD active context
|
|
984
758
|
|
|
@@ -1101,41 +875,6 @@ Refresh context: \`sdd agents refresh\`.
|
|
|
1101
875
|
}
|
|
1102
876
|
|
|
1103
877
|
export {
|
|
1104
|
-
GateSchema,
|
|
1105
|
-
ArtifactDefSchema,
|
|
1106
|
-
VerifyCommandSchema,
|
|
1107
|
-
StageSchema,
|
|
1108
|
-
WorkflowSchema,
|
|
1109
|
-
ConfigSchema,
|
|
1110
|
-
ChangeMetaSchema,
|
|
1111
|
-
SDD_DIR,
|
|
1112
|
-
sddRoot,
|
|
1113
|
-
configPath,
|
|
1114
|
-
workflowsDir,
|
|
1115
|
-
templatesDir,
|
|
1116
|
-
memoryDir,
|
|
1117
|
-
changesDir,
|
|
1118
|
-
archiveDir,
|
|
1119
|
-
domainsDir,
|
|
1120
|
-
changePath,
|
|
1121
|
-
changeMetaPath,
|
|
1122
|
-
activePointerPath,
|
|
1123
|
-
pathExists,
|
|
1124
|
-
ensureDir,
|
|
1125
|
-
readText,
|
|
1126
|
-
writeText,
|
|
1127
|
-
readYaml,
|
|
1128
|
-
writeYaml,
|
|
1129
|
-
listDirs,
|
|
1130
|
-
listFiles,
|
|
1131
|
-
copyDir,
|
|
1132
|
-
copyDirSkipExisting,
|
|
1133
|
-
writeTextIfMissing,
|
|
1134
|
-
removePath,
|
|
1135
|
-
isInitialized,
|
|
1136
|
-
loadConfig,
|
|
1137
|
-
saveConfig,
|
|
1138
|
-
defaultConfig,
|
|
1139
878
|
listWorkflowNames,
|
|
1140
879
|
loadWorkflow,
|
|
1141
880
|
resolveStages,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyDir,
|
|
3
|
+
copyDirSkipExisting,
|
|
4
|
+
ensureDir,
|
|
5
|
+
listDirs,
|
|
6
|
+
listFiles,
|
|
7
|
+
pathExists,
|
|
8
|
+
readText,
|
|
9
|
+
readYaml,
|
|
10
|
+
removePath,
|
|
11
|
+
writeText,
|
|
12
|
+
writeTextIfMissing,
|
|
13
|
+
writeYaml
|
|
14
|
+
} from "./chunk-EPFURATC.js";
|
|
15
|
+
export {
|
|
16
|
+
copyDir,
|
|
17
|
+
copyDirSkipExisting,
|
|
18
|
+
ensureDir,
|
|
19
|
+
listDirs,
|
|
20
|
+
listFiles,
|
|
21
|
+
pathExists,
|
|
22
|
+
readText,
|
|
23
|
+
readYaml,
|
|
24
|
+
removePath,
|
|
25
|
+
writeText,
|
|
26
|
+
writeTextIfMissing,
|
|
27
|
+
writeYaml
|
|
28
|
+
};
|