@structured-vibe-coding/core 0.18.0 → 0.19.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/teams/research/roles/researcher.md +29 -0
- package/defaults/teams/research/team.yaml +7 -0
- package/defaults/teams/software/roles/architect.md +31 -0
- package/defaults/teams/software/roles/dev-lead.md +32 -0
- package/defaults/teams/software/roles/developer.md +30 -0
- package/defaults/teams/software/roles/product-manager.md +32 -0
- package/defaults/teams/software/roles/product-owner.md +30 -0
- package/defaults/teams/software/roles/qa.md +35 -0
- package/defaults/teams/software/routing.yaml +28 -0
- package/defaults/teams/software/team.yaml +24 -0
- package/defaults/teams/solo/roles/developer.md +28 -0
- package/defaults/teams/solo/team.yaml +7 -0
- package/defaults/templates/plan.md +17 -0
- package/defaults/templates/review.md +11 -0
- package/defaults/templates/story.md +13 -0
- package/defaults/templates/task.md +11 -0
- package/defaults/workflows/team-software.yaml +153 -0
- package/dist/{agents-AKSUXQHN.js → agents-PSIOQEYH.js} +3 -3
- package/dist/{change-context-REE7UKU7.js → change-context-EZJMSGSP.js} +1 -1
- package/dist/{chunk-4PIRWYRA.js → chunk-26YG6A4M.js} +166 -81
- package/dist/{chunk-LHSR7JG2.js → chunk-2FJBFYVJ.js} +306 -31
- package/dist/{chunk-QGP43XCN.js → chunk-H55RFAEI.js} +1 -1
- package/dist/{chunk-QHV5KZKX.js → chunk-HG7TPW5V.js} +93 -8
- package/dist/{chunk-44LQ5BKZ.js → chunk-TGRV6WGS.js} +812 -4
- package/dist/{gather-ZNV2NAUN.js → gather-7TSVKQO2.js} +1 -1
- package/dist/{greenfield-I5S5OS2D.js → greenfield-QFA5WVHE.js} +3 -3
- package/dist/index.d.ts +804 -10
- package/dist/index.js +2765 -524
- package/package.json +2 -2
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildAgentPrompt,
|
|
3
|
+
canonicalRoleName,
|
|
3
4
|
defaultConfig,
|
|
4
5
|
formatStatus,
|
|
5
|
-
loadConfig
|
|
6
|
-
|
|
6
|
+
loadConfig,
|
|
7
|
+
readRole,
|
|
8
|
+
readTeamConfig
|
|
9
|
+
} from "./chunk-TGRV6WGS.js";
|
|
7
10
|
import {
|
|
8
11
|
buildContext,
|
|
12
|
+
defaultMemoryDir,
|
|
9
13
|
getActiveChangeId
|
|
10
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HG7TPW5V.js";
|
|
11
15
|
import {
|
|
12
16
|
SddError
|
|
13
17
|
} from "./chunk-MHODNFIL.js";
|
|
@@ -26,7 +30,7 @@ import {
|
|
|
26
30
|
// src/agents.ts
|
|
27
31
|
import { constants } from "fs";
|
|
28
32
|
import { access, lstat as lstat2, readdir, rmdir, unlink } from "fs/promises";
|
|
29
|
-
import { dirname
|
|
33
|
+
import { dirname, join as join4 } from "pathe";
|
|
30
34
|
|
|
31
35
|
// src/generated.ts
|
|
32
36
|
import { createHash } from "crypto";
|
|
@@ -152,12 +156,17 @@ async function reconcileGeneratedTree(projectRoot, sourceDir, destinationDir, de
|
|
|
152
156
|
kept.push({ path: projectRelative, reason: "unreadable" });
|
|
153
157
|
continue;
|
|
154
158
|
}
|
|
155
|
-
const
|
|
159
|
+
const currentHash = contentHash(current);
|
|
160
|
+
let previousHash = manifest.files[projectRelative];
|
|
156
161
|
if (!previousHash) {
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
if (currentHash !== generatedHash) {
|
|
163
|
+
kept.push({ path: projectRelative, reason: "not-generated" });
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
manifest.files[projectRelative] = generatedHash;
|
|
167
|
+
previousHash = generatedHash;
|
|
159
168
|
}
|
|
160
|
-
if (
|
|
169
|
+
if (currentHash !== previousHash) {
|
|
161
170
|
kept.push({ path: projectRelative, reason: "edited" });
|
|
162
171
|
continue;
|
|
163
172
|
}
|
|
@@ -184,47 +193,7 @@ function describeKept(k) {
|
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
// src/guidance.ts
|
|
187
|
-
import { join as
|
|
188
|
-
|
|
189
|
-
// src/defaults.ts
|
|
190
|
-
import { existsSync } from "fs";
|
|
191
|
-
import { fileURLToPath } from "url";
|
|
192
|
-
import { dirname, join as join2 } from "pathe";
|
|
193
|
-
function defaultsRoot() {
|
|
194
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
195
|
-
const candidates = [
|
|
196
|
-
join2(here, "..", "defaults"),
|
|
197
|
-
// npm package: dist/../defaults
|
|
198
|
-
join2(here, "defaults"),
|
|
199
|
-
// same-dir defaults if present
|
|
200
|
-
join2(here, "..", "..", "defaults")
|
|
201
|
-
// src/ during some test runners
|
|
202
|
-
];
|
|
203
|
-
for (const c of candidates) {
|
|
204
|
-
if (existsSync(c)) return c;
|
|
205
|
-
}
|
|
206
|
-
return candidates[0];
|
|
207
|
-
}
|
|
208
|
-
function defaultWorkflowsDir() {
|
|
209
|
-
return join2(defaultsRoot(), "workflows");
|
|
210
|
-
}
|
|
211
|
-
function defaultTemplatesDir() {
|
|
212
|
-
return join2(defaultsRoot(), "templates");
|
|
213
|
-
}
|
|
214
|
-
function defaultMemoryDir() {
|
|
215
|
-
return join2(defaultsRoot(), "memory");
|
|
216
|
-
}
|
|
217
|
-
function defaultMcpYamlPath() {
|
|
218
|
-
return join2(defaultsRoot(), "mcp.yaml");
|
|
219
|
-
}
|
|
220
|
-
function defaultPricesYamlPath() {
|
|
221
|
-
return join2(defaultsRoot(), "prices.yaml");
|
|
222
|
-
}
|
|
223
|
-
function defaultSearchYamlPath() {
|
|
224
|
-
return join2(defaultsRoot(), "search.yaml");
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// src/guidance.ts
|
|
196
|
+
import { join as join2 } from "pathe";
|
|
228
197
|
function renderSddReadme(config) {
|
|
229
198
|
const paths = guidancePaths(config);
|
|
230
199
|
return `# Structured Vibe Coding (SDD)
|
|
@@ -302,14 +271,14 @@ async function refreshConfiguredGuidance(projectRoot, config) {
|
|
|
302
271
|
await writeConfiguredReadme(projectRoot, config);
|
|
303
272
|
}
|
|
304
273
|
async function writeConfiguredMemoryIndex(memoryRoot, config) {
|
|
305
|
-
const indexPath =
|
|
274
|
+
const indexPath = join2(memoryRoot, "index.md");
|
|
306
275
|
const paths = guidancePaths(config);
|
|
307
276
|
if (paths.memory === "memory" && paths.changes === "changes") return;
|
|
308
277
|
if (!await pathExists(indexPath)) return;
|
|
309
278
|
const existing = await readText(indexPath);
|
|
310
279
|
const state = generatedState(existing);
|
|
311
280
|
if (state === "edited") return;
|
|
312
|
-
const shippedPath =
|
|
281
|
+
const shippedPath = join2(defaultMemoryDir(), "index.md");
|
|
313
282
|
const shipped = await pathExists(shippedPath) ? await readText(shippedPath) : "";
|
|
314
283
|
if (state !== "unmodified" && existing.replace(/\r\n/g, "\n").trim() !== shipped.trim()) return;
|
|
315
284
|
const body = renderMemoryIndex(config);
|
|
@@ -317,7 +286,7 @@ async function writeConfiguredMemoryIndex(memoryRoot, config) {
|
|
|
317
286
|
await writeText(indexPath, markGenerated(body));
|
|
318
287
|
}
|
|
319
288
|
async function writeConfiguredReadme(projectRoot, config) {
|
|
320
|
-
const readmePath =
|
|
289
|
+
const readmePath = join2(sddRoot(projectRoot), "README.md");
|
|
321
290
|
if (!await pathExists(readmePath)) return;
|
|
322
291
|
const existing = await readText(readmePath);
|
|
323
292
|
if (generatedState(existing) !== "unmodified") return;
|
|
@@ -327,6 +296,95 @@ async function writeConfiguredReadme(projectRoot, config) {
|
|
|
327
296
|
await writeText(readmePath, marked);
|
|
328
297
|
}
|
|
329
298
|
|
|
299
|
+
// src/role/entrypoint-team.ts
|
|
300
|
+
import { homedir } from "os";
|
|
301
|
+
import { join as join3 } from "pathe";
|
|
302
|
+
function productTeamHome() {
|
|
303
|
+
const fromEnv = process.env.SDD_HOME?.trim();
|
|
304
|
+
if (fromEnv) return fromEnv;
|
|
305
|
+
return homedir();
|
|
306
|
+
}
|
|
307
|
+
function oneLine(value) {
|
|
308
|
+
const text = value.replace(/\s+/g, " ").replaceAll("|", "/").trim();
|
|
309
|
+
return text || "not named";
|
|
310
|
+
}
|
|
311
|
+
function toMember(role, name, order, source) {
|
|
312
|
+
return {
|
|
313
|
+
order,
|
|
314
|
+
name,
|
|
315
|
+
job: oneLine(role?.job ?? ""),
|
|
316
|
+
accepts: role && role.approvers.length > 0 ? oneLine(role.approvers.join(", ")) : "not named",
|
|
317
|
+
file: source === "repo" ? `.sdd/roles/${name}.md` : `~/.sdd/roles/${name}.md`,
|
|
318
|
+
source
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
async function listedRoles(root) {
|
|
322
|
+
const dir = join3(root, ".sdd", "roles");
|
|
323
|
+
if (!await pathExists(dir)) return [];
|
|
324
|
+
const listed = [];
|
|
325
|
+
for (const file of await listFiles(dir)) {
|
|
326
|
+
if (!file.endsWith(".md")) continue;
|
|
327
|
+
const name = file.slice(0, -".md".length);
|
|
328
|
+
if (canonicalRoleName(name) !== name) continue;
|
|
329
|
+
listed.push({ name, role: await readRole(root, name) });
|
|
330
|
+
}
|
|
331
|
+
return listed;
|
|
332
|
+
}
|
|
333
|
+
function byName(listed) {
|
|
334
|
+
return listed.filter((item) => !item.role?.hidden).map((item) => item.name).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
335
|
+
}
|
|
336
|
+
async function membersFrom(root, source) {
|
|
337
|
+
const listed = await listedRoles(root);
|
|
338
|
+
const teamFile = await pathExists(join3(root, ".sdd", "team.yaml"));
|
|
339
|
+
if (!teamFile && listed.length === 0) return null;
|
|
340
|
+
const known = new Map(listed.map((item) => [item.name, item.role]));
|
|
341
|
+
let names;
|
|
342
|
+
if (teamFile) {
|
|
343
|
+
const loaded = await readTeamConfig(root);
|
|
344
|
+
if ("team" in loaded) {
|
|
345
|
+
const seen = /* @__PURE__ */ new Set();
|
|
346
|
+
names = [];
|
|
347
|
+
for (const bot of loaded.team.bots) {
|
|
348
|
+
if (seen.has(bot.role)) continue;
|
|
349
|
+
seen.add(bot.role);
|
|
350
|
+
names.push(bot.role);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
names = byName(listed);
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
names = byName(listed);
|
|
357
|
+
}
|
|
358
|
+
return names.map((name, index) => toMember(known.get(name) ?? null, name, index + 1, source));
|
|
359
|
+
}
|
|
360
|
+
async function loadProductTeam(projectRoot) {
|
|
361
|
+
const repo = await membersFrom(projectRoot, "repo");
|
|
362
|
+
if (repo) return repo;
|
|
363
|
+
const user = await membersFrom(productTeamHome(), "user");
|
|
364
|
+
return user ?? [];
|
|
365
|
+
}
|
|
366
|
+
function renderProductTeamSection(members) {
|
|
367
|
+
if (members.length === 0) return "";
|
|
368
|
+
const lines = [
|
|
369
|
+
"## Product team",
|
|
370
|
+
"",
|
|
371
|
+
"Run these product roles in order. The stage hat (planner, implementer, reviewer) is how the host behaves inside one role's turn. It does not replace this team. Do not ask the human to repeat this roster.",
|
|
372
|
+
""
|
|
373
|
+
];
|
|
374
|
+
if (members[0]?.source === "user") {
|
|
375
|
+
lines.push(
|
|
376
|
+
"Source: user default (`~/.sdd`). It applies only because this repo has no product team.",
|
|
377
|
+
""
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
for (const member of members) {
|
|
381
|
+
lines.push(
|
|
382
|
+
`${member.order}. \`${member.name}\` \u2014 ${member.job}. Accepts: ${member.accepts}. File: \`${member.file}\``
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
return lines.join("\n");
|
|
386
|
+
}
|
|
387
|
+
|
|
330
388
|
// src/agents.ts
|
|
331
389
|
var AGENT_INTEGRATIONS = [
|
|
332
390
|
{
|
|
@@ -546,7 +604,7 @@ async function planOtherHostRemoval(projectRoot, keep) {
|
|
|
546
604
|
async function removalProblems(projectRoot, plan) {
|
|
547
605
|
const problems = [];
|
|
548
606
|
for (const rel of plan.remove) {
|
|
549
|
-
const dir =
|
|
607
|
+
const dir = dirname(rel);
|
|
550
608
|
if (!await canAccess(join4(projectRoot, dir), constants.W_OK)) {
|
|
551
609
|
problems.push(
|
|
552
610
|
`Cannot remove ${rel} (another host's sdd file): ${dir}/ is not writable (permission denied). Make it writable or delete the file yourself.`
|
|
@@ -565,12 +623,12 @@ async function executeRemoval(projectRoot, plan) {
|
|
|
565
623
|
throw new SddError(describeFsError(err, "remove", rel), { cause: err });
|
|
566
624
|
}
|
|
567
625
|
removed.push(rel);
|
|
568
|
-
let dir =
|
|
626
|
+
let dir = dirname(rel);
|
|
569
627
|
while (dir !== "." && dir !== "") {
|
|
570
628
|
const top = !dir.includes("/");
|
|
571
629
|
if (top && !PRUNABLE_PARENTS.includes(dir)) break;
|
|
572
630
|
ancestors.add(dir);
|
|
573
|
-
dir =
|
|
631
|
+
dir = dirname(dir);
|
|
574
632
|
}
|
|
575
633
|
}
|
|
576
634
|
const dirs = [...ancestors].sort((a, b) => b.split("/").length - a.split("/").length);
|
|
@@ -633,11 +691,13 @@ var SDD_AGENT_ROLES = [
|
|
|
633
691
|
{
|
|
634
692
|
id: "sdd",
|
|
635
693
|
description: "Default SDD agent. Use for any Spec-Driven Development work on the active change pack.",
|
|
636
|
-
roleLine: "Router:
|
|
694
|
+
roleLine: "Router: send the stage to the role that owns it, one item at a time.",
|
|
637
695
|
roleRules: [
|
|
638
|
-
"
|
|
639
|
-
"
|
|
640
|
-
"
|
|
696
|
+
"Owns: which role works this stage.",
|
|
697
|
+
"Never: accept a spec or record a decision.",
|
|
698
|
+
"Checks: the stage matches the work.",
|
|
699
|
+
"Ask when: the stage is missing or a hard gate is blocked.",
|
|
700
|
+
"Route with sdd role route. Rules in .sdd/routing.yaml win, then a role owns list. One item at a time. No owner asks the human.",
|
|
641
701
|
"If kickoff/brief says refine or analyze: follow <changes_path>/<id>/refine-brief.md \u2014 stage-scoped refine; never edit constitution; do not sdd next."
|
|
642
702
|
]
|
|
643
703
|
},
|
|
@@ -646,6 +706,10 @@ var SDD_AGENT_ROLES = [
|
|
|
646
706
|
description: "SDD planner. Use when filling intent, design, research, stories, or tasks \u2014 not large code changes.",
|
|
647
707
|
roleLine: "Planner: produce/update stage markdown only.",
|
|
648
708
|
roleRules: [
|
|
709
|
+
"Owns: stage markdown, not product code.",
|
|
710
|
+
"Never: accept a spec.",
|
|
711
|
+
"Checks: the artifact says what changes and how to tell.",
|
|
712
|
+
"Ask when: the scope is missing.",
|
|
649
713
|
"Do not make large product code changes unless the user explicitly overrides.",
|
|
650
714
|
"Keep artifacts short and actionable."
|
|
651
715
|
]
|
|
@@ -655,6 +719,10 @@ var SDD_AGENT_ROLES = [
|
|
|
655
719
|
description: "SDD implementer. Use when coding the active change (implement stage).",
|
|
656
720
|
roleLine: "Implementer: code only for the active change pack.",
|
|
657
721
|
roleRules: [
|
|
722
|
+
"Owns: code for this change pack.",
|
|
723
|
+
"Never: accept a spec or widen the change.",
|
|
724
|
+
"Checks: the diff matches the tasks and acceptance.",
|
|
725
|
+
"Ask when: a task or a design constraint is missing.",
|
|
658
726
|
"Follow tasks.md / acceptance; honor arb-decision and design constraints.",
|
|
659
727
|
"Do not expand scope beyond the change title/intent."
|
|
660
728
|
]
|
|
@@ -664,20 +732,24 @@ var SDD_AGENT_ROLES = [
|
|
|
664
732
|
description: "SDD reviewer. Use before verify/complete or to check work against the change pack.",
|
|
665
733
|
roleLine: "Reviewer: compare work to acceptance and stage artifacts.",
|
|
666
734
|
roleRules: [
|
|
735
|
+
"Owns: the gap list.",
|
|
736
|
+
"Never: accept a spec or treat a claim as evidence.",
|
|
737
|
+
"Checks: the change matches acceptance.",
|
|
738
|
+
"Ask when: acceptance does not say how to tell it worked.",
|
|
667
739
|
"List gaps; suggest minimal fixes only.",
|
|
668
740
|
"Remind user to run sdd verify before sdd complete."
|
|
669
741
|
]
|
|
670
742
|
}
|
|
671
743
|
];
|
|
672
|
-
function agentFiles(integ, config) {
|
|
744
|
+
function agentFiles(integ, config, team = []) {
|
|
673
745
|
const roles = integ.rolesToInstall?.length ? SDD_AGENT_ROLES.filter((r) => integ.rolesToInstall.includes(r.id)) : SDD_AGENT_ROLES;
|
|
674
746
|
return [
|
|
675
|
-
{ rel: join4(".sdd", "protocol.md"), content: renderProtocolMd(config) },
|
|
747
|
+
{ rel: join4(".sdd", "protocol.md"), content: renderProtocolMd(config, team) },
|
|
676
748
|
...roles.map((role) => ({
|
|
677
749
|
rel: integ.rolePath(role.id),
|
|
678
|
-
content: renderThinAgent(role, config)
|
|
750
|
+
content: renderThinAgent(role, config, team)
|
|
679
751
|
})),
|
|
680
|
-
{ rel: "AGENTS.md", content: renderAgentsMd(integ, config) }
|
|
752
|
+
{ rel: "AGENTS.md", content: renderAgentsMd(integ, config, team) }
|
|
681
753
|
];
|
|
682
754
|
}
|
|
683
755
|
async function planAgentIntegration(opts) {
|
|
@@ -687,7 +759,8 @@ async function planAgentIntegration(opts) {
|
|
|
687
759
|
const problems = [];
|
|
688
760
|
const writes = [];
|
|
689
761
|
const config = await pathExists(configPath(root)) ? await loadConfig(root) : defaultConfig();
|
|
690
|
-
|
|
762
|
+
const team = await loadProductTeam(root);
|
|
763
|
+
for (const { rel, content } of agentFiles(integ, config, team)) {
|
|
691
764
|
const full = join4(root, rel);
|
|
692
765
|
if (!rel.startsWith(".sdd/")) {
|
|
693
766
|
const link = await symlinkOnPath(root, rel);
|
|
@@ -698,8 +771,8 @@ async function planAgentIntegration(opts) {
|
|
|
698
771
|
}
|
|
699
772
|
const kind = await pathKind(full);
|
|
700
773
|
if (kind === "missing") {
|
|
701
|
-
let anc =
|
|
702
|
-
while (anc !== "." && await pathKind(join4(root, anc)) === "missing") anc =
|
|
774
|
+
let anc = dirname(rel);
|
|
775
|
+
while (anc !== "." && await pathKind(join4(root, anc)) === "missing") anc = dirname(anc);
|
|
703
776
|
const ancFull = anc === "." ? root : join4(root, anc);
|
|
704
777
|
const ancKind = await pathKind(ancFull);
|
|
705
778
|
if (ancKind !== "dir" && ancKind !== "symlink") {
|
|
@@ -834,9 +907,12 @@ async function installAgentIntegrations(opts) {
|
|
|
834
907
|
}
|
|
835
908
|
return { created, skipped, targets: opts.targets };
|
|
836
909
|
}
|
|
837
|
-
function renderThinAgent(role, config) {
|
|
910
|
+
function renderThinAgent(role, config, team = []) {
|
|
838
911
|
const paths = guidancePaths(config ?? { memory_path: "memory", changes_path: "changes" });
|
|
839
912
|
const rules = role.roleRules.map((r) => `- ${r.replaceAll("<changes_path>", paths.changes)}`).join("\n");
|
|
913
|
+
const productTeam = team.length > 0 ? `
|
|
914
|
+
${renderProductTeamSection(team)}
|
|
915
|
+
` : "";
|
|
840
916
|
return `---
|
|
841
917
|
name: ${role.id}
|
|
842
918
|
description: ${role.description}
|
|
@@ -856,7 +932,7 @@ ${role.roleLine}
|
|
|
856
932
|
## Role rules
|
|
857
933
|
|
|
858
934
|
${rules}
|
|
859
|
-
|
|
935
|
+
${productTeam}
|
|
860
936
|
## Done
|
|
861
937
|
|
|
862
938
|
Remind the human: \`sdd verify\` then \`sdd next\` / \`sdd complete\` as appropriate.
|
|
@@ -879,6 +955,7 @@ async function loadInstalledAgent(projectRoot) {
|
|
|
879
955
|
async function writeAgentHandoff(projectRoot, config, changeId, budget, runtime) {
|
|
880
956
|
const ctx = await buildContext(projectRoot, config, changeId);
|
|
881
957
|
const body = await buildAgentPrompt(ctx, config, projectRoot, budget, runtime);
|
|
958
|
+
const team = renderProductTeamSection(await loadProductTeam(projectRoot));
|
|
882
959
|
const outPath = join4(sddRoot(projectRoot), "handoff.md");
|
|
883
960
|
const header = `# SDD handoff (auto)
|
|
884
961
|
|
|
@@ -890,7 +967,10 @@ Also read: \`.sdd/active-context.md\`, \`.sdd/protocol.md\`, \`${guidancePaths(c
|
|
|
890
967
|
---
|
|
891
968
|
|
|
892
969
|
`;
|
|
893
|
-
|
|
970
|
+
const teamBlock = team ? `
|
|
971
|
+
${team}
|
|
972
|
+
` : "";
|
|
973
|
+
await writeText(outPath, header + teamBlock + body + "\n");
|
|
894
974
|
return outPath;
|
|
895
975
|
}
|
|
896
976
|
function agentKickoffMessage(opts) {
|
|
@@ -920,7 +1000,8 @@ function agentKickoffMessage(opts) {
|
|
|
920
1000
|
}
|
|
921
1001
|
async function refreshGeneratedAgentFiles(projectRoot, config) {
|
|
922
1002
|
const installed = await loadInstalledAgent(projectRoot);
|
|
923
|
-
const
|
|
1003
|
+
const team = await loadProductTeam(projectRoot);
|
|
1004
|
+
const files = installed ? agentFiles(installed.integration, config, team) : [{ rel: join4(".sdd", "protocol.md"), content: renderProtocolMd(config, team) }];
|
|
924
1005
|
for (const { rel, content } of files) {
|
|
925
1006
|
const full = join4(projectRoot, rel);
|
|
926
1007
|
if (!await pathExists(full)) {
|
|
@@ -945,6 +1026,10 @@ async function refreshActiveAgentContext(projectRoot) {
|
|
|
945
1026
|
await refreshGeneratedAgentFiles(projectRoot, config);
|
|
946
1027
|
const activeId = await getActiveChangeId(projectRoot, config);
|
|
947
1028
|
const outPath = join4(markerDir, "active-context.md");
|
|
1029
|
+
const team = renderProductTeamSection(await loadProductTeam(projectRoot));
|
|
1030
|
+
const teamBlock = team ? `
|
|
1031
|
+
${team}
|
|
1032
|
+
` : "";
|
|
948
1033
|
if (!activeId) {
|
|
949
1034
|
const empty = `# SDD active context
|
|
950
1035
|
|
|
@@ -953,7 +1038,7 @@ _No active change._ Run \`sdd new "\u2026"\` or **SDD: New Change** in the IDE.
|
|
|
953
1038
|
Then re-run \`sdd agents refresh\` (or advance a stage) to update this file.
|
|
954
1039
|
|
|
955
1040
|
Protocol: \`.sdd/protocol.md\`
|
|
956
|
-
`;
|
|
1041
|
+
${teamBlock}`;
|
|
957
1042
|
await writeText(outPath, empty);
|
|
958
1043
|
return outPath;
|
|
959
1044
|
}
|
|
@@ -971,16 +1056,17 @@ Protocol: \`.sdd/protocol.md\`
|
|
|
971
1056
|
\`\`\`
|
|
972
1057
|
${status}
|
|
973
1058
|
\`\`\`
|
|
974
|
-
|
|
1059
|
+
${teamBlock}
|
|
975
1060
|
${handoff}
|
|
976
1061
|
`;
|
|
977
1062
|
await writeText(outPath, body);
|
|
978
1063
|
return outPath;
|
|
979
1064
|
}
|
|
980
1065
|
var PROTOCOL_MD = renderProtocolMd({ memory_path: "memory", changes_path: "changes" });
|
|
981
|
-
function renderProtocolMd(config) {
|
|
1066
|
+
function renderProtocolMd(config, team = []) {
|
|
982
1067
|
const paths = guidancePaths(config);
|
|
983
1068
|
const fence = "```";
|
|
1069
|
+
const teamLines = team.length > 0 ? ["", ...renderProductTeamSection(team).split("\n")] : [];
|
|
984
1070
|
return [
|
|
985
1071
|
"# SDD protocol",
|
|
986
1072
|
"",
|
|
@@ -1003,6 +1089,7 @@ function renderProtocolMd(config) {
|
|
|
1003
1089
|
`3. \`${paths.constitution}\` if present (non-negotiables agents must not violate)`,
|
|
1004
1090
|
"4. `meta.yaml` + artifacts for the **current stage** (and prior stages if needed)",
|
|
1005
1091
|
`5. \`${paths.memoryIndex}\` (documentation map) if present, then linked memory pages when architecture or conventions matter`,
|
|
1092
|
+
...teamLines,
|
|
1006
1093
|
"",
|
|
1007
1094
|
"## Stage behavior",
|
|
1008
1095
|
"",
|
|
@@ -1051,8 +1138,9 @@ function renderProtocolMd(config) {
|
|
|
1051
1138
|
""
|
|
1052
1139
|
].join("\n");
|
|
1053
1140
|
}
|
|
1054
|
-
function renderAgentsMd(integ, config) {
|
|
1141
|
+
function renderAgentsMd(integ, config, team = []) {
|
|
1055
1142
|
const paths = guidancePaths(config ?? { memory_path: "memory", changes_path: "changes" });
|
|
1143
|
+
const teamLines = team.length > 0 ? ["", ...renderProductTeamSection(team).split("\n")] : [];
|
|
1056
1144
|
return [
|
|
1057
1145
|
"# Agents",
|
|
1058
1146
|
"",
|
|
@@ -1067,6 +1155,7 @@ function renderAgentsMd(integ, config) {
|
|
|
1067
1155
|
`| Constitution | \`${paths.constitution}\` if present |`,
|
|
1068
1156
|
`| Doc map (stable) | \`${paths.memoryIndex}\` if present |`,
|
|
1069
1157
|
`| Active change | \`${paths.changes}/<id>/\` |`,
|
|
1158
|
+
...teamLines,
|
|
1070
1159
|
"",
|
|
1071
1160
|
"| AI agent | Files |",
|
|
1072
1161
|
"|----------|--------|",
|
|
@@ -1085,16 +1174,12 @@ export {
|
|
|
1085
1174
|
saveGeneratedManifest,
|
|
1086
1175
|
reconcileGeneratedTree,
|
|
1087
1176
|
describeKept,
|
|
1088
|
-
defaultsRoot,
|
|
1089
|
-
defaultWorkflowsDir,
|
|
1090
|
-
defaultTemplatesDir,
|
|
1091
|
-
defaultMemoryDir,
|
|
1092
|
-
defaultMcpYamlPath,
|
|
1093
|
-
defaultPricesYamlPath,
|
|
1094
|
-
defaultSearchYamlPath,
|
|
1095
1177
|
renderSddReadme,
|
|
1096
1178
|
renderMemoryIndex,
|
|
1097
1179
|
refreshConfiguredGuidance,
|
|
1180
|
+
productTeamHome,
|
|
1181
|
+
loadProductTeam,
|
|
1182
|
+
renderProductTeamSection,
|
|
1098
1183
|
AGENT_INTEGRATIONS,
|
|
1099
1184
|
ALL_AGENT_TARGETS,
|
|
1100
1185
|
DEFAULT_INIT_INTEGRATION,
|