@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.
- package/AGENTS.md +116 -4
- package/dist/os/adr/handlers/validate.d.ts.map +1 -1
- package/dist/os/adr/handlers/validate.js +31 -3
- package/dist/os/adr/handlers/validate.js.map +1 -1
- package/dist/os/core/core.module.d.ts.map +1 -1
- package/dist/os/core/core.module.js +76 -2
- package/dist/os/core/core.module.js.map +1 -1
- package/dist/os/core/handlers/knowledge-compact.d.ts +4 -0
- package/dist/os/core/handlers/knowledge-compact.d.ts.map +1 -0
- package/dist/os/core/handlers/knowledge-compact.js +198 -0
- package/dist/os/core/handlers/knowledge-compact.js.map +1 -0
- package/dist/os/rfc/handlers/lifecycle.d.ts.map +1 -1
- package/dist/os/rfc/handlers/lifecycle.js +3 -4
- package/dist/os/rfc/handlers/lifecycle.js.map +1 -1
- package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -1
- package/dist/os/rfc/handlers/validate-rules.js +41 -3
- package/dist/os/rfc/handlers/validate-rules.js.map +1 -1
- package/dist/os/spec/spec-schema.d.ts +1 -1
- package/dist/src/config/forge-config.d.ts +67 -3
- package/dist/src/config/forge-config.d.ts.map +1 -1
- package/dist/src/config/forge-config.js +86 -2
- package/dist/src/config/forge-config.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/knowledge/budgets.d.ts +38 -0
- package/dist/src/knowledge/budgets.d.ts.map +1 -0
- package/dist/src/knowledge/budgets.js +157 -0
- package/dist/src/knowledge/budgets.js.map +1 -0
- package/dist/src/knowledge/compact.d.ts +47 -0
- package/dist/src/knowledge/compact.d.ts.map +1 -0
- package/dist/src/knowledge/compact.js +322 -0
- package/dist/src/knowledge/compact.js.map +1 -0
- package/dist/src/knowledge/index.d.ts +7 -0
- package/dist/src/knowledge/index.d.ts.map +1 -0
- package/dist/src/knowledge/index.js +19 -0
- package/dist/src/knowledge/index.js.map +1 -0
- package/dist/src/knowledge/parse.d.ts +3 -0
- package/dist/src/knowledge/parse.d.ts.map +1 -0
- package/dist/src/knowledge/parse.js +205 -0
- package/dist/src/knowledge/parse.js.map +1 -0
- package/dist/src/knowledge/promote.d.ts +37 -0
- package/dist/src/knowledge/promote.d.ts.map +1 -0
- package/dist/src/knowledge/promote.js +120 -0
- package/dist/src/knowledge/promote.js.map +1 -0
- package/dist/src/knowledge/schema.d.ts +61 -0
- package/dist/src/knowledge/schema.d.ts.map +1 -0
- package/dist/src/knowledge/schema.js +64 -0
- package/dist/src/knowledge/schema.js.map +1 -0
- package/dist/src/knowledge/serialize.d.ts +3 -0
- package/dist/src/knowledge/serialize.d.ts.map +1 -0
- package/dist/src/knowledge/serialize.js +67 -0
- package/dist/src/knowledge/serialize.js.map +1 -0
- package/dist/src/onboarding/agents-generate.d.ts +15 -0
- package/dist/src/onboarding/agents-generate.d.ts.map +1 -1
- package/dist/src/onboarding/agents-generate.js +118 -58
- package/dist/src/onboarding/agents-generate.js.map +1 -1
- package/dist/src/onboarding/create.d.ts.map +1 -1
- package/dist/src/onboarding/create.js +62 -3
- package/dist/src/onboarding/create.js.map +1 -1
- package/dist/src/onboarding/doctor.d.ts +13 -1
- package/dist/src/onboarding/doctor.d.ts.map +1 -1
- package/dist/src/onboarding/doctor.js +527 -11
- package/dist/src/onboarding/doctor.js.map +1 -1
- package/dist/src/onboarding/init.d.ts +8 -1
- package/dist/src/onboarding/init.d.ts.map +1 -1
- package/dist/src/onboarding/init.js +33 -2
- package/dist/src/onboarding/init.js.map +1 -1
- package/dist/src/onboarding/memory-scaffold.d.ts +18 -0
- package/dist/src/onboarding/memory-scaffold.d.ts.map +1 -0
- package/dist/src/onboarding/memory-scaffold.js +151 -0
- package/dist/src/onboarding/memory-scaffold.js.map +1 -0
- package/dist/src/onboarding/nested-agents-generate.d.ts +6 -2
- package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -1
- package/dist/src/onboarding/nested-agents-generate.js +34 -9
- package/dist/src/onboarding/nested-agents-generate.js.map +1 -1
- package/dist/src/onboarding/nested-agents-templates.d.ts +23 -1
- package/dist/src/onboarding/nested-agents-templates.d.ts.map +1 -1
- package/dist/src/onboarding/nested-agents-templates.js +138 -8
- package/dist/src/onboarding/nested-agents-templates.js.map +1 -1
- package/dist/src/onboarding/profile-validate.d.ts +13 -0
- package/dist/src/onboarding/profile-validate.d.ts.map +1 -0
- package/dist/src/onboarding/profile-validate.js +109 -0
- package/dist/src/onboarding/profile-validate.js.map +1 -0
- package/dist/src/onboarding/upgrade.d.ts +5 -0
- package/dist/src/onboarding/upgrade.d.ts.map +1 -1
- package/dist/src/onboarding/upgrade.js +27 -1
- package/dist/src/onboarding/upgrade.js.map +1 -1
- package/dist/src/onboarding/workspace-discovery.d.ts +4 -3
- package/dist/src/onboarding/workspace-discovery.d.ts.map +1 -1
- package/dist/src/onboarding/workspace-discovery.js +51 -3
- package/dist/src/onboarding/workspace-discovery.js.map +1 -1
- package/dist/src/profiles/profile-schema.d.ts +116 -0
- package/dist/src/profiles/profile-schema.d.ts.map +1 -0
- package/dist/src/profiles/profile-schema.js +94 -0
- package/dist/src/profiles/profile-schema.js.map +1 -0
- package/dist/src/profiles/stack-profile.d.ts +41 -1
- package/dist/src/profiles/stack-profile.d.ts.map +1 -1
- package/dist/src/profiles/stack-profile.js +9 -0
- package/dist/src/profiles/stack-profile.js.map +1 -1
- package/dist/src/profiles/terminology-utils.d.ts +8 -0
- package/dist/src/profiles/terminology-utils.d.ts.map +1 -0
- package/dist/src/profiles/terminology-utils.js +31 -0
- package/dist/src/profiles/terminology-utils.js.map +1 -0
- package/dist/src/validators/skill-validate.d.ts +15 -0
- package/dist/src/validators/skill-validate.d.ts.map +1 -1
- package/dist/src/validators/skill-validate.js +204 -2
- package/dist/src/validators/skill-validate.js.map +1 -1
- package/package.json +11 -3
- package/profiles/astro-typescript-turborepo.yaml +87 -0
- package/profiles/editframe-html-templates/composition-agents.md +34 -0
- package/profiles/editframe-html-templates/composition.html +32 -0
- package/profiles/editframe-html.yaml +283 -0
- package/profiles/forge-shell.yaml +77 -0
- package/profiles/phaser-turborepo.yaml +87 -0
- package/skills/fo/fo-doc-audit/SKILL.md +6 -6
- package/skills/fo/fo-handoff/SKILL.md +2 -1
- package/skills/fo/fo-idea-implement/SKILL.md +44 -2
- package/skills/fo/fo-idea-plan/SKILL.md +1 -0
- package/skills/fo/fo-knowledge-distill/SKILL.md +170 -0
- package/skills/fo/fo-memory-sync/SKILL.md +6 -3
- package/skills/fo/fo-memory-sync/fix-patterns.md +38 -4
- package/skills/fo/fo-review/SKILL.md +2 -2
- package/skills/fo/fo-session-retro/SKILL.md +21 -11
- package/skills/fo/fo-session-save/SKILL.md +1 -1
- package/skills/fo/fo-session-save/fix-patterns.md +53 -29
- package/skills/fo/fo-session-save/learned-principles.md +59 -8
- package/skills/meta/forge-bootstrap/SKILL.md +16 -4
- package/skills/meta/forge-bootstrap/operator-profile-template.md +1 -0
- package/skills/meta/skill-create/SKILL.md +1 -1
- package/skills/shared/grilling/SKILL.md +1 -1
- package/skills/shared/grilling/learned-principles.md +14 -0
- package/skills/shared/grilling/qa-log.md +105 -6
- package/skills/shared/knowledge/learned-principles.md +5 -0
- package/skills/shared/windows-ai-tooling/SKILL.md +154 -1
- 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 @@
|
|
|
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":"
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
357
|
+
dynamicLines.push(`| ${skill.name} | ${skill.category} | ${skill.invocation} | ${skill.concerns} |`);
|
|
343
358
|
}
|
|
344
|
-
|
|
359
|
+
dynamicLines.push("");
|
|
345
360
|
// Capabilities section (RFC-0393)
|
|
346
361
|
if (config.bindings) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
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
|
-
|
|
390
|
+
dynamicLines.push(`| paths.compassDocs | resolved | ${compassDocs.map((d) => `\`${d}\``).join(", ")} |`);
|
|
376
391
|
}
|
|
377
392
|
else {
|
|
378
|
-
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
397
|
+
dynamicLines.push("");
|
|
398
|
+
dynamicLines.push("**Terminology:**");
|
|
399
|
+
dynamicLines.push("");
|
|
385
400
|
for (const [term, value] of Object.entries(config.bindings.terminology)) {
|
|
386
|
-
|
|
401
|
+
dynamicLines.push(`- ${term}: ${value}`);
|
|
387
402
|
}
|
|
388
403
|
}
|
|
389
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
//
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
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,
|