@warpgogol/forge 0.4.0 → 0.5.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.
Files changed (137) hide show
  1. package/AGENTS.md +116 -4
  2. package/dist/os/adr/handlers/validate.d.ts.map +1 -1
  3. package/dist/os/adr/handlers/validate.js +31 -3
  4. package/dist/os/adr/handlers/validate.js.map +1 -1
  5. package/dist/os/core/core.module.d.ts.map +1 -1
  6. package/dist/os/core/core.module.js +76 -2
  7. package/dist/os/core/core.module.js.map +1 -1
  8. package/dist/os/core/handlers/knowledge-compact.d.ts +4 -0
  9. package/dist/os/core/handlers/knowledge-compact.d.ts.map +1 -0
  10. package/dist/os/core/handlers/knowledge-compact.js +198 -0
  11. package/dist/os/core/handlers/knowledge-compact.js.map +1 -0
  12. package/dist/os/rfc/handlers/lifecycle.d.ts.map +1 -1
  13. package/dist/os/rfc/handlers/lifecycle.js +3 -4
  14. package/dist/os/rfc/handlers/lifecycle.js.map +1 -1
  15. package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -1
  16. package/dist/os/rfc/handlers/validate-rules.js +41 -3
  17. package/dist/os/rfc/handlers/validate-rules.js.map +1 -1
  18. package/dist/os/spec/spec-schema.d.ts +1 -1
  19. package/dist/src/config/forge-config.d.ts +67 -3
  20. package/dist/src/config/forge-config.d.ts.map +1 -1
  21. package/dist/src/config/forge-config.js +86 -2
  22. package/dist/src/config/forge-config.js.map +1 -1
  23. package/dist/src/index.d.ts +3 -1
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/index.js +6 -2
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/knowledge/budgets.d.ts +38 -0
  28. package/dist/src/knowledge/budgets.d.ts.map +1 -0
  29. package/dist/src/knowledge/budgets.js +157 -0
  30. package/dist/src/knowledge/budgets.js.map +1 -0
  31. package/dist/src/knowledge/compact.d.ts +47 -0
  32. package/dist/src/knowledge/compact.d.ts.map +1 -0
  33. package/dist/src/knowledge/compact.js +322 -0
  34. package/dist/src/knowledge/compact.js.map +1 -0
  35. package/dist/src/knowledge/index.d.ts +7 -0
  36. package/dist/src/knowledge/index.d.ts.map +1 -0
  37. package/dist/src/knowledge/index.js +19 -0
  38. package/dist/src/knowledge/index.js.map +1 -0
  39. package/dist/src/knowledge/parse.d.ts +3 -0
  40. package/dist/src/knowledge/parse.d.ts.map +1 -0
  41. package/dist/src/knowledge/parse.js +205 -0
  42. package/dist/src/knowledge/parse.js.map +1 -0
  43. package/dist/src/knowledge/promote.d.ts +37 -0
  44. package/dist/src/knowledge/promote.d.ts.map +1 -0
  45. package/dist/src/knowledge/promote.js +120 -0
  46. package/dist/src/knowledge/promote.js.map +1 -0
  47. package/dist/src/knowledge/schema.d.ts +61 -0
  48. package/dist/src/knowledge/schema.d.ts.map +1 -0
  49. package/dist/src/knowledge/schema.js +64 -0
  50. package/dist/src/knowledge/schema.js.map +1 -0
  51. package/dist/src/knowledge/serialize.d.ts +3 -0
  52. package/dist/src/knowledge/serialize.d.ts.map +1 -0
  53. package/dist/src/knowledge/serialize.js +67 -0
  54. package/dist/src/knowledge/serialize.js.map +1 -0
  55. package/dist/src/onboarding/agents-generate.d.ts +15 -0
  56. package/dist/src/onboarding/agents-generate.d.ts.map +1 -1
  57. package/dist/src/onboarding/agents-generate.js +118 -58
  58. package/dist/src/onboarding/agents-generate.js.map +1 -1
  59. package/dist/src/onboarding/create.d.ts.map +1 -1
  60. package/dist/src/onboarding/create.js +62 -3
  61. package/dist/src/onboarding/create.js.map +1 -1
  62. package/dist/src/onboarding/doctor.d.ts +13 -1
  63. package/dist/src/onboarding/doctor.d.ts.map +1 -1
  64. package/dist/src/onboarding/doctor.js +527 -11
  65. package/dist/src/onboarding/doctor.js.map +1 -1
  66. package/dist/src/onboarding/init.d.ts +8 -1
  67. package/dist/src/onboarding/init.d.ts.map +1 -1
  68. package/dist/src/onboarding/init.js +33 -2
  69. package/dist/src/onboarding/init.js.map +1 -1
  70. package/dist/src/onboarding/memory-scaffold.d.ts +18 -0
  71. package/dist/src/onboarding/memory-scaffold.d.ts.map +1 -0
  72. package/dist/src/onboarding/memory-scaffold.js +151 -0
  73. package/dist/src/onboarding/memory-scaffold.js.map +1 -0
  74. package/dist/src/onboarding/nested-agents-generate.d.ts +6 -2
  75. package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -1
  76. package/dist/src/onboarding/nested-agents-generate.js +34 -9
  77. package/dist/src/onboarding/nested-agents-generate.js.map +1 -1
  78. package/dist/src/onboarding/nested-agents-templates.d.ts +23 -1
  79. package/dist/src/onboarding/nested-agents-templates.d.ts.map +1 -1
  80. package/dist/src/onboarding/nested-agents-templates.js +138 -8
  81. package/dist/src/onboarding/nested-agents-templates.js.map +1 -1
  82. package/dist/src/onboarding/profile-validate.d.ts +13 -0
  83. package/dist/src/onboarding/profile-validate.d.ts.map +1 -0
  84. package/dist/src/onboarding/profile-validate.js +109 -0
  85. package/dist/src/onboarding/profile-validate.js.map +1 -0
  86. package/dist/src/onboarding/upgrade.d.ts +5 -0
  87. package/dist/src/onboarding/upgrade.d.ts.map +1 -1
  88. package/dist/src/onboarding/upgrade.js +27 -1
  89. package/dist/src/onboarding/upgrade.js.map +1 -1
  90. package/dist/src/onboarding/workspace-discovery.d.ts +4 -3
  91. package/dist/src/onboarding/workspace-discovery.d.ts.map +1 -1
  92. package/dist/src/onboarding/workspace-discovery.js +51 -3
  93. package/dist/src/onboarding/workspace-discovery.js.map +1 -1
  94. package/dist/src/profiles/profile-schema.d.ts +116 -0
  95. package/dist/src/profiles/profile-schema.d.ts.map +1 -0
  96. package/dist/src/profiles/profile-schema.js +94 -0
  97. package/dist/src/profiles/profile-schema.js.map +1 -0
  98. package/dist/src/profiles/stack-profile.d.ts +41 -1
  99. package/dist/src/profiles/stack-profile.d.ts.map +1 -1
  100. package/dist/src/profiles/stack-profile.js +9 -0
  101. package/dist/src/profiles/stack-profile.js.map +1 -1
  102. package/dist/src/profiles/terminology-utils.d.ts +8 -0
  103. package/dist/src/profiles/terminology-utils.d.ts.map +1 -0
  104. package/dist/src/profiles/terminology-utils.js +31 -0
  105. package/dist/src/profiles/terminology-utils.js.map +1 -0
  106. package/dist/src/validators/skill-validate.d.ts +15 -0
  107. package/dist/src/validators/skill-validate.d.ts.map +1 -1
  108. package/dist/src/validators/skill-validate.js +204 -2
  109. package/dist/src/validators/skill-validate.js.map +1 -1
  110. package/package.json +11 -3
  111. package/profiles/astro-typescript-turborepo.yaml +87 -0
  112. package/profiles/editframe-html-templates/composition-agents.md +34 -0
  113. package/profiles/editframe-html-templates/composition.html +32 -0
  114. package/profiles/editframe-html.yaml +283 -0
  115. package/profiles/forge-shell.yaml +77 -0
  116. package/profiles/phaser-turborepo.yaml +87 -0
  117. package/skills/fo/fo-doc-audit/SKILL.md +6 -6
  118. package/skills/fo/fo-handoff/SKILL.md +2 -1
  119. package/skills/fo/fo-idea-implement/SKILL.md +44 -2
  120. package/skills/fo/fo-idea-plan/SKILL.md +1 -0
  121. package/skills/fo/fo-knowledge-distill/SKILL.md +170 -0
  122. package/skills/fo/fo-memory-sync/SKILL.md +6 -3
  123. package/skills/fo/fo-memory-sync/fix-patterns.md +38 -4
  124. package/skills/fo/fo-review/SKILL.md +2 -2
  125. package/skills/fo/fo-session-retro/SKILL.md +21 -11
  126. package/skills/fo/fo-session-save/SKILL.md +1 -1
  127. package/skills/fo/fo-session-save/fix-patterns.md +53 -29
  128. package/skills/fo/fo-session-save/learned-principles.md +59 -8
  129. package/skills/meta/forge-bootstrap/SKILL.md +16 -4
  130. package/skills/meta/forge-bootstrap/operator-profile-template.md +1 -0
  131. package/skills/meta/skill-create/SKILL.md +1 -1
  132. package/skills/shared/grilling/SKILL.md +1 -1
  133. package/skills/shared/grilling/learned-principles.md +14 -0
  134. package/skills/shared/grilling/qa-log.md +105 -6
  135. package/skills/shared/knowledge/learned-principles.md +5 -0
  136. package/skills/shared/windows-ai-tooling/SKILL.md +154 -1
  137. package/skills/shared/writing-great-skills/SKILL.md +74 -1
