@warpgogol/forge 2.8.0 → 2.8.2
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 +1 -1
- package/README.md +23 -13
- package/README.uk.md +19 -12
- package/dist/os/session/handlers/validate.d.ts.map +1 -1
- package/dist/os/session/handlers/validate.js +23 -1
- package/dist/os/session/handlers/validate.js.map +1 -1
- package/dist/os/session/types.d.ts +31 -1
- package/dist/os/session/types.d.ts.map +1 -1
- package/dist/os/session/types.js +7 -0
- package/dist/os/session/types.js.map +1 -1
- package/os/adr/adr.module.ts +158 -0
- package/os/adr/frontmatter-io.ts +80 -0
- package/os/adr/handlers/archive.ts +244 -0
- package/os/adr/handlers/implement-stamp.ts +331 -0
- package/os/adr/handlers/list-create.ts +207 -0
- package/os/adr/handlers/validate.test.ts +232 -0
- package/os/adr/handlers/validate.ts +429 -0
- package/os/adr/index.ts +33 -0
- package/os/adr/types.ts +141 -0
- package/os/audit/audit.module.ts +48 -0
- package/os/audit/frontmatter-io.ts +84 -0
- package/os/audit/handlers/archive.ts +239 -0
- package/os/audit/index.ts +23 -0
- package/os/audit/types.ts +29 -0
- package/os/compass/compass.module.ts +186 -0
- package/os/compass/handlers/compass-audit-handler.ts +382 -0
- package/os/compass/handlers/compass-change-summary-handler.ts +272 -0
- package/os/compass/handlers/compass-inventory-handler.ts +294 -0
- package/os/compass/handlers/compass-inventory.ts +521 -0
- package/os/compass/handlers/git-revision.ts +137 -0
- package/os/compass/handlers/resolve-scan-root-workpiece.test.ts +65 -0
- package/os/compass/handlers/resolve-scan-root.ts +79 -0
- package/os/compass/index.ts +33 -0
- package/os/core/core.module.ts +948 -0
- package/os/core/handlers/assets-check.ts +122 -0
- package/os/core/handlers/assets-helpers.ts +174 -0
- package/os/core/handlers/assets-list.ts +99 -0
- package/os/core/handlers/build.ts +155 -0
- package/os/core/handlers/determinism-check.ts +354 -0
- package/os/core/handlers/dev.ts +127 -0
- package/os/core/handlers/invariant-engine.test.ts +681 -0
- package/os/core/handlers/knowledge-compact.ts +248 -0
- package/os/core/handlers/lifecycle-handlers.test.ts +524 -0
- package/os/core/handlers/note-frontmatter-validate.test.ts +93 -0
- package/os/core/handlers/note-link-validate.test.ts +117 -0
- package/os/core/handlers/note-orphan-detect.test.ts +94 -0
- package/os/core/handlers/package-health.test.ts +226 -0
- package/os/core/handlers/package-health.ts +231 -0
- package/os/core/handlers/pinned-check.ts +200 -0
- package/os/core/handlers/pinned-init.ts +333 -0
- package/os/core/handlers/pinned-types.ts +50 -0
- package/os/core/handlers/pinned-validate.ts +301 -0
- package/os/core/handlers/profile-resolve.ts +92 -0
- package/os/core/handlers/release-prepare.ts +294 -0
- package/os/core/handlers/release-publish.ts +211 -0
- package/os/core/handlers/validate.ts +262 -0
- package/os/core/handlers/workspace-deps.ts +55 -0
- package/os/core/index.ts +13 -0
- package/os/exploration/exploration.module.ts +90 -0
- package/os/exploration/frontmatter-io.ts +77 -0
- package/os/exploration/handlers/archive.ts +159 -0
- package/os/exploration/handlers/list.ts +77 -0
- package/os/exploration/handlers/show.ts +107 -0
- package/os/exploration/index.ts +25 -0
- package/os/exploration/types.ts +65 -0
- package/os/mission/handlers/archive.test.ts +391 -0
- package/os/mission/handlers/archive.ts +453 -0
- package/os/mission/index.ts +1 -0
- package/os/mission/mission.module.ts +48 -0
- package/os/mission/types.ts +41 -0
- package/os/naming/index.ts +14 -0
- package/os/naming/naming-convention.test.ts +163 -0
- package/os/naming/naming-convention.ts +378 -0
- package/os/naming/naming.module.ts +36 -0
- package/os/notes/index.ts +10 -0
- package/os/notes/notes.module.ts +109 -0
- package/os/plan/frontmatter-io.ts +82 -0
- package/os/plan/handlers/archive.ts +238 -0
- package/os/plan/index.ts +23 -0
- package/os/plan/plan.module.ts +48 -0
- package/os/plan/types.ts +29 -0
- package/os/program/discovery.ts +294 -0
- package/os/program/handlers/complete.ts +419 -0
- package/os/program/handlers/lease.ts +433 -0
- package/os/program/handlers/seal.ts +332 -0
- package/os/program/handlers/validate.ts +213 -0
- package/os/program/lease.ts +156 -0
- package/os/program/program.module.ts +212 -0
- package/os/program/schemas.ts +218 -0
- package/os/program/state.ts +264 -0
- package/os/rfc/acceptance.ts +344 -0
- package/os/rfc/decision-log.ts +310 -0
- package/os/rfc/dna-trace.ts +280 -0
- package/os/rfc/frontmatter-io.test.ts +132 -0
- package/os/rfc/frontmatter-io.ts +130 -0
- package/os/rfc/handlers/archive.ts +300 -0
- package/os/rfc/handlers/check.ts +238 -0
- package/os/rfc/handlers/implement-stamp.ts +515 -0
- package/os/rfc/handlers/index-graph.ts +237 -0
- package/os/rfc/handlers/lifecycle.test.ts +127 -0
- package/os/rfc/handlers/lifecycle.ts +249 -0
- package/os/rfc/handlers/list-create.ts +296 -0
- package/os/rfc/handlers/pipeline-status.ts +173 -0
- package/os/rfc/handlers/shared.ts +121 -0
- package/os/rfc/handlers/supersede-propose.ts +241 -0
- package/os/rfc/handlers/validate-rules.test.ts +891 -0
- package/os/rfc/handlers/validate-rules.ts +984 -0
- package/os/rfc/handlers/validate.ts +166 -0
- package/os/rfc/handlers.ts +19 -0
- package/os/rfc/index.ts +97 -0
- package/os/rfc/rfc.module.ts +416 -0
- package/os/rfc/types.ts +596 -0
- package/os/rfc/verification-evidence.ts +244 -0
- package/os/session/atif-parser.ts +166 -0
- package/os/session/frontmatter-io.ts +163 -0
- package/os/session/handlers/archive.ts +288 -0
- package/os/session/handlers/list.ts +174 -0
- package/os/session/handlers/save.ts +348 -0
- package/os/session/handlers/validate.ts +250 -0
- package/os/session/index.ts +50 -0
- package/os/session/session.module.ts +126 -0
- package/os/session/types.ts +203 -0
- package/os/spec/live-spec-list-show-validate.test.ts +257 -0
- package/os/spec/live-spec-list.ts +78 -0
- package/os/spec/live-spec-merge.test.ts +261 -0
- package/os/spec/live-spec-merge.ts +413 -0
- package/os/spec/live-spec-show.ts +117 -0
- package/os/spec/live-spec-types.ts +93 -0
- package/os/spec/live-spec-validate.ts +170 -0
- package/os/spec/spec-materialize.ts +390 -0
- package/os/spec/spec-schema.ts +174 -0
- package/os/spec/spec-status.ts +284 -0
- package/os/spec/spec-validate.ts +513 -0
- package/os/spec/spec.module.ts +135 -0
- package/os/werkstatt/handlers/lock.ts +168 -0
- package/os/werkstatt/handlers/schema.ts +47 -0
- package/os/werkstatt/handlers/werkstatt-lock-recover.ts +175 -0
- package/os/werkstatt/handlers/werkstatt-lock-status.ts +73 -0
- package/os/werkstatt/handlers/werkstatt-operation-validate.ts +104 -0
- package/os/werkstatt/index.ts +39 -0
- package/os/werkstatt/werkstatt.module.ts +62 -0
- package/os/workflow/handlers.ts +304 -0
- package/os/workflow/index.ts +28 -0
- package/os/workflow/types.ts +99 -0
- package/os/workflow/workflow.module.ts +54 -0
- package/package.json +3 -3
- package/profiles/godot-csharp.yaml +4 -4
- package/profiles/knowledge-typescript-turborepo.yaml +110 -0
- package/profiles/phaser-turborepo.yaml +5 -5
- package/skills/_shared/fo-session-summary.md +132 -16
- package/skills/fo/fo-add-tests/pbt-guide.md +13 -13
- package/skills/fo/fo-doc-audit/SKILL.md +6 -6
- package/skills/fo/fo-handoff/SKILL.md +5 -0
- package/skills/fo/fo-idea/SKILL.md +1 -1
- package/skills/fo/fo-review/SKILL.md +1 -1
- package/skills/fo/fo-session-retro/SKILL.md +8 -0
- package/skills/fo/fo-session-save/SKILL.md +8 -0
- package/src/cli-output.ts +59 -0
- package/src/config/__tests__/resolve-terminology.test.ts +45 -0
- package/src/config/forge-config.ts +524 -0
- package/src/forge-module.ts +39 -0
- package/src/index.ts +172 -0
- package/src/knowledge/__tests__/promote.test.ts +219 -0
- package/src/knowledge/__tests__/serialize.test.ts +192 -0
- package/src/knowledge/budgets.ts +202 -0
- package/src/knowledge/compact.ts +405 -0
- package/src/knowledge/index.ts +53 -0
- package/src/knowledge/parse.ts +227 -0
- package/src/knowledge/promote.ts +156 -0
- package/src/knowledge/schema.ts +112 -0
- package/src/knowledge/serialize.ts +72 -0
- package/src/migration-adapters/__tests__/ignored-files.test.ts +51 -0
- package/src/migration-adapters/__tests__/registry.test.ts +131 -0
- package/src/migration-adapters/__tests__/types.test.ts +86 -0
- package/src/migration-adapters/git-utils.ts +71 -0
- package/src/migration-adapters/ignored-files.ts +221 -0
- package/src/migration-adapters/index.ts +21 -0
- package/src/migration-adapters/node-typescript-pnpm/index.ts +189 -0
- package/src/migration-adapters/phaser-pnpm/index.ts +189 -0
- package/src/migration-adapters/registry.ts +61 -0
- package/src/migration-adapters/types.ts +68 -0
- package/src/onboarding/__tests__/workspace-discovery.test.ts +134 -0
- package/src/onboarding/agents-generate.ts +443 -0
- package/src/onboarding/create.ts +427 -0
- package/src/onboarding/doctor.ts +1297 -0
- package/src/onboarding/init.ts +337 -0
- package/src/onboarding/invariant-engine.ts +448 -0
- package/src/onboarding/memory-scaffold.ts +189 -0
- package/src/onboarding/nested-agents-generate.ts +93 -0
- package/src/onboarding/nested-agents-templates.ts +233 -0
- package/src/onboarding/profile-validate.ts +139 -0
- package/src/onboarding/scaffold-project.ts +299 -0
- package/src/onboarding/scaffold.ts +143 -0
- package/src/onboarding/upgrade.ts +558 -0
- package/src/onboarding/workspace-discovery.ts +167 -0
- package/src/profiles/__tests__/profile-schema.test.ts +228 -0
- package/src/profiles/__tests__/stack-profile.test.ts +190 -0
- package/src/profiles/__tests__/terminology-utils.test.ts +71 -0
- package/src/profiles/profile-schema.ts +350 -0
- package/src/profiles/stack-profile.ts +188 -0
- package/src/profiles/terminology-utils.ts +36 -0
- package/src/registry.ts +185 -0
- package/src/skill-schema.ts +37 -0
- package/src/tests/acceptance-criteria.test.ts +99 -0
- package/src/tests/adr-implement-stamp.test.ts +368 -0
- package/src/tests/agents-generate-domain.test.ts +317 -0
- package/src/tests/agents-generate.test.ts +458 -0
- package/src/tests/bindings-schema.test.ts +314 -0
- package/src/tests/budgets.test.ts +213 -0
- package/src/tests/cli-output.test.ts +196 -0
- package/src/tests/compact.test.ts +796 -0
- package/src/tests/create.test.ts +289 -0
- package/src/tests/doctor-autonomy.test.ts +98 -0
- package/src/tests/doctor-bindings.test.ts +286 -0
- package/src/tests/doctor-domain.test.ts +113 -0
- package/src/tests/exploration-handlers.test.ts +256 -0
- package/src/tests/fixtures/agents-generate-business-before.txt +288 -0
- package/src/tests/forge-config.test.ts +420 -0
- package/src/tests/fs-atomic.test.ts +96 -0
- package/src/tests/fs.test.ts +103 -0
- package/src/tests/generated-marker.test.ts +161 -0
- package/src/tests/hash.test.ts +37 -0
- package/src/tests/implement-stamp.test.ts +505 -0
- package/src/tests/init-bindings.test.ts +183 -0
- package/src/tests/knowledge-parse.test.ts +307 -0
- package/src/tests/knowledge-pbt.test.ts +164 -0
- package/src/tests/memory-scaffold.test.ts +192 -0
- package/src/tests/migration-adapters.test.ts +480 -0
- package/src/tests/package-files.test.ts +54 -0
- package/src/tests/pinned-check.test.ts +194 -0
- package/src/tests/pinned-init.test.ts +126 -0
- package/src/tests/profile-schema.test.ts +194 -0
- package/src/tests/profile-validate.test.ts +120 -0
- package/src/tests/program-lease.test.ts +245 -0
- package/src/tests/program-paths-extended.test.ts +117 -0
- package/src/tests/program-paths.test.ts +98 -0
- package/src/tests/program-property.test.ts +139 -0
- package/src/tests/program-schemas.test.ts +445 -0
- package/src/tests/program-spec-node.test.ts +250 -0
- package/src/tests/promote.test.ts +481 -0
- package/src/tests/registry.test.ts +73 -0
- package/src/tests/scaffold-project.test.ts +150 -0
- package/src/tests/session-handlers.test.ts +503 -0
- package/src/tests/session-pbt.test.ts +190 -0
- package/src/tests/skill-schema.test.ts +146 -0
- package/src/tests/skill-validate-knowledge.test.ts +26 -0
- package/src/tests/skill-validate.test.ts +254 -0
- package/src/tests/stack-profile.test.ts +221 -0
- package/src/tests/string-utils.test.ts +42 -0
- package/src/tests/upgrade.test.ts +428 -0
- package/src/tests/werkstatt-lock.test.ts +343 -0
- package/src/tests/workspace-discovery-domain.test.ts +112 -0
- package/src/tests/workspace-discovery.test.ts +135 -0
- package/src/types.ts +221 -0
- package/src/utils/__tests__/fs-idempotent.test.ts +73 -0
- package/src/utils/__tests__/fs-trash.test.ts +45 -0
- package/src/utils/fs-atomic.ts +93 -0
- package/src/utils/fs-idempotent.ts +41 -0
- package/src/utils/fs-trash-sync.ts +37 -0
- package/src/utils/fs-trash.ts +24 -0
- package/src/utils/fs.ts +74 -0
- package/src/utils/generated-marker.ts +166 -0
- package/src/utils/hash.ts +19 -0
- package/src/utils/index.ts +29 -0
- package/src/utils/string-utils.ts +19 -0
- package/src/validators/__tests__/note-orphan-detect.test.ts +94 -0
- package/src/validators/note-frontmatter-validate.ts +142 -0
- package/src/validators/note-link-validate.ts +145 -0
- package/src/validators/note-orphan-detect.ts +146 -0
- package/src/validators/port-validate.ts +97 -0
- package/src/validators/skill-validate.ts +831 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Unit tests for RFC-0643: per-domain AGENTS.md template generation —
|
|
4
|
+
terminology substitution, register selection, nested template selection, fallback,
|
|
5
|
+
path traversal rejection, details field, and regression.</purpose>
|
|
6
|
+
</MODULE_CONTRACT>
|
|
7
|
+
<CHANGE_SUMMARY>
|
|
8
|
+
<item>RFC-0643: initial domain template tests — terminology substitution, register selection, nested templates, fallback, path traversal, details field, regression.</item>
|
|
9
|
+
</CHANGE_SUMMARY>
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { test, expect, beforeEach, afterEach } from "vitest";
|
|
13
|
+
import { mkdtemp, rm, writeFile, readFile } from "node:fs/promises";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import {
|
|
17
|
+
runAgentsGenerate,
|
|
18
|
+
substituteTemplate,
|
|
19
|
+
selectRootTemplate,
|
|
20
|
+
} from "../onboarding/agents-generate.ts";
|
|
21
|
+
import { selectNestedTemplate } from "../onboarding/nested-agents-templates.ts";
|
|
22
|
+
import type { ForgeRuntimeContext } from "../types.ts";
|
|
23
|
+
import type { ProfileWorkspaceType } from "../profiles/profile-schema.ts";
|
|
24
|
+
import type { StackProfile } from "../profiles/stack-profile.ts";
|
|
25
|
+
|
|
26
|
+
const silentLogger = {
|
|
27
|
+
section: () => {},
|
|
28
|
+
info: () => {},
|
|
29
|
+
warn: () => {},
|
|
30
|
+
error: () => {},
|
|
31
|
+
success: () => {},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const FORGE_ROOT = join(import.meta.dirname, "..", "..");
|
|
35
|
+
const _WORKSPACE_ROOT = join(FORGE_ROOT, "..", "..");
|
|
36
|
+
|
|
37
|
+
function makeContext(workspaceRoot: string): ForgeRuntimeContext {
|
|
38
|
+
return {
|
|
39
|
+
workspaceRoot,
|
|
40
|
+
logger: silentLogger as never,
|
|
41
|
+
dryRun: false,
|
|
42
|
+
outputFormat: "json",
|
|
43
|
+
forgeRoot: FORGE_ROOT,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let tempDir: string;
|
|
48
|
+
|
|
49
|
+
beforeEach(async () => {
|
|
50
|
+
tempDir = await mkdtemp(join(tmpdir(), "agents-gen-domain-test-"));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(async () => {
|
|
54
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
async function makeForgeYaml(dir: string, extra?: string): Promise<void> {
|
|
58
|
+
const lines = [
|
|
59
|
+
"schema: forge/config@1",
|
|
60
|
+
"project:",
|
|
61
|
+
" name: test-project",
|
|
62
|
+
" stack: [typescript]",
|
|
63
|
+
" packageManager: pnpm",
|
|
64
|
+
"paths:",
|
|
65
|
+
" rfcsDir: docs/rfcs",
|
|
66
|
+
" adrsDir: docs/adrs",
|
|
67
|
+
" plansDir: docs/plans",
|
|
68
|
+
" auditsDir: docs/audits",
|
|
69
|
+
" specsDir: docs/specs",
|
|
70
|
+
" skillsDir: .agents/skills",
|
|
71
|
+
"bindings:",
|
|
72
|
+
" schema: forge/bindings@1",
|
|
73
|
+
" commands:",
|
|
74
|
+
" validateRfc: pnpm exec forge rfc.validate",
|
|
75
|
+
" validateAdr: pnpm exec forge adr.validate",
|
|
76
|
+
" typecheck: null",
|
|
77
|
+
" test: null",
|
|
78
|
+
" scopedBuild: null",
|
|
79
|
+
" implementStamp: pnpm exec forge rfc.implement.stamp",
|
|
80
|
+
" specValidate: pnpm exec forge spec.validate",
|
|
81
|
+
" sessionSave: pnpm exec forge session.save",
|
|
82
|
+
" paths:",
|
|
83
|
+
" invariantsFile: docs/architecture-dna.md",
|
|
84
|
+
" reviewsDir: docs/reviews",
|
|
85
|
+
" handoffsDir: docs/handoffs",
|
|
86
|
+
" sessionsDir: docs/sessions",
|
|
87
|
+
" compassDocs: [docs/requirements.xml]",
|
|
88
|
+
"forge:",
|
|
89
|
+
" syncedVersion: 0.1.2",
|
|
90
|
+
];
|
|
91
|
+
if (extra) lines.push(extra);
|
|
92
|
+
await writeFile(join(dir, "forge.yaml"), lines.join("\n"), "utf8");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- substituteTemplate tests ---
|
|
96
|
+
|
|
97
|
+
test("substituteTemplate replaces {{terminology.artifact}} with 'game'", () => {
|
|
98
|
+
const result = substituteTemplate("Each {{terminology.artifact}} is a workspace.", {
|
|
99
|
+
artifact: "game",
|
|
100
|
+
});
|
|
101
|
+
expect(result).toBe("Each game is a workspace.");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("substituteTemplate replaces unknown key with key name itself", () => {
|
|
105
|
+
const result = substituteTemplate("The {{terminology.unknown}} is here.", {});
|
|
106
|
+
expect(result).toBe("The unknown is here.");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("substituteTemplate leaves content without placeholders unchanged", () => {
|
|
110
|
+
const content = "No placeholders here.";
|
|
111
|
+
expect(substituteTemplate(content, { artifact: "game" })).toBe(content);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("substituteTemplate replaces multiple placeholders in one pass", () => {
|
|
115
|
+
const result = substituteTemplate(
|
|
116
|
+
"The {{terminology.operator}} creates {{terminology.artifactPlural}}.",
|
|
117
|
+
{ operator: "developer", artifactPlural: "games" },
|
|
118
|
+
);
|
|
119
|
+
expect(result).toBe("The developer creates games.");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// --- selectRootTemplate tests ---
|
|
123
|
+
|
|
124
|
+
test("selectRootTemplate('business') returns business template content", () => {
|
|
125
|
+
const content = selectRootTemplate("business");
|
|
126
|
+
expect(content).toContain("{{projectName}}");
|
|
127
|
+
expect(content).toContain("{{rfcsDir}}");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("selectRootTemplate('creative') returns creative template content", () => {
|
|
131
|
+
const content = selectRootTemplate("creative");
|
|
132
|
+
expect(content).toContain("{{projectName}}");
|
|
133
|
+
expect(content).toContain("{{rfcsDir}}");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("business template contains {{terminology.key}} placeholders", () => {
|
|
137
|
+
const content = selectRootTemplate("business");
|
|
138
|
+
// Templates use {{projectName}} etc. — no {{terminology.*}} needed in static prose
|
|
139
|
+
// but the template should contain {{dynamicSections}} marker
|
|
140
|
+
expect(content).toContain("{{dynamicSections}}");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("creative template contains {{dynamicSections}} marker", () => {
|
|
144
|
+
const content = selectRootTemplate("creative");
|
|
145
|
+
expect(content).toContain("{{dynamicSections}}");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("selectRootTemplate with profile rootAgentsMdTemplate loads profile template", () => {
|
|
149
|
+
const profile = {
|
|
150
|
+
id: "godot-csharp",
|
|
151
|
+
rootAgentsMdTemplate: "root-agents-godot.md",
|
|
152
|
+
} as StackProfile;
|
|
153
|
+
const content = selectRootTemplate("creative", profile);
|
|
154
|
+
expect(content).toContain("Godot 4.x with .NET / C#");
|
|
155
|
+
expect(content).toContain("{{dynamicSections}}");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("selectRootTemplate with missing rootAgentsMdTemplate falls back to register template", () => {
|
|
159
|
+
const profile = {
|
|
160
|
+
id: "test-profile",
|
|
161
|
+
rootAgentsMdTemplate: "nonexistent-template.md",
|
|
162
|
+
} as StackProfile;
|
|
163
|
+
const content = selectRootTemplate("business", profile);
|
|
164
|
+
expect(content).toContain("{{projectName}}");
|
|
165
|
+
expect(content).toContain("{{rfcsDir}}");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("selectRootTemplate rejects path traversal in rootAgentsMdTemplate", () => {
|
|
169
|
+
const profile = {
|
|
170
|
+
id: "evil-profile",
|
|
171
|
+
rootAgentsMdTemplate: "../../../etc/passwd",
|
|
172
|
+
} as StackProfile;
|
|
173
|
+
const content = selectRootTemplate("business", profile);
|
|
174
|
+
expect(content).toContain("{{projectName}}");
|
|
175
|
+
expect(content).not.toContain("root:");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("selectRootTemplate rejects absolute path in rootAgentsMdTemplate", () => {
|
|
179
|
+
const profile = {
|
|
180
|
+
id: "evil-profile",
|
|
181
|
+
rootAgentsMdTemplate: "/etc/passwd",
|
|
182
|
+
} as StackProfile;
|
|
183
|
+
const content = selectRootTemplate("business", profile);
|
|
184
|
+
expect(content).toContain("{{projectName}}");
|
|
185
|
+
expect(content).not.toContain("root:");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// --- selectNestedTemplate tests ---
|
|
189
|
+
|
|
190
|
+
test("selectNestedTemplate without agentsMdTemplate uses fallback", () => {
|
|
191
|
+
const wsType: ProfileWorkspaceType = {
|
|
192
|
+
id: "package",
|
|
193
|
+
detect: { glob: "package.json" },
|
|
194
|
+
};
|
|
195
|
+
const profile = { id: "forge-shell" } as StackProfile;
|
|
196
|
+
const result = selectNestedTemplate(wsType, profile, {}, "FALLBACK");
|
|
197
|
+
expect(result).toBe("FALLBACK");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("selectNestedTemplate without profile uses fallback", () => {
|
|
201
|
+
const wsType: ProfileWorkspaceType = {
|
|
202
|
+
id: "game",
|
|
203
|
+
detect: { glob: "*.ts" },
|
|
204
|
+
agentsMdTemplate: "some-template.md",
|
|
205
|
+
};
|
|
206
|
+
const result = selectNestedTemplate(wsType, undefined, {}, "FALLBACK");
|
|
207
|
+
expect(result).toBe("FALLBACK");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("selectNestedTemplate rejects path traversal (..) with fallback", () => {
|
|
211
|
+
const wsType: ProfileWorkspaceType = {
|
|
212
|
+
id: "evil",
|
|
213
|
+
detect: { glob: "*" },
|
|
214
|
+
agentsMdTemplate: "../../../etc/passwd",
|
|
215
|
+
};
|
|
216
|
+
const profile = { id: "evil-profile" } as StackProfile;
|
|
217
|
+
const result = selectNestedTemplate(wsType, profile, {}, "FALLBACK");
|
|
218
|
+
expect(result).toBe("FALLBACK");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("selectNestedTemplate rejects absolute path with fallback", () => {
|
|
222
|
+
const wsType: ProfileWorkspaceType = {
|
|
223
|
+
id: "evil",
|
|
224
|
+
detect: { glob: "*" },
|
|
225
|
+
agentsMdTemplate: "/etc/passwd",
|
|
226
|
+
};
|
|
227
|
+
const profile = { id: "evil-profile" } as StackProfile;
|
|
228
|
+
const result = selectNestedTemplate(wsType, profile, {}, "FALLBACK");
|
|
229
|
+
expect(result).toBe("FALLBACK");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// --- Integration: --json output has details field ---
|
|
233
|
+
|
|
234
|
+
test("agents-generate --json output has details field with domain, register, workspaceType", async () => {
|
|
235
|
+
await makeForgeYaml(tempDir);
|
|
236
|
+
const ctx = makeContext(tempDir);
|
|
237
|
+
ctx.dryRun = true;
|
|
238
|
+
|
|
239
|
+
const result = await runAgentsGenerate({ argv: [], flags: {} }, ctx);
|
|
240
|
+
expect(result.exitCode).toBe(0);
|
|
241
|
+
expect(result.data?.details).toBeDefined();
|
|
242
|
+
expect(Array.isArray(result.data?.details)).toBe(true);
|
|
243
|
+
expect(result.data?.details?.length).toBeGreaterThan(0);
|
|
244
|
+
const rootDetail = result.data?.details?.find((d) => d.path === "AGENTS.md");
|
|
245
|
+
expect(rootDetail).toBeDefined();
|
|
246
|
+
expect(rootDetail?.register).toBe("business");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("agents-generate --json output generated field is string[]", async () => {
|
|
250
|
+
await makeForgeYaml(tempDir);
|
|
251
|
+
const ctx = makeContext(tempDir);
|
|
252
|
+
ctx.dryRun = true;
|
|
253
|
+
|
|
254
|
+
const result = await runAgentsGenerate({ argv: [], flags: {} }, ctx);
|
|
255
|
+
expect(result.exitCode).toBe(0);
|
|
256
|
+
expect(result.data?.generated).toEqual(["AGENTS.md"]);
|
|
257
|
+
// generated is string[], not object array
|
|
258
|
+
expect(typeof result.data?.generated?.[0]).toBe("string");
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// --- Regression: no profile → identical output ---
|
|
262
|
+
|
|
263
|
+
test("agents-generate with no profile generates identical AGENTS.md (regression)", async () => {
|
|
264
|
+
await makeForgeYaml(tempDir);
|
|
265
|
+
const ctx = makeContext(tempDir);
|
|
266
|
+
ctx.dryRun = true;
|
|
267
|
+
|
|
268
|
+
const result = await runAgentsGenerate({ argv: [], flags: {} }, ctx);
|
|
269
|
+
const content = result.data?.renderedFiles?.["AGENTS.md"];
|
|
270
|
+
expect(content).toBeDefined();
|
|
271
|
+
|
|
272
|
+
const goldenFixture = await readFile(
|
|
273
|
+
join(import.meta.dirname, "fixtures", "agents-generate-business-before.txt"),
|
|
274
|
+
"utf8",
|
|
275
|
+
);
|
|
276
|
+
expect(content).toBe(goldenFixture);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// --- Integration: creative register uses creative template ---
|
|
280
|
+
|
|
281
|
+
test("agents-generate with creative register uses creative template", async () => {
|
|
282
|
+
await makeForgeYaml(tempDir);
|
|
283
|
+
await writeFile(
|
|
284
|
+
join(tempDir, "PREFERENCES.md"),
|
|
285
|
+
"---\naiLanguage: en\ndocumentationLanguage: en\nregister: creative\n---\n",
|
|
286
|
+
"utf8",
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const ctx = makeContext(tempDir);
|
|
290
|
+
ctx.dryRun = true;
|
|
291
|
+
|
|
292
|
+
const result = await runAgentsGenerate({ argv: [], flags: {} }, ctx);
|
|
293
|
+
expect(result.exitCode).toBe(0);
|
|
294
|
+
const content = result.data?.renderedFiles?.["AGENTS.md"];
|
|
295
|
+
expect(content).toBeDefined();
|
|
296
|
+
expect(content).toContain("Agent Guide: test-project");
|
|
297
|
+
// Creative register should have extended behavioral layer
|
|
298
|
+
expect(content).toContain("Extended behavioral layer (creative register)");
|
|
299
|
+
// Details should show creative register
|
|
300
|
+
const rootDetail = result.data?.details?.find((d) => d.path === "AGENTS.md");
|
|
301
|
+
expect(rootDetail?.register).toBe("creative");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("agents-generate with godot-csharp profile generates Godot-specific AGENTS.md", async () => {
|
|
305
|
+
await makeForgeYaml(tempDir, "profile: godot-csharp");
|
|
306
|
+
const ctx = makeContext(tempDir);
|
|
307
|
+
ctx.dryRun = true;
|
|
308
|
+
|
|
309
|
+
const result = await runAgentsGenerate({ argv: [], flags: {} }, ctx);
|
|
310
|
+
expect(result.exitCode).toBe(0);
|
|
311
|
+
const content = result.data?.renderedFiles?.["AGENTS.md"];
|
|
312
|
+
expect(content).toBeDefined();
|
|
313
|
+
expect(content).toContain("Godot 4.x with .NET / C#");
|
|
314
|
+
expect(content).toContain("dotnet build");
|
|
315
|
+
expect(content).toContain("Scenes and Resources");
|
|
316
|
+
expect(content).toContain("Definition of Done");
|
|
317
|
+
});
|