@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,337 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>forge.init — deploys forge into a project: creates forge.yaml, PREFERENCES.md, copies skills to .agents/skills/, creates docs directories, copies RFC template. Config-driven via resolveForgeRoot and loadForgeConfig.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not overwrite existing forge.yaml, PREFERENCES.md, or kernel.config.ts — skip with warning.</item>
|
|
6
|
+
<item>Do not generate AGENTS.md — that is forge.agents.generate's responsibility.</item>
|
|
7
|
+
</non-goals>
|
|
8
|
+
</MODULE_CONTRACT>
|
|
9
|
+
<CHANGE_SUMMARY>
|
|
10
|
+
<item>RFC-0374: initial forge.init handler.</item>
|
|
11
|
+
<item>RFC-0391: reworked to be config-driven — creates forge.yaml, uses resolveForgeRoot, creates docs/plans/ and docs/audits/ directories.</item>
|
|
12
|
+
<item>RFC-0392: added --from flag for stack detection from existing project or spec folder.</item>
|
|
13
|
+
<item>RFC-0393: defaultForgeConfig now includes default bindings section (all commands null, default paths).</item>
|
|
14
|
+
<item>RFC-0524: extended skill sync to copy declared knowledge files alongside SKILL.md.</item>
|
|
15
|
+
<item>RFC-0539: extended skill sync to also copy declared pack skills from skillPacks config.</item>
|
|
16
|
+
<item>RFC-0543: write forge.syncedVersion on first init (set to installed forge version).</item>
|
|
17
|
+
<item>RFC-0544: accept optional context.forgeRoot to support composition from forge.create.</item>
|
|
18
|
+
<item>RFC-0544 fix: export InitResult interface for type-safe consumption by forge.create.</item>
|
|
19
|
+
<item>RFC-0552: add skippedSkills to InitResult, detect Forge-vs-pack skill name conflicts.</item>
|
|
20
|
+
<item>RFC-0640: accept optional domain fields from profile (register, domain, terminology, semanticBindings) and write them into PREFERENCES.md and forge.yaml.</item>
|
|
21
|
+
<item>RFC-0643: accept optional profileId and write it to forge.yaml as the `profile` field.</item>
|
|
22
|
+
<item>RFC-0663: added syncSharedKnowledge step to sync shared knowledge layer to .agents/skills/shared-knowledge/.</item>
|
|
23
|
+
</CHANGE_SUMMARY>
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import fs from "node:fs";
|
|
27
|
+
import path from "node:path";
|
|
28
|
+
import { stringify as stringifyYaml, parse as parseYaml } from "yaml";
|
|
29
|
+
import { FORGE_SKILLS, discoverPackSkills } from "../registry.ts";
|
|
30
|
+
import { defaultForgeConfig, loadForgeConfig, resolveForgeRoot, type ForgeConfig } from "../config/forge-config.ts";
|
|
31
|
+
import { listStackProfiles, detectStack } from "../profiles/stack-profile.ts";
|
|
32
|
+
|
|
33
|
+
export interface SkippedSkill {
|
|
34
|
+
name: string;
|
|
35
|
+
reason: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface InitResult {
|
|
39
|
+
command: string;
|
|
40
|
+
status: "pass" | "fail";
|
|
41
|
+
configPath?: string;
|
|
42
|
+
created: string[];
|
|
43
|
+
skipped: string[];
|
|
44
|
+
errors: string[];
|
|
45
|
+
skippedSkills: SkippedSkill[];
|
|
46
|
+
detection?: { profile: string | null; unsupported: string[] };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface InitDomainFields {
|
|
50
|
+
register?: "business" | "creative";
|
|
51
|
+
domain?: string;
|
|
52
|
+
terminology?: Record<string, string>;
|
|
53
|
+
semanticBindings?: Record<string, string | null>;
|
|
54
|
+
profileId?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function runInit(
|
|
58
|
+
input: { flags: { aiLanguage?: string; documentationLanguage?: string; from?: string } },
|
|
59
|
+
context: unknown,
|
|
60
|
+
domainFields?: InitDomainFields,
|
|
61
|
+
): InitResult {
|
|
62
|
+
const ctx = context as { workspaceRoot?: string; forgeRoot?: string };
|
|
63
|
+
const workspaceRoot = ctx?.workspaceRoot ?? process.cwd();
|
|
64
|
+
const contextForgeRoot = ctx?.forgeRoot;
|
|
65
|
+
|
|
66
|
+
const created: string[] = [];
|
|
67
|
+
const skipped: string[] = [];
|
|
68
|
+
const errors: string[] = [];
|
|
69
|
+
const skippedSkills: SkippedSkill[] = [];
|
|
70
|
+
|
|
71
|
+
const aiLang = input.flags?.aiLanguage ?? "en";
|
|
72
|
+
const docLang = input.flags?.documentationLanguage ?? "en";
|
|
73
|
+
|
|
74
|
+
// 0. Handle --from flag: detect stack from existing project
|
|
75
|
+
let detection: { profile: string | null; unsupported: string[] } | undefined;
|
|
76
|
+
const fromPath = input.flags?.from;
|
|
77
|
+
if (fromPath) {
|
|
78
|
+
const resolvedFrom = path.resolve(fromPath);
|
|
79
|
+
if (!fs.existsSync(resolvedFrom)) {
|
|
80
|
+
return {
|
|
81
|
+
command: "forge.init",
|
|
82
|
+
status: "fail",
|
|
83
|
+
created,
|
|
84
|
+
skipped,
|
|
85
|
+
errors: [`--from path does not exist: ${resolvedFrom}`],
|
|
86
|
+
skippedSkills,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
let forgeRootForProfiles: string;
|
|
90
|
+
if (contextForgeRoot) {
|
|
91
|
+
forgeRootForProfiles = contextForgeRoot;
|
|
92
|
+
} else {
|
|
93
|
+
try {
|
|
94
|
+
forgeRootForProfiles = resolveForgeRoot(workspaceRoot);
|
|
95
|
+
} catch {
|
|
96
|
+
forgeRootForProfiles = workspaceRoot;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const profiles = listStackProfiles(forgeRootForProfiles);
|
|
100
|
+
const match = detectStack(resolvedFrom, profiles);
|
|
101
|
+
detection = {
|
|
102
|
+
profile: match?.id ?? null,
|
|
103
|
+
unsupported: match ? [] : ["unknown"],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 0b. Create forge.yaml if missing
|
|
108
|
+
const forgeYamlPath = path.join(workspaceRoot, "forge.yaml");
|
|
109
|
+
let config: ForgeConfig;
|
|
110
|
+
if (fs.existsSync(forgeYamlPath)) {
|
|
111
|
+
skipped.push("forge.yaml (already exists)");
|
|
112
|
+
try {
|
|
113
|
+
config = loadForgeConfig(workspaceRoot);
|
|
114
|
+
} catch {
|
|
115
|
+
config = defaultForgeConfig(path.basename(workspaceRoot));
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
const projectName = path.basename(workspaceRoot);
|
|
119
|
+
config = defaultForgeConfig(projectName);
|
|
120
|
+
// If detection found a stack, write it into config
|
|
121
|
+
if (detection?.profile) {
|
|
122
|
+
config.project.stack = [detection.profile];
|
|
123
|
+
}
|
|
124
|
+
// RFC-0543: set forge.syncedVersion to the installed forge version
|
|
125
|
+
try {
|
|
126
|
+
const forgeRootForVersion = contextForgeRoot ?? resolveForgeRoot(workspaceRoot);
|
|
127
|
+
const pkgPath = path.join(forgeRootForVersion, "package.json");
|
|
128
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as { version?: string };
|
|
129
|
+
if (pkg.version) {
|
|
130
|
+
config.forge = { syncedVersion: pkg.version };
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
// forge root not resolvable — leave syncedVersion as null (default)
|
|
134
|
+
}
|
|
135
|
+
// RFC-0643: write profile id to forge.yaml
|
|
136
|
+
if (domainFields?.profileId) {
|
|
137
|
+
(config as unknown as Record<string, unknown>).profile = domainFields.profileId;
|
|
138
|
+
}
|
|
139
|
+
// RFC-0640: write domain fields from profile into forge.yaml
|
|
140
|
+
if (domainFields?.domain) {
|
|
141
|
+
config.project.domain = domainFields.domain;
|
|
142
|
+
}
|
|
143
|
+
if (domainFields?.terminology && config.bindings) {
|
|
144
|
+
config.bindings.terminology = { ...config.bindings.terminology, ...domainFields.terminology };
|
|
145
|
+
}
|
|
146
|
+
if (domainFields?.semanticBindings && config.bindings) {
|
|
147
|
+
for (const [key, value] of Object.entries(domainFields.semanticBindings)) {
|
|
148
|
+
if (key in config.bindings.commands) {
|
|
149
|
+
(config.bindings.commands as Record<string, string | null>)[key] = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const yamlContent = stringifyYaml(config);
|
|
154
|
+
fs.writeFileSync(forgeYamlPath, yamlContent, "utf8");
|
|
155
|
+
created.push("forge.yaml");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 1. Resolve forge root (monorepo or npm-installed, or from context)
|
|
159
|
+
let forgeRoot: string;
|
|
160
|
+
if (contextForgeRoot) {
|
|
161
|
+
forgeRoot = contextForgeRoot;
|
|
162
|
+
} else {
|
|
163
|
+
try {
|
|
164
|
+
forgeRoot = resolveForgeRoot(workspaceRoot);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
return {
|
|
167
|
+
command: "forge.init",
|
|
168
|
+
status: "fail",
|
|
169
|
+
configPath: "forge.yaml",
|
|
170
|
+
created,
|
|
171
|
+
skipped,
|
|
172
|
+
errors: [(err as Error).message],
|
|
173
|
+
skippedSkills,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 2. Create PREFERENCES.md if missing
|
|
179
|
+
const prefsPath = path.join(workspaceRoot, "PREFERENCES.md");
|
|
180
|
+
const register = domainFields?.register ?? "business";
|
|
181
|
+
if (fs.existsSync(prefsPath)) {
|
|
182
|
+
skipped.push("PREFERENCES.md (already exists)");
|
|
183
|
+
} else {
|
|
184
|
+
const prefsContent = `---\naiLanguage: ${aiLang}\ndocumentationLanguage: ${docLang}\nregister: ${register}\n---\n\n# Operator Preferences\n\n- \`aiLanguage\`: ${aiLang} — AI uses this language for all communication with the operator.\n- \`documentationLanguage\`: ${docLang} — generated documentation uses this language.\n- \`register\`: ${register} — communication register (business or creative).\n`;
|
|
185
|
+
fs.writeFileSync(prefsPath, prefsContent, "utf8");
|
|
186
|
+
created.push("PREFERENCES.md");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 3. Copy forge skills to .agents/skills/<name>/ for IDE discovery
|
|
190
|
+
const agentsSkillsDir = path.join(workspaceRoot, config.paths.skillsDir);
|
|
191
|
+
for (const skill of FORGE_SKILLS) {
|
|
192
|
+
const srcPath = path.join(forgeRoot, skill.path);
|
|
193
|
+
const skillName = skill.name;
|
|
194
|
+
const destDir = path.join(agentsSkillsDir, skillName);
|
|
195
|
+
const destPath = path.join(destDir, "SKILL.md");
|
|
196
|
+
|
|
197
|
+
if (!fs.existsSync(srcPath)) {
|
|
198
|
+
errors.push(`Source skill not found: ${skill.path}`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
203
|
+
const content = fs.readFileSync(srcPath, "utf8");
|
|
204
|
+
fs.writeFileSync(destPath, content, "utf8");
|
|
205
|
+
created.push(`${config.paths.skillsDir}/${skillName}/SKILL.md`);
|
|
206
|
+
|
|
207
|
+
// RFC-0524: sync declared knowledge files
|
|
208
|
+
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
209
|
+
if (fmMatch) {
|
|
210
|
+
try {
|
|
211
|
+
const fm = parseYaml(fmMatch[1]) as Record<string, unknown>;
|
|
212
|
+
const knowledgeFiles = fm["knowledge"];
|
|
213
|
+
if (Array.isArray(knowledgeFiles)) {
|
|
214
|
+
const skillSrcDir = path.dirname(srcPath);
|
|
215
|
+
for (const kf of knowledgeFiles) {
|
|
216
|
+
if (typeof kf !== "string") continue;
|
|
217
|
+
const kfSrcPath = path.join(skillSrcDir, kf);
|
|
218
|
+
const kfDestPath = path.join(destDir, kf);
|
|
219
|
+
if (fs.existsSync(kfSrcPath)) {
|
|
220
|
+
const kfContent = fs.readFileSync(kfSrcPath, "utf8");
|
|
221
|
+
fs.writeFileSync(kfDestPath, kfContent, "utf8");
|
|
222
|
+
created.push(`${config.paths.skillsDir}/${skillName}/${kf}`);
|
|
223
|
+
} else {
|
|
224
|
+
errors.push(`Knowledge file '${kf}' declared in ${skillName} but not found in source`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} catch {
|
|
229
|
+
// Frontmatter parse error — SKILL-01 will catch this in validation
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// RFC-0539: Copy declared pack skills to .agents/skills/<name>/ for IDE discovery
|
|
235
|
+
// RFC-0552: Detect Forge-vs-pack skill name conflicts, skip pack skills that conflict
|
|
236
|
+
const forgeSkillNames = new Set(FORGE_SKILLS.map((s) => s.name));
|
|
237
|
+
const packSkills = discoverPackSkills(workspaceRoot, config);
|
|
238
|
+
for (const skill of packSkills) {
|
|
239
|
+
const srcPath = path.join(workspaceRoot, skill.dir, skill.path);
|
|
240
|
+
const skillName = skill.name;
|
|
241
|
+
const destDir = path.join(agentsSkillsDir, skillName);
|
|
242
|
+
const destPath = path.join(destDir, "SKILL.md");
|
|
243
|
+
|
|
244
|
+
if (!fs.existsSync(srcPath)) {
|
|
245
|
+
errors.push(`Pack skill source not found: ${skill.dir}/${skill.path}`);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (forgeSkillNames.has(skillName)) {
|
|
250
|
+
skippedSkills.push({ name: skillName, reason: "conflict with Forge skill" });
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
255
|
+
const content = fs.readFileSync(srcPath, "utf8");
|
|
256
|
+
fs.writeFileSync(destPath, content, "utf8");
|
|
257
|
+
created.push(`${config.paths.skillsDir}/${skillName}/SKILL.md`);
|
|
258
|
+
|
|
259
|
+
// RFC-0524: sync declared knowledge files for pack skills
|
|
260
|
+
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
261
|
+
if (fmMatch) {
|
|
262
|
+
try {
|
|
263
|
+
const fm = parseYaml(fmMatch[1]) as Record<string, unknown>;
|
|
264
|
+
const knowledgeFiles = fm["knowledge"];
|
|
265
|
+
if (Array.isArray(knowledgeFiles)) {
|
|
266
|
+
const skillSrcDir = path.dirname(srcPath);
|
|
267
|
+
for (const kf of knowledgeFiles) {
|
|
268
|
+
if (typeof kf !== "string") continue;
|
|
269
|
+
const kfSrcPath = path.join(skillSrcDir, kf);
|
|
270
|
+
const kfDestPath = path.join(destDir, kf);
|
|
271
|
+
if (fs.existsSync(kfSrcPath)) {
|
|
272
|
+
const kfContent = fs.readFileSync(kfSrcPath, "utf8");
|
|
273
|
+
fs.writeFileSync(kfDestPath, kfContent, "utf8");
|
|
274
|
+
created.push(`${config.paths.skillsDir}/${skillName}/${kf}`);
|
|
275
|
+
} else {
|
|
276
|
+
errors.push(`Knowledge file '${kf}' declared in ${skillName} but not found in source`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
// Frontmatter parse error — SKILL-01 will catch this in validation
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// RFC-0663: Sync shared knowledge layer to .agents/skills/shared-knowledge/
|
|
287
|
+
const sharedKnowledgeSrc = path.join(forgeRoot, "skills", "shared", "knowledge", "learned-principles.md");
|
|
288
|
+
if (fs.existsSync(sharedKnowledgeSrc)) {
|
|
289
|
+
const sharedKnowledgeDestDir = path.join(agentsSkillsDir, "shared-knowledge");
|
|
290
|
+
fs.mkdirSync(sharedKnowledgeDestDir, { recursive: true });
|
|
291
|
+
const sharedKnowledgeDest = path.join(sharedKnowledgeDestDir, "learned-principles.md");
|
|
292
|
+
fs.writeFileSync(sharedKnowledgeDest, fs.readFileSync(sharedKnowledgeSrc, "utf8"), "utf8");
|
|
293
|
+
created.push(`${config.paths.skillsDir}/shared-knowledge/learned-principles.md`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 4. Create docs directories from config paths
|
|
297
|
+
const dirsToCreate = [
|
|
298
|
+
{ rel: config.paths.rfcsDir, label: "docs/rfcs/" },
|
|
299
|
+
{ rel: config.paths.adrsDir, label: "docs/adrs/" },
|
|
300
|
+
{ rel: config.paths.plansDir, label: "docs/plans/" },
|
|
301
|
+
{ rel: config.paths.auditsDir, label: "docs/audits/" },
|
|
302
|
+
{ rel: config.paths.sessionsDir, label: "docs/sessions/" },
|
|
303
|
+
];
|
|
304
|
+
// Also create directories declared in bindings.paths (reviewsDir, handoffsDir, sessionsDir)
|
|
305
|
+
if (config.bindings?.paths) {
|
|
306
|
+
const bindingPaths = [
|
|
307
|
+
{ rel: config.bindings.paths.reviewsDir, label: "docs/reviews/" },
|
|
308
|
+
{ rel: config.bindings.paths.handoffsDir, label: "docs/handoffs/" },
|
|
309
|
+
{ rel: config.bindings.paths.sessionsDir, label: "docs/sessions/ (bindings)" },
|
|
310
|
+
];
|
|
311
|
+
for (const { rel, label } of bindingPaths) {
|
|
312
|
+
if (rel && !dirsToCreate.some((d) => d.rel === rel)) {
|
|
313
|
+
dirsToCreate.push({ rel, label });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
for (const { rel, label } of dirsToCreate) {
|
|
318
|
+
const dir = path.join(workspaceRoot, rel);
|
|
319
|
+
if (fs.existsSync(dir)) {
|
|
320
|
+
skipped.push(`${label} (already exists)`);
|
|
321
|
+
} else {
|
|
322
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
323
|
+
created.push(`${label}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
command: "forge.init",
|
|
329
|
+
status: errors.length === 0 ? "pass" : "fail",
|
|
330
|
+
configPath: "forge.yaml",
|
|
331
|
+
created,
|
|
332
|
+
skipped,
|
|
333
|
+
errors,
|
|
334
|
+
skippedSkills,
|
|
335
|
+
detection,
|
|
336
|
+
};
|
|
337
|
+
}
|