@@ -0,0 +1,120 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Cross-skill duplicate detection and promotion planning for shared knowledge layer (RFC-0663).</purpose>
4
+ <non-goals>
5
+ <item>Do not read or write files — that is handled by parse.ts/serialize.ts and the distill skill.</item>
6
+ <item>Do not execute promotions — that is the operator's decision inside fo-knowledge-distill.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>RFC-0663: initial cross-skill duplicate detection and promotion planning.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+ const STOP_WORDS = new Set(["the", "a", "an"]);
14
+ const MIN_CONTAINMENT_LENGTH = 20;
15
+ const MIN_CONTAINMENT_RATIO = 0.6;
16
+ export function normalizeTitle(title) {
17
+ let result = title.toLowerCase();
18
+ result = result.replace(/[^\p{L}\p{N}\s]/gu, " ");
19
+ result = result.replace(/\s+/g, " ").trim();
20
+ const words = result.split(" ").filter((w) => w.length > 0 && !STOP_WORDS.has(w));
21
+ return words.join(" ");
22
+ }
23
+ function isExcludedEntry(meta) {
24
+ if (meta.status === "stale" || meta.status === "archived")
25
+ return true;
26
+ if (meta.promotedTo !== undefined && meta.promotedTo !== null)
27
+ return true;
28
+ return false;
29
+ }
30
+ function isLinkedPair(metaA, metaB) {
31
+ if (metaA.supersedes && metaB.id && metaA.supersedes.includes(metaB.id))
32
+ return true;
33
+ if (metaB.supersedes && metaA.id && metaB.supersedes.includes(metaA.id))
34
+ return true;
35
+ if (metaA.promotedTo && metaB.id && metaA.promotedTo === `shared/${metaB.id}`)
36
+ return true;
37
+ if (metaB.promotedTo && metaA.id && metaB.promotedTo === `shared/${metaA.id}`)
38
+ return true;
39
+ return false;
40
+ }
41
+ function collectActiveEntries(files) {
42
+ const active = [];
43
+ for (const file of files) {
44
+ if (file.parsed.isKnowledgeAdjacent)
45
+ continue;
46
+ if (file.parsed.parseIssues.length > 0)
47
+ continue;
48
+ for (const entry of file.parsed.entries) {
49
+ if (isExcludedEntry(entry.meta))
50
+ continue;
51
+ const normalized = normalizeTitle(entry.title);
52
+ if (normalized.length === 0)
53
+ continue;
54
+ active.push({ skill: file.skill, entry, normalizedTitle: normalized });
55
+ }
56
+ }
57
+ return active;
58
+ }
59
+ export function detectDuplicatePrinciples(files) {
60
+ const active = collectActiveEntries(files);
61
+ const pairs = [];
62
+ for (let i = 0; i < active.length; i++) {
63
+ for (let j = i + 1; j < active.length; j++) {
64
+ const a = active[i];
65
+ const b = active[j];
66
+ if (isLinkedPair(a.entry.meta, b.entry.meta))
67
+ continue;
68
+ if (a.normalizedTitle === b.normalizedTitle) {
69
+ pairs.push({
70
+ a: { skill: a.skill, entryId: a.entry.meta.id, title: a.entry.title },
71
+ b: { skill: b.skill, entryId: b.entry.meta.id, title: b.entry.title },
72
+ normalizedTitle: a.normalizedTitle,
73
+ kind: "exact",
74
+ });
75
+ continue;
76
+ }
77
+ const shorter = a.normalizedTitle.length <= b.normalizedTitle.length ? a : b;
78
+ const longer = a.normalizedTitle.length <= b.normalizedTitle.length ? b : a;
79
+ if (shorter.normalizedTitle.length < MIN_CONTAINMENT_LENGTH)
80
+ continue;
81
+ if (shorter.normalizedTitle.length < longer.normalizedTitle.length * MIN_CONTAINMENT_RATIO)
82
+ continue;
83
+ if (longer.normalizedTitle.includes(shorter.normalizedTitle)) {
84
+ pairs.push({
85
+ a: { skill: a.skill, entryId: a.entry.meta.id, title: a.entry.title },
86
+ b: { skill: b.skill, entryId: b.entry.meta.id, title: b.entry.title },
87
+ normalizedTitle: shorter.normalizedTitle,
88
+ kind: "containment",
89
+ });
90
+ }
91
+ }
92
+ }
93
+ return pairs;
94
+ }
95
+ export function planPromotion(sources, merged, nextSharedId, today) {
96
+ const totalConfirmations = sources.reduce((sum, s) => sum + (s.entry.meta.confirmations ?? 0), 0);
97
+ const promotedFrom = sources.map((s) => `${s.skill}/${s.entry.meta.id}`);
98
+ const sharedMeta = {
99
+ id: nextSharedId,
100
+ layer: "L2",
101
+ created: today,
102
+ lastConfirmedAt: today,
103
+ confirmations: totalConfirmations,
104
+ status: "active",
105
+ promotedFrom,
106
+ };
107
+ const sharedEntry = {
108
+ meta: sharedMeta,
109
+ title: merged.title,
110
+ body: merged.body,
111
+ lineStart: 0,
112
+ };
113
+ const localPointers = sources.map((s) => ({
114
+ skill: s.skill,
115
+ file: s.file,
116
+ entryId: s.entry.meta.id,
117
+ }));
118
+ return { sharedEntry, localPointers };
119
+ }
120
+ //# sourceMappingURL=promote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promote.js","sourceRoot":"","sources":["../../../src/knowledge/promote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { z } from "zod";
2
+ export type KnowledgeLayer = "L0" | "L1" | "L2";
3
+ export type KnowledgeEntryStatus = "active" | "stale" | "superseded" | "archived";
4
+ export declare const knowledgeEntryMetaSchema: z.ZodObject<{
5
+ id: z.ZodString;
6
+ layer: z.ZodEnum<{
7
+ L0: "L0";
8
+ L1: "L1";
9
+ L2: "L2";
10
+ }>;
11
+ created: z.ZodString;
12
+ lastConfirmedAt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNull]>>;
13
+ confirmations: z.ZodOptional<z.ZodNumber>;
14
+ expiresAt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNull]>>;
15
+ supersedes: z.ZodOptional<z.ZodArray<z.ZodString>>;
16
+ promotedTo: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNull]>>;
17
+ promotedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
18
+ status: z.ZodEnum<{
19
+ active: "active";
20
+ stale: "stale";
21
+ superseded: "superseded";
22
+ archived: "archived";
23
+ }>;
24
+ }, z.core.$strip>;
25
+ export interface KnowledgeEntryMeta {
26
+ id: string;
27
+ layer: KnowledgeLayer;
28
+ created: string;
29
+ lastConfirmedAt?: string | null;
30
+ confirmations?: number;
31
+ expiresAt?: string | null;
32
+ supersedes?: string[];
33
+ promotedTo?: string | null;
34
+ promotedFrom?: string[];
35
+ status: KnowledgeEntryStatus;
36
+ }
37
+ export interface KnowledgeEntry {
38
+ meta: KnowledgeEntryMeta;
39
+ title: string;
40
+ body: string;
41
+ lineStart: number;
42
+ }
43
+ export interface LegacySection {
44
+ text: string;
45
+ lineStart: number;
46
+ }
47
+ export interface ParseIssue {
48
+ line: number;
49
+ message: string;
50
+ entryId?: string;
51
+ }
52
+ export interface ParsedKnowledgeFile {
53
+ path: string;
54
+ layer: KnowledgeLayer | null;
55
+ preamble: string;
56
+ entries: KnowledgeEntry[];
57
+ legacySections: LegacySection[];
58
+ parseIssues: ParseIssue[];
59
+ isKnowledgeAdjacent: boolean;
60
+ }
61
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/knowledge/schema.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAChD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,GAAG,UAAU,CAAC;AAmBlF,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;iBAkCnC,CAAC;AAEH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,mBAAmB,EAAE,OAAO,CAAC;CAC9B"}
@@ -0,0 +1,64 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Define the Zod metadata schema for knowledge entries with layer-specific refinements (RFC-0660).</purpose>
4
+ <non-goals>
5
+ <item>Do not parse markdown — that is handled by parse.ts.</item>
6
+ <item>Do not serialize — that is handled by serialize.ts.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>RFC-0660: initial knowledge entry metadata schema with L0/L1/L2 layer-specific refinements.</item>
11
+ <item>RFC-0663: added promotedFrom field for shared-layer provenance tracking.</item>
12
+ </CHANGE_SUMMARY>
13
+ */
14
+ import { z } from "zod";
15
+ const ENTRY_ID_PATTERN = /^K-\d{4}$/;
16
+ const PROMOTED_TO_PATTERN = /^shared\/K-\d{4}$/;
17
+ const PROMOTED_FROM_PATTERN = /^[a-z0-9-]+\/K-\d{4}$/;
18
+ const baseMetaSchema = z.object({
19
+ id: z.string().regex(ENTRY_ID_PATTERN, "id must match ^K-\\d{4}$"),
20
+ layer: z.enum(["L0", "L1", "L2"]),
21
+ created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "created must be YYYY-MM-DD"),
22
+ lastConfirmedAt: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
23
+ confirmations: z.number().int().min(0).optional(),
24
+ expiresAt: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
25
+ supersedes: z.array(z.string().regex(ENTRY_ID_PATTERN)).optional(),
26
+ promotedTo: z.union([z.string().regex(PROMOTED_TO_PATTERN), z.null()]).optional(),
27
+ promotedFrom: z.array(z.string().regex(PROMOTED_FROM_PATTERN)).optional(),
28
+ status: z.enum(["active", "stale", "superseded", "archived"]),
29
+ });
30
+ export const knowledgeEntryMetaSchema = baseMetaSchema.superRefine((data, ctx) => {
31
+ if (data.layer === "L0" || data.layer === "L1") {
32
+ if (data.confirmations !== undefined) {
33
+ ctx.addIssue({
34
+ code: z.ZodIssueCode.custom,
35
+ message: `confirmations is forbidden on ${data.layer} entries`,
36
+ path: ["confirmations"],
37
+ });
38
+ }
39
+ if (data.lastConfirmedAt !== undefined) {
40
+ ctx.addIssue({
41
+ code: z.ZodIssueCode.custom,
42
+ message: `lastConfirmedAt is forbidden on ${data.layer} entries`,
43
+ path: ["lastConfirmedAt"],
44
+ });
45
+ }
46
+ }
47
+ if (data.layer === "L2") {
48
+ if (data.confirmations === undefined) {
49
+ ctx.addIssue({
50
+ code: z.ZodIssueCode.custom,
51
+ message: "confirmations is required on L2 entries",
52
+ path: ["confirmations"],
53
+ });
54
+ }
55
+ if (data.lastConfirmedAt === undefined) {
56
+ ctx.addIssue({
57
+ code: z.ZodIssueCode.custom,
58
+ message: "lastConfirmedAt is required on L2 entries",
59
+ path: ["lastConfirmedAt"],
60
+ });
61
+ }
62
+ }
63
+ });
64
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/knowledge/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ParsedKnowledgeFile } from "./schema.ts";
2
+ export declare function serializeKnowledgeFile(parsed: ParsedKnowledgeFile): string;
3
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../../src/knowledge/serialize.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,mBAAmB,EAAsB,MAAM,aAAa,CAAC;AAoC3E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAsB1E"}
@@ -0,0 +1,67 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Round-trip serializer for knowledge files — reconstructs markdown from ParsedKnowledgeFile (RFC-0660).</purpose>
4
+ <non-goals>
5
+ <item>Do not parse — that is handled by parse.ts.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>RFC-0660: initial round-trip serializer for structured knowledge entries.</item>
10
+ <item>RFC-0663: added promotedFrom to FIELD_ORDER for shared-layer provenance.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+ const FIELD_ORDER = [
14
+ "id",
15
+ "layer",
16
+ "created",
17
+ "lastConfirmedAt",
18
+ "confirmations",
19
+ "expiresAt",
20
+ "supersedes",
21
+ "promotedTo",
22
+ "promotedFrom",
23
+ "status",
24
+ ];
25
+ function formatYamlValue(value) {
26
+ if (value === null)
27
+ return "null";
28
+ if (Array.isArray(value)) {
29
+ if (value.length === 0)
30
+ return "[]";
31
+ return `[${value.join(", ")}]`;
32
+ }
33
+ if (typeof value === "string")
34
+ return value;
35
+ return String(value);
36
+ }
37
+ function serializeMeta(meta) {
38
+ const lines = ["```knowledge-entry"];
39
+ for (const field of FIELD_ORDER) {
40
+ const value = meta[field];
41
+ if (value === undefined)
42
+ continue;
43
+ lines.push(`${field}: ${formatYamlValue(value)}`);
44
+ }
45
+ lines.push("```");
46
+ return lines.join("\n");
47
+ }
48
+ export function serializeKnowledgeFile(parsed) {
49
+ const parts = [];
50
+ if (parsed.preamble) {
51
+ parts.push(parsed.preamble);
52
+ }
53
+ for (const entry of parsed.entries) {
54
+ parts.push(`### ${entry.meta.id}: ${entry.title}`);
55
+ parts.push("");
56
+ parts.push(serializeMeta(entry.meta));
57
+ if (entry.body) {
58
+ parts.push("");
59
+ parts.push(entry.body);
60
+ }
61
+ }
62
+ for (const legacy of parsed.legacySections) {
63
+ parts.push(legacy.text);
64
+ }
65
+ return parts.join("\n").replace(/\n{3,}/g, "\n\n") + "\n";
66
+ }
67
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../../src/knowledge/serialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;EAWE;AAIF,MAAM,WAAW,GAAiC;IAChD,IAAI;IACJ,OAAO;IACP,SAAS;IACT,iBAAiB;IACjB,eAAe;IACf,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,QAAQ;CACT,CAAC;AAEF,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACjC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,aAAa,CAAC,IAAwB;IAC7C,MAAM,KAAK,GAAa,CAAC,oBAAoB,CAAC,CAAC;IAC/C,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAA2B;IAChE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;AAC5D,CAAC"}
@@ -1,4 +1,13 @@
1
1
  import type { ForgeCommandInput, ForgeCommandResult, ForgeRuntimeContext } from "../types.ts";
