@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,350 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Domain-neutral profile schema extensions — optional fields that allow a stack profile to declare its domain model (terminology, artifacts, workspace types, invariants, register) without breaking backward compatibility.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not import from @warpgogol/* — this module is portable.</item>
|
|
6
|
+
<item>Do not define enforcement logic for invariants — that lives in the invariant engine module.</item>
|
|
7
|
+
</non-goals>
|
|
8
|
+
</MODULE_CONTRACT>
|
|
9
|
+
<CHANGE_SUMMARY>
|
|
10
|
+
<item>RFC-0638: initial domain-neutral profile schema extensions with six optional fields.</item>
|
|
11
|
+
<item>RFC-0674: add profileDevServerSchema and devServer field for lifecycle commands.</item>
|
|
12
|
+
<item>RFC-0675: add profileInvariantCheckSchema and check field to profileInvariantSchema for enforcement.</item>
|
|
13
|
+
<item>RFC-0679: add profileAssetSchema and assets field for asset management commands.</item>
|
|
14
|
+
<item>RFC-0680: add profileReleaseSchema and release field for release lifecycle commands.</item>
|
|
15
|
+
<item>RFC-0694: replace html-attribute-pattern with attribute-pattern (elements array) for HTML+JSX support.</item>
|
|
16
|
+
<item>Add prerequisites field for profile-declared system dependency checks (e.g. FFmpeg).</item>
|
|
17
|
+
<item>Add templates field for multi-template profiles (e.g. React + HTML).</item>
|
|
18
|
+
<item>ADR-0043: add scriptDir field for agent-generated script directory convention.</item>
|
|
19
|
+
<item>RFC-0808: add link-resolution, frontmatter-required, path-exclusion check kinds for note vault validation.</item>
|
|
20
|
+
<item>Add gitignore-entry and secret-scan check kinds for godot-csharp profile.</item>
|
|
21
|
+
</CHANGE_SUMMARY>
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Universal terminology key catalog
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
export const UNIVERSAL_TERMINOLOGY_KEYS = [
|
|
31
|
+
"artifact",
|
|
32
|
+
"artifactPlural",
|
|
33
|
+
"module",
|
|
34
|
+
"source",
|
|
35
|
+
"output",
|
|
36
|
+
"verify",
|
|
37
|
+
"operator",
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
export const TERMINOLOGY_DEFAULTS: Record<string, string> = {
|
|
41
|
+
artifact: "artifact",
|
|
42
|
+
artifactPlural: "artifacts",
|
|
43
|
+
module: "module",
|
|
44
|
+
source: "source file",
|
|
45
|
+
output: "output",
|
|
46
|
+
verify: "verify",
|
|
47
|
+
operator: "operator",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Profile artifact schema
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
export const profileArtifactSchema = z.object({
|
|
55
|
+
id: z.string().min(1),
|
|
56
|
+
extensions: z.array(z.string().min(1)),
|
|
57
|
+
produce: z
|
|
58
|
+
.object({
|
|
59
|
+
command: z.string().min(1),
|
|
60
|
+
output: z.string().optional(),
|
|
61
|
+
})
|
|
62
|
+
.optional(),
|
|
63
|
+
validate: z
|
|
64
|
+
.object({
|
|
65
|
+
command: z.string().min(1),
|
|
66
|
+
outputFormat: z.enum(["plain", "json"]).optional(),
|
|
67
|
+
violationPattern: z.string().optional(),
|
|
68
|
+
})
|
|
69
|
+
.optional(),
|
|
70
|
+
determinism: z
|
|
71
|
+
.object({
|
|
72
|
+
hashable: z.boolean(),
|
|
73
|
+
inputs: z.array(z.string()),
|
|
74
|
+
})
|
|
75
|
+
.optional(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export interface ProfileArtifact {
|
|
79
|
+
id: string;
|
|
80
|
+
extensions: string[];
|
|
81
|
+
produce?: {
|
|
82
|
+
command: string;
|
|
83
|
+
output?: string;
|
|
84
|
+
};
|
|
85
|
+
validate?: {
|
|
86
|
+
command: string;
|
|
87
|
+
outputFormat?: "plain" | "json";
|
|
88
|
+
violationPattern?: string;
|
|
89
|
+
};
|
|
90
|
+
determinism?: {
|
|
91
|
+
hashable: boolean;
|
|
92
|
+
inputs: string[];
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Profile workspace type schema
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
export const profileWorkspaceTypeSchema = z.object({
|
|
101
|
+
id: z.string().min(1),
|
|
102
|
+
detect: z.object({
|
|
103
|
+
glob: z.string().optional(),
|
|
104
|
+
contains: z.string().optional(),
|
|
105
|
+
packageJsonDep: z.string().optional(),
|
|
106
|
+
}),
|
|
107
|
+
skills: z.array(z.string()).optional(),
|
|
108
|
+
agentsMdTemplate: z.string().optional(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export interface ProfileWorkspaceType {
|
|
112
|
+
id: string;
|
|
113
|
+
detect: {
|
|
114
|
+
glob?: string;
|
|
115
|
+
contains?: string;
|
|
116
|
+
packageJsonDep?: string;
|
|
117
|
+
};
|
|
118
|
+
skills?: string[];
|
|
119
|
+
agentsMdTemplate?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Profile invariant schema
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
export const profileInvariantCheckSchema = z
|
|
127
|
+
.object({
|
|
128
|
+
kind: z.enum([
|
|
129
|
+
"filename-pattern",
|
|
130
|
+
"file-contains",
|
|
131
|
+
"file-not-contains",
|
|
132
|
+
"attribute-pattern",
|
|
133
|
+
"link-resolution",
|
|
134
|
+
"frontmatter-required",
|
|
135
|
+
"path-exclusion",
|
|
136
|
+
"gitignore-entry",
|
|
137
|
+
"secret-scan",
|
|
138
|
+
]),
|
|
139
|
+
glob: z.string().optional(),
|
|
140
|
+
pattern: z.string().optional(),
|
|
141
|
+
negatedPattern: z.string().optional(),
|
|
142
|
+
elements: z.array(z.string()).optional(),
|
|
143
|
+
attribute: z.string().optional(),
|
|
144
|
+
fields: z.array(z.string()).optional(),
|
|
145
|
+
})
|
|
146
|
+
.refine(
|
|
147
|
+
(v) =>
|
|
148
|
+
v.kind !== "attribute-pattern" ||
|
|
149
|
+
(v.elements != null && v.elements.length > 0 && v.attribute != null && v.pattern != null),
|
|
150
|
+
{
|
|
151
|
+
message:
|
|
152
|
+
"elements (non-empty array), attribute, and pattern are required for kind: attribute-pattern",
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
.refine((v) => v.kind !== "frontmatter-required" || (v.fields != null && v.fields.length > 0), {
|
|
156
|
+
message: "fields (non-empty array) is required for kind: frontmatter-required",
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
export interface ProfileInvariantCheck {
|
|
160
|
+
kind:
|
|
161
|
+
| "filename-pattern"
|
|
162
|
+
| "file-contains"
|
|
163
|
+
| "file-not-contains"
|
|
164
|
+
| "attribute-pattern"
|
|
165
|
+
| "link-resolution"
|
|
166
|
+
| "frontmatter-required"
|
|
167
|
+
| "path-exclusion"
|
|
168
|
+
| "gitignore-entry"
|
|
169
|
+
| "secret-scan";
|
|
170
|
+
glob?: string;
|
|
171
|
+
pattern?: string;
|
|
172
|
+
negatedPattern?: string;
|
|
173
|
+
elements?: string[];
|
|
174
|
+
attribute?: string;
|
|
175
|
+
fields?: string[];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const profileInvariantSchema = z.object({
|
|
179
|
+
id: z
|
|
180
|
+
.string()
|
|
181
|
+
.min(1)
|
|
182
|
+
.regex(/^[A-Z]+-\d+$/, "Invariant id must match ^[A-Z]+-\\d+$ (e.g. VIDEO-01)"),
|
|
183
|
+
rule: z.string().min(1),
|
|
184
|
+
severity: z.enum(["error", "warning"]),
|
|
185
|
+
check: profileInvariantCheckSchema.optional(),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
export interface ProfileInvariant {
|
|
189
|
+
id: string;
|
|
190
|
+
rule: string;
|
|
191
|
+
severity: "error" | "warning";
|
|
192
|
+
check?: ProfileInvariantCheck;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Profile dev server schema (RFC-0674)
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
export const profileDevServerSchema = z.object({
|
|
200
|
+
command: z.string().min(1),
|
|
201
|
+
port: z.number().int().positive().optional(),
|
|
202
|
+
readinessTimeout: z.number().int().positive().optional(),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
export interface ProfileDevServer {
|
|
206
|
+
command: string;
|
|
207
|
+
port?: number;
|
|
208
|
+
readinessTimeout?: number;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Profile asset schema (RFC-0679)
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
export const profileAssetTypeSchema = z.object({
|
|
216
|
+
id: z.string().min(1),
|
|
217
|
+
extensions: z.array(z.string().min(1)),
|
|
218
|
+
referencePattern: z.string().optional(),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
export interface ProfileAssetType {
|
|
222
|
+
id: string;
|
|
223
|
+
extensions: string[];
|
|
224
|
+
referencePattern?: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const profileAssetSchema = z.object({
|
|
228
|
+
dir: z.string().min(1),
|
|
229
|
+
types: z.array(profileAssetTypeSchema),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export interface ProfileAsset {
|
|
233
|
+
dir: string;
|
|
234
|
+
types: ProfileAssetType[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Profile release schema (RFC-0680)
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
export const profileReleaseSchema = z.object({
|
|
242
|
+
target: z.enum(["local", "r2", "s3"]),
|
|
243
|
+
outputDir: z.string().min(1),
|
|
244
|
+
manifestName: z.string().default("release-manifest.json"),
|
|
245
|
+
includeArtifacts: z.array(z.string()).optional(),
|
|
246
|
+
r2: z
|
|
247
|
+
.object({
|
|
248
|
+
bucket: z.string().min(1),
|
|
249
|
+
accountId: z.string().min(1),
|
|
250
|
+
prefix: z.string().default(""),
|
|
251
|
+
})
|
|
252
|
+
.optional(),
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
export interface ProfileRelease {
|
|
256
|
+
target: "local" | "r2" | "s3";
|
|
257
|
+
outputDir: string;
|
|
258
|
+
manifestName: string;
|
|
259
|
+
includeArtifacts?: string[];
|
|
260
|
+
r2?: {
|
|
261
|
+
bucket: string;
|
|
262
|
+
accountId: string;
|
|
263
|
+
prefix: string;
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
// Profile prerequisite schema
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
|
|
271
|
+
export const profilePrerequisiteSchema = z.object({
|
|
272
|
+
id: z.string().min(1),
|
|
273
|
+
name: z.string().min(1),
|
|
274
|
+
check: z.string().min(1),
|
|
275
|
+
installHint: z.string().optional(),
|
|
276
|
+
severity: z.enum(["error", "warning"]).default("error"),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
export interface ProfilePrerequisite {
|
|
280
|
+
id: string;
|
|
281
|
+
name: string;
|
|
282
|
+
check: string;
|
|
283
|
+
installHint?: string;
|
|
284
|
+
severity: "error" | "warning";
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
// Profile template schema (multi-template profiles)
|
|
289
|
+
// ---------------------------------------------------------------------------
|
|
290
|
+
|
|
291
|
+
export const profileTemplateSchema = z.object({
|
|
292
|
+
id: z.string().min(1),
|
|
293
|
+
default: z.boolean().optional(),
|
|
294
|
+
firstWorkspace: z.object({
|
|
295
|
+
path: z.string().min(1),
|
|
296
|
+
files: z.array(
|
|
297
|
+
z.object({
|
|
298
|
+
path: z.string().min(1),
|
|
299
|
+
content: z.string(),
|
|
300
|
+
}),
|
|
301
|
+
),
|
|
302
|
+
install: z.array(z.string()).default([]),
|
|
303
|
+
}),
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
export interface ProfileTemplate {
|
|
307
|
+
id: string;
|
|
308
|
+
default?: boolean;
|
|
309
|
+
firstWorkspace: {
|
|
310
|
+
path: string;
|
|
311
|
+
files: Array<{ path: string; content: string }>;
|
|
312
|
+
install: string[];
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ---------------------------------------------------------------------------
|
|
317
|
+
// Stack profile domain fields
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
|
|
320
|
+
export const stackProfileDomainFieldsSchema = z.object({
|
|
321
|
+
domain: z.string().optional(),
|
|
322
|
+
terminology: z.record(z.string(), z.string()).optional(),
|
|
323
|
+
artifacts: z.array(profileArtifactSchema).optional(),
|
|
324
|
+
workspaceTypes: z.array(profileWorkspaceTypeSchema).optional(),
|
|
325
|
+
invariants: z.array(profileInvariantSchema).optional(),
|
|
326
|
+
register: z.enum(["business", "creative"]).optional(),
|
|
327
|
+
devServer: profileDevServerSchema.optional(),
|
|
328
|
+
assets: profileAssetSchema.optional(),
|
|
329
|
+
release: profileReleaseSchema.optional(),
|
|
330
|
+
prerequisites: z.array(profilePrerequisiteSchema).optional(),
|
|
331
|
+
templates: z.array(profileTemplateSchema).optional(),
|
|
332
|
+
scriptDir: z.string().min(1).optional(),
|
|
333
|
+
rootAgentsMdTemplate: z.string().optional(),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
export interface StackProfileDomainFields {
|
|
337
|
+
domain?: string;
|
|
338
|
+
terminology?: Record<string, string>;
|
|
339
|
+
artifacts?: ProfileArtifact[];
|
|
340
|
+
workspaceTypes?: ProfileWorkspaceType[];
|
|
341
|
+
invariants?: ProfileInvariant[];
|
|
342
|
+
register?: "business" | "creative";
|
|
343
|
+
devServer?: ProfileDevServer;
|
|
344
|
+
assets?: ProfileAsset;
|
|
345
|
+
release?: ProfileRelease;
|
|
346
|
+
prerequisites?: ProfilePrerequisite[];
|
|
347
|
+
templates?: ProfileTemplate[];
|
|
348
|
+
scriptDir?: string;
|
|
349
|
+
rootAgentsMdTemplate?: string;
|
|
350
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Stack profile schema, loader, and detector. Defines what a supported stack looks like and how to detect it in an existing project.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not scaffold projects — that is scaffold-project.ts.</item>
|
|
6
|
+
<item>Do not import from @warpgogol/* — this module is portable.</item>
|
|
7
|
+
</non-goals>
|
|
8
|
+
</MODULE_CONTRACT>
|
|
9
|
+
<CHANGE_SUMMARY>
|
|
10
|
+
<item>RFC-0392: initial stack profile module with zod schema, listStackProfiles, detectStack.</item>
|
|
11
|
+
<item>RFC-0638: extended stackProfileSchema with optional domain-neutral fields (domain, terminology, artifacts, workspaceTypes, invariants, register).</item>
|
|
12
|
+
<item>RFC-0674: add devServer field to stackProfileSchema for lifecycle commands.</item>
|
|
13
|
+
<item>RFC-0679: add assets field to stackProfileSchema for asset management commands.</item>
|
|
14
|
+
<item>RFC-0680: add release field to stackProfileSchema for release lifecycle commands.</item>
|
|
15
|
+
<item>ADR-0043: add scriptDir field for agent-generated script directory convention.</item>
|
|
16
|
+
</CHANGE_SUMMARY>
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import fs from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
import { parse as parseYaml } from "yaml";
|
|
23
|
+
import { stackProfileDomainFieldsSchema, type StackProfileDomainFields } from "./profile-schema.ts";
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Schema (forge/stack-profile@1)
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export const stackProfileSchema = z.object({
|
|
30
|
+
schema: z.literal("forge/stack-profile@1"),
|
|
31
|
+
id: z.string().min(1),
|
|
32
|
+
displayName: z.string().min(1),
|
|
33
|
+
detect: z.object({
|
|
34
|
+
anyOf: z.array(z.string()).min(1),
|
|
35
|
+
}),
|
|
36
|
+
workspace: z.object({
|
|
37
|
+
dirs: z.array(z.string()).min(1),
|
|
38
|
+
files: z.array(
|
|
39
|
+
z.object({
|
|
40
|
+
path: z.string().min(1),
|
|
41
|
+
content: z.string(),
|
|
42
|
+
}),
|
|
43
|
+
),
|
|
44
|
+
}),
|
|
45
|
+
install: z.array(z.string()).default([]),
|
|
46
|
+
firstWorkspace: z
|
|
47
|
+
.object({
|
|
48
|
+
path: z.string().min(1),
|
|
49
|
+
files: z.array(
|
|
50
|
+
z.object({
|
|
51
|
+
path: z.string().min(1),
|
|
52
|
+
content: z.string(),
|
|
53
|
+
}),
|
|
54
|
+
),
|
|
55
|
+
install: z.array(z.string()).default([]),
|
|
56
|
+
})
|
|
57
|
+
.optional(),
|
|
58
|
+
// RFC-0638: Domain-neutral optional fields
|
|
59
|
+
domain: stackProfileDomainFieldsSchema.shape.domain,
|
|
60
|
+
terminology: stackProfileDomainFieldsSchema.shape.terminology,
|
|
61
|
+
artifacts: stackProfileDomainFieldsSchema.shape.artifacts,
|
|
62
|
+
workspaceTypes: stackProfileDomainFieldsSchema.shape.workspaceTypes,
|
|
63
|
+
invariants: stackProfileDomainFieldsSchema.shape.invariants,
|
|
64
|
+
register: stackProfileDomainFieldsSchema.shape.register,
|
|
65
|
+
// RFC-0674: Dev server declaration for lifecycle commands
|
|
66
|
+
devServer: stackProfileDomainFieldsSchema.shape.devServer,
|
|
67
|
+
// RFC-0679: Asset management declaration
|
|
68
|
+
assets: stackProfileDomainFieldsSchema.shape.assets,
|
|
69
|
+
// RFC-0680: Release lifecycle declaration
|
|
70
|
+
release: stackProfileDomainFieldsSchema.shape.release,
|
|
71
|
+
// Prerequisites: system-level dependency checks (e.g. FFmpeg)
|
|
72
|
+
prerequisites: stackProfileDomainFieldsSchema.shape.prerequisites,
|
|
73
|
+
// Templates: multi-template profiles (e.g. React + HTML)
|
|
74
|
+
templates: stackProfileDomainFieldsSchema.shape.templates,
|
|
75
|
+
// ADR-0043: Agent-generated script directory override
|
|
76
|
+
scriptDir: stackProfileDomainFieldsSchema.shape.scriptDir,
|
|
77
|
+
// Profile-driven root AGENTS.md template
|
|
78
|
+
rootAgentsMdTemplate: stackProfileDomainFieldsSchema.shape.rootAgentsMdTemplate,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export interface ProfileFile {
|
|
82
|
+
path: string;
|
|
83
|
+
content: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface StackProfile extends StackProfileDomainFields {
|
|
87
|
+
schema: "forge/stack-profile@1";
|
|
88
|
+
id: string;
|
|
89
|
+
displayName: string;
|
|
90
|
+
detect: { anyOf: string[] };
|
|
91
|
+
workspace: {
|
|
92
|
+
dirs: string[];
|
|
93
|
+
files: ProfileFile[];
|
|
94
|
+
};
|
|
95
|
+
install: string[];
|
|
96
|
+
firstWorkspace?: {
|
|
97
|
+
path: string;
|
|
98
|
+
files: ProfileFile[];
|
|
99
|
+
install: string[];
|
|
100
|
+
};
|
|
101
|
+
prerequisites?: import("./profile-schema.ts").ProfilePrerequisite[];
|
|
102
|
+
templates?: import("./profile-schema.ts").ProfileTemplate[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Loader
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
export function loadStackProfile(profilePath: string): StackProfile {
|
|
110
|
+
const raw = fs.readFileSync(profilePath, "utf8");
|
|
111
|
+
let parsed: unknown;
|
|
112
|
+
try {
|
|
113
|
+
parsed = parseYaml(raw);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
throw new Error(`Stack profile at ${profilePath} is not valid YAML: ${(err as Error).message}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const result = stackProfileSchema.safeParse(parsed);
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
const issues = result.error.issues
|
|
121
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
122
|
+
.join("\n");
|
|
123
|
+
throw new Error(`Stack profile at ${profilePath} failed schema validation:\n${issues}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return result.data as StackProfile;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function listStackProfiles(forgeRoot: string): StackProfile[] {
|
|
130
|
+
const profilesDir = path.join(forgeRoot, "profiles");
|
|
131
|
+
if (!fs.existsSync(profilesDir)) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const entries = fs.readdirSync(profilesDir);
|
|
136
|
+
const profiles: StackProfile[] = [];
|
|
137
|
+
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
if (entry.endsWith(".yaml") && !entry.startsWith(".")) {
|
|
140
|
+
const profilePath = path.join(profilesDir, entry);
|
|
141
|
+
profiles.push(loadStackProfile(profilePath));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return profiles;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Detector
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
export function detectStack(projectRoot: string, profiles: StackProfile[]): StackProfile | null {
|
|
153
|
+
for (const profile of profiles) {
|
|
154
|
+
if (matchesAnyOf(projectRoot, profile.detect.anyOf)) {
|
|
155
|
+
return profile;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function matchesAnyOf(projectRoot: string, patterns: string[]): boolean {
|
|
162
|
+
for (const pattern of patterns) {
|
|
163
|
+
if (globExists(projectRoot, pattern)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function globExists(dir: string, pattern: string): boolean {
|
|
171
|
+
// Simple glob: support * and *.ext patterns at the root level
|
|
172
|
+
if (!pattern.includes("*")) {
|
|
173
|
+
return fs.existsSync(path.join(dir, pattern));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const entries = fs.readdirSync(dir);
|
|
178
|
+
const regex = globToRegex(pattern);
|
|
179
|
+
return entries.some((e) => regex.test(e));
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function globToRegex(pattern: string): RegExp {
|
|
186
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
187
|
+
return new RegExp(`^${escaped}$`);
|
|
188
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Shared terminology resolution utility (RFC-0643). Resolves all terminology keys
|
|
4
|
+
from config + profile, used by both root and nested AGENTS.md generation.</purpose>
|
|
5
|
+
<non-goals>
|
|
6
|
+
<item>Do not import from @warpgogol/* — this module is portable.</item>
|
|
7
|
+
</non-goals>
|
|
8
|
+
</MODULE_CONTRACT>
|
|
9
|
+
<CHANGE_SUMMARY>
|
|
10
|
+
<item>RFC-0643: extracted resolveAllTerminology from agents-generate.ts and nested-agents-generate.ts to eliminate duplication.</item>
|
|
11
|
+
</CHANGE_SUMMARY>
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { UNIVERSAL_TERMINOLOGY_KEYS } from "./profile-schema.ts";
|
|
15
|
+
import { resolveTerminology, type ForgeConfig } from "../config/forge-config.ts";
|
|
16
|
+
import type { StackProfile } from "./stack-profile.ts";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolve all terminology keys (universal + profile-specific) from config and profile.
|
|
20
|
+
* Used by both root AGENTS.md generation and nested AGENTS.md generation.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveAllTerminology(
|
|
23
|
+
config: ForgeConfig,
|
|
24
|
+
profile: StackProfile | undefined,
|
|
25
|
+
): Record<string, string> {
|
|
26
|
+
const terminology: Record<string, string> = {};
|
|
27
|
+
for (const key of UNIVERSAL_TERMINOLOGY_KEYS) {
|
|
28
|
+
terminology[key] = resolveTerminology(config, profile?.terminology, key);
|
|
29
|
+
}
|
|
30
|
+
if (profile?.terminology) {
|
|
31
|
+
for (const key of Object.keys(profile.terminology)) {
|
|
32
|
+
terminology[key] = resolveTerminology(config, profile.terminology, key);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return terminology;
|
|
36
|
+
}
|