2
+ type BehavioralRegister = "business" | "creative";
3
+ export declare function substituteTemplate(content: string, terminology: Record<string, string>): string;
4
+ export interface TemplateContext {
5
+ terminology: Record<string, string>;
6
+ register: BehavioralRegister;
7
+ domain: string | null;
8
+ workspaceType?: string;
9
+ }
10
+ export declare function selectRootTemplate(register: BehavioralRegister): string;
2
11
  interface AgentsGenerateResult {
3
12
  command: string;
4
13
  status: "pass" | "fail";
@@ -9,6 +18,12 @@ interface AgentsGenerateResult {
9
18
  renderedFiles?: {
10
19
  [relPath: string]: string;
11
20
  };
21
+ details?: Array<{
22
+ path: string;
23
+ domain?: string;
24
+ register?: string;
25
+ workspaceType?: string;
26
+ }>;
12
27
  }
13
28
  export declare function runAgentsGenerate(_input: ForgeCommandInput, context: ForgeRuntimeContext): Promise<ForgeCommandResult<AgentsGenerateResult>>;
14
29
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"agents-generate.d.ts","sourceRoot":"","sources":["../../../src/onboarding/agents-generate.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAqQrB,UAAU,oBAAoB;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,CAAC,EAAE;QAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,CAAC,CA6MnD"}
1
+ {"version":3,"file":"agents-generate.d.ts","sourceRoot":"","sources":["../../../src/onboarding/agents-generate.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,KAAK,kBAAkB,GAAG,UAAU,GAAG,UAAU,CAAC;AAGlD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,MAAM,CAKR;AAGD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAQvE;AAsRD,UAAU,oBAAoB;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,CAAC,EAAE;QAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAC9C,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/F;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,CAAC,CAqOnD"}
@@ -13,16 +13,52 @@
13
13
  <item>RFC-0549: replaced extended layer stub with full nine-section content from extended-behavioral-layer.ts.</item>
14
14
  <item>RFC-0551: added register-conditional commit policy to core behavioral layer.</item>
15
15
  <item>RFC-0611: added nested AGENTS.md generation for workspace directories + dryRun support.</item>
16
+ <item>RFC-0640: load workspaceTypes from stack profile and pass to generateNestedAgentsMd for profile-driven workspace detection.</item>
17
+ <item>RFC-0643: terminology substitution on final content, root template selection by register, details field in result.</item>
18
+ <item>RFC-0664: added project memory layer read discipline section to generated AGENTS.md.</item>
16
19
  </CHANGE_SUMMARY>
17
20
  */
18
21
  import fs from "node:fs";
19
22
  import path from "node:path";
20
23
  import { parse as parseYaml } from "yaml";
21
- import { loadForgeConfig, resolveBinding } from "../config/forge-config.js";
24
+ import { loadForgeConfig, resolveBinding, resolveForgeRoot } from "../config/forge-config.js";
25
+ import { resolveAllTerminology } from "../profiles/terminology-utils.js";
22
26
  import { FORGE_SKILLS } from "../registry.js";
23
27
  import { buildGeneratedHeader, hasGeneratedMarker, writeFileIfChanged } from "../utils/index.js";
24
28
  import { buildExtendedBehavioralLayer } from "./extended-behavioral-layer.js";
25
29
  import { generateNestedAgentsMd } from "./nested-agents-generate.js";
30
+ import { listStackProfiles } from "../profiles/stack-profile.js";
31
+ // RFC-0643: Template placeholder substitution for {{terminology.key}} patterns
32
+ export function substituteTemplate(content, terminology) {
33
+ return content.replace(/\{\{terminology\.(\w+)\}\}/g, (_, key) => terminology[key] ?? key);
34
+ }
35
+ // RFC-0643: Root template paths
36
+ const BUSINESS_ROOT_TEMPLATE = path.join(import.meta.dirname, "templates", "root-agents-business.md");
37
+ const CREATIVE_ROOT_TEMPLATE = path.join(import.meta.dirname, "templates", "root-agents-creative.md");
38
+ export function selectRootTemplate(register) {
39
+ try {
40
+ const templatePath = register === "creative" ? CREATIVE_ROOT_TEMPLATE : BUSINESS_ROOT_TEMPLATE;
41
+ return fs.readFileSync(templatePath, "utf8");
42
+ }
43
+ catch {
44
+ // Template file not found — fall back to empty string (dynamic sections will still be appended)
45
+ return "";
46
+ }
47
+ }
48
+ function replaceProjectPlaceholders(template, config, dynamicSections) {
49
+ let result = template;
50
+ result = result.replace(/\{\{projectName\}\}/g, config.project.name);
51
+ result = result.replace(/\{\{projectStack\}\}/g, config.project.stack.length > 0 ? config.project.stack.join(", ") : "(none)");
52
+ result = result.replace(/\{\{projectPm\}\}/g, config.project.packageManager);
53
+ result = result.replace(/\{\{rfcsDir\}\}/g, config.paths.rfcsDir);
54
+ result = result.replace(/\{\{adrsDir\}\}/g, config.paths.adrsDir);
55
+ result = result.replace(/\{\{plansDir\}\}/g, config.paths.plansDir);
56
+ result = result.replace(/\{\{auditsDir\}\}/g, config.paths.auditsDir);
57
+ result = result.replace(/\{\{specsDir\}\}/g, config.paths.specsDir);
58
+ result = result.replace(/\{\{skillsDir\}\}/g, config.paths.skillsDir);
59
+ result = result.replace(/\{\{dynamicSections\}\}/g, dynamicSections);
60
+ return result;
61
+ }
26
62
  function readRegister(workspaceRoot) {
27
63
  const prefsPath = path.join(workspaceRoot, "PREFERENCES.md");
28
64
  try {
@@ -308,48 +344,27 @@ export async function runAgentsGenerate(_input, context) {
308
344
  ownerCommand: "forge.agents.generate",
309
345
  commandPrefix: "forge",
310
346
  });
311
- const lines = [];
312
- lines.push(header);
313
- lines.push("");
314
- lines.push(`# Agent Guide: ${config.project.name}`);
315
- lines.push("");
316
- lines.push("> This file is generated by `forge.agents.generate` from `forge.yaml`.");
317
- lines.push("> Do not edit by hand — edit `forge.yaml` and regenerate.");
318
- lines.push("");
319
- // Project section
320
- lines.push("## Project");
321
- lines.push("");
322
- lines.push(`- **Name:** ${config.project.name}`);
323
- lines.push(`- **Stack:** ${config.project.stack.length > 0 ? config.project.stack.join(", ") : "(none)"}`);
324
- lines.push(`- **Package manager:** ${config.project.packageManager}`);
325
- lines.push("");
326
- // Paths section
327
- lines.push("## Paths");
328
- lines.push("");
329
- lines.push(`- RFCs: \`${config.paths.rfcsDir}\``);
330
- lines.push(`- ADRs: \`${config.paths.adrsDir}\``);
331
- lines.push(`- Plans: \`${config.paths.plansDir}\``);
332
- lines.push(`- Audits: \`${config.paths.auditsDir}\``);
333
- lines.push(`- Specs: \`${config.paths.specsDir}\``);
334
- lines.push(`- Skills: \`${config.paths.skillsDir}\``);
335
- lines.push("");
347
+ // RFC-0643: determine register from PREFERENCES.md or profile
348
+ const register = readRegister(workspaceRoot);
349
+ // Build dynamic sections (skills table, capabilities, behavioral layer)
350
+ const dynamicLines = [];
336
351
  // Skills table
337
- lines.push("## Skills");
338
- lines.push("");
339
- lines.push("| Name | Category | Invocation | Concerns |");
340
- lines.push("| --- | --- | --- | --- |");
352
+ dynamicLines.push("## Skills");
353
+ dynamicLines.push("");
354
+ dynamicLines.push("| Name | Category | Invocation | Concerns |");
355
+ dynamicLines.push("| --- | --- | --- | --- |");
341
356
  for (const skill of FORGE_SKILLS) {
342
- lines.push(`| ${skill.name} | ${skill.category} | ${skill.invocation} | ${skill.concerns} |`);
357
+ dynamicLines.push(`| ${skill.name} | ${skill.category} | ${skill.invocation} | ${skill.concerns} |`);
343
358
  }
344
- lines.push("");
359
+ dynamicLines.push("");
345
360
  // Capabilities section (RFC-0393)
346
361
  if (config.bindings) {
347
- lines.push("## Capabilities");
348
- lines.push("");
349
- lines.push("Bindings resolved from `forge.yaml`:");
350
- lines.push("");
351
- lines.push("| Key | Status | Value |");
352
- lines.push("| --- | --- | --- |");
362
+ dynamicLines.push("## Capabilities");
363
+ dynamicLines.push("");
364
+ dynamicLines.push("Bindings resolved from `forge.yaml`:");
365
+ dynamicLines.push("");
366
+ dynamicLines.push("| Key | Status | Value |");
367
+ dynamicLines.push("| --- | --- | --- |");
353
368
  const bindingKeys = [
354
369
  "commands.validateRfc",
355
370
  "commands.validateAdr",
@@ -367,43 +382,57 @@ export async function runAgentsGenerate(_input, context) {
367
382
  const value = resolveBinding(config, key);
368
383
  const status = value === null ? "absent" : "resolved";
369
384
  const display = value === null ? "—" : typeof value === "string" ? `\`${value}\`` : String(value);
370
- lines.push(`| ${key} | ${status} | ${display} |`);
385
+ dynamicLines.push(`| ${key} | ${status} | ${display} |`);
371
386
  }
372
387
  // compassDocs array
373
388
  const compassDocs = resolveBinding(config, "paths.compassDocs");
374
389
  if (Array.isArray(compassDocs) && compassDocs.length > 0) {
375
- lines.push(`| paths.compassDocs | resolved | ${compassDocs.map((d) => `\`${d}\``).join(", ")} |`);
390
+ dynamicLines.push(`| paths.compassDocs | resolved | ${compassDocs.map((d) => `\`${d}\``).join(", ")} |`);
376
391
  }
377
392
  else {
378
- lines.push(`| paths.compassDocs | absent | — |`);
393
+ dynamicLines.push(`| paths.compassDocs | absent | — |`);
379
394
  }
380
395
  // terminology
381
396
  if (config.bindings.terminology && Object.keys(config.bindings.terminology).length > 0) {
382
- lines.push("");
383
- lines.push("**Terminology:**");
384
- lines.push("");
397
+ dynamicLines.push("");
398
+ dynamicLines.push("**Terminology:**");
399
+ dynamicLines.push("");
385
400
  for (const [term, value] of Object.entries(config.bindings.terminology)) {
386
- lines.push(`- ${term}: ${value}`);
401
+ dynamicLines.push(`- ${term}: ${value}`);
387
402
  }
388
403
  }
389
- lines.push("");
404
+ dynamicLines.push("");
390
405
  }
391
406
  // Behavioral layer section (RFC-0548)
392
- const register = readRegister(workspaceRoot);
393
407
  const behavioralLayer = generateBehavioralLayer(workspaceRoot, register);
394
- lines.push(behavioralLayer);
395
- lines.push("");
396
- // Conventions section
397
- lines.push("## Conventions");
398
- lines.push("");
399
- lines.push("- Use `forge.yaml` as the single source of truth for project configuration.");
400
- lines.push("- Regenerate this file with `forge.agents.generate` after changing `forge.yaml`.");
401
- lines.push("- Follow the closest `AGENTS.md` for workspace or directory details.");
402
- lines.push("");
403
- const content = lines.join("\n");
408
+ dynamicLines.push(behavioralLayer);
409
+ dynamicLines.push("");
410
+ // RFC-0664: Project memory layer read discipline
411
+ dynamicLines.push("## Project memory layer");
412
+ dynamicLines.push("");
413
+ dynamicLines.push("`.agents/memory/MEMORY.md` is the curated hot store (versioned). `.agents/memory/daily/YYYY-MM-DD.md` are append-only warm logs (git-ignored).");
414
+ dynamicLines.push("");
415
+ dynamicLines.push("**Session-start read discipline (advisory):** At session start, read `MEMORY.md` (always), then `daily/<today>.md` and `daily/<yesterday>.md` (if present). Older daily files are cold — use grep when a task references past context.");
416
+ dynamicLines.push("");
417
+ // RFC-0643: Load root template, replace project placeholders, insert dynamic sections
418
+ const rootTemplate = selectRootTemplate(register);
419
+ const dynamicSections = dynamicLines.join("\n");
420
+ let content = replaceProjectPlaceholders(rootTemplate, config, dynamicSections);
421
+ // Prepend generated header (above template content)
422
+ content = header + "\n\n" + content;
423
+ // RFC-0643: Apply terminology substitution on final assembled content
424
+ const profile = config.profile;
425
+ const resolvedTerminology = resolveAllTerminology(config, profile);
426
+ content = substituteTemplate(content, resolvedTerminology);
404
427
  const generated = ["AGENTS.md"];
405
428
  const skipped = [];
406
429
  const renderedFiles = {};
430
+ const details = [];
431
+ details.push({
432
+ path: "AGENTS.md",
433
+ domain: profile?.domain,
434
+ register,
435
+ });
407
436
  if (dryRun) {
408
437
  renderedFiles["AGENTS.md"] = content;
409
438
  }
@@ -414,10 +443,40 @@ export async function runAgentsGenerate(_input, context) {
414
443
  }
415
444
  }
416
445
  // Nested AGENTS.md generation (RFC-0611)
417
- const nestedResult = await generateNestedAgentsMd(workspaceRoot, config, dryRun);
446
+ // RFC-0640: load workspaceTypes from stack profile for profile-driven detection
447
+ // RFC-0643: prefer config.profile (loaded by loadForgeConfig), fallback to stack-based lookup
448
+ let workspaceTypes;
449
+ if (profile?.workspaceTypes && profile.workspaceTypes.length > 0) {
450
+ workspaceTypes = profile.workspaceTypes;
451
+ }
452
+ else {
453
+ try {
454
+ const forgeRoot = resolveForgeRoot(workspaceRoot);
455
+ const profiles = listStackProfiles(forgeRoot);
456
+ for (const stackId of config.project.stack) {
457
+ const stackProfile = profiles.find((p) => p.id === stackId);
458
+ if (stackProfile?.workspaceTypes && stackProfile.workspaceTypes.length > 0) {
459
+ workspaceTypes = stackProfile.workspaceTypes;
460
+ break;
461
+ }
462
+ }
463
+ }
464
+ catch {
465
+ // forge root not resolvable — fallback to hardcoded detection
466
+ }
467
+ }
468
+ const nestedResult = await generateNestedAgentsMd(workspaceRoot, config, dryRun, workspaceTypes);
418
469
  generated.push(...nestedResult.generated);
419
470
  skipped.push(...nestedResult.skipped);
420
471
  Object.assign(renderedFiles, nestedResult.renderedFiles);
472
+ // RFC-0643: add details for nested files
473
+ for (const rel of nestedResult.generated) {
474
+ details.push({
475
+ path: rel,
476
+ domain: profile?.domain,
477
+ workspaceType: nestedResult.workspaceTypeMap?.[rel],
478
+ });
479
+ }
421
480
  if (!dryRun && outputFormat === "pretty") {
422
481
  for (const rel of nestedResult.generated) {
423
482
  logger.success(`Generated ${rel}`);
@@ -434,6 +493,7 @@ export async function runAgentsGenerate(_input, context) {
434
493
  generated,
435
494
  skipped,
436
495
  errors: [],
496
+ details,
437
497
  ...(dryRun ? { renderedFiles } : {}),
438
498
  },
439
499
  exitCode: 0,