@warpgogol/forge 2.20.0 → 2.21.1
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/README.md +3 -2
- package/README.uk.md +7 -1
- package/os/adr/adr.module.ts +2 -0
- package/os/compass/compass.module.ts +6 -0
- package/os/core/core.module.ts +16 -0
- package/os/naming/naming.module.ts +2 -0
- package/os/notes/notes.module.ts +4 -0
- package/os/plugin/plugin.module.ts +2 -0
- package/os/program/program.module.ts +2 -0
- package/os/rfc/acceptance.ts +1 -1
- package/os/rfc/rfc.module.ts +10 -0
- package/os/session/session.module.ts +2 -0
- package/os/spec/spec.module.ts +4 -0
- package/os/werkstatt/werkstatt.module.ts +2 -0
- package/os/workflow/workflow.module.ts +2 -0
- package/package.json +1 -1
- package/profiles/root-agents-godot.md +4 -4
- package/skills/meta/forge-bootstrap/SKILL.md +6 -6
- package/src/index.ts +1 -0
- package/src/migration-adapters/git-utils.ts +4 -3
- package/src/onboarding/agents-generate.ts +1 -0
- package/src/onboarding/create.ts +19 -0
- package/src/onboarding/doctor.ts +37 -0
- package/src/onboarding/nested-agents-templates.ts +2 -1
- package/src/onboarding/templates/root-agents-business.md +1 -1
- package/src/onboarding/templates/root-agents-creative.md +1 -1
- package/src/tests/fixtures/agents-generate-business-before.txt +3 -3
- package/src/tests/generated-marker.test.ts +14 -0
- package/src/tests/migration-adapters.test.ts +21 -0
- package/src/types/werkstatt-engine-shims.d.ts +93 -12
- package/src/types.ts +4 -0
- package/src/utils/generated-marker.ts +55 -36
- package/src/utils/index.ts +1 -0
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Replace `[describe your project]` with your idea — a game, a library, a knowle
|
|
|
20
20
|
|
|
21
21
|
## What you can build with Forge
|
|
22
22
|
|
|
23
|
-
Forge supports
|
|
23
|
+
Forge supports five kinds of projects. You pick one when you start — everything else is automatic.
|
|
24
24
|
|
|
25
25
|
| Project type | What it is | Example ideas |
|
|
26
26
|
| --- | --- | --- |
|
|
@@ -28,6 +28,7 @@ Forge supports four kinds of projects. You pick one when you start — everythin
|
|
|
28
28
|
| **Governance / library** | A code library or governance-only project — no website, no game, no video, just structure and documentation | npm package, internal toolkit, documentation hub |
|
|
29
29
|
| **Godot game** | A desktop or mobile game built with Godot 4.x and C# — 2D, 3D, platformer, RPG | Top-down adventure, 3D platformer, puzzle game |
|
|
30
30
|
| **Knowledge system** | An evidence-backed knowledge base — structured datasets where claims are backed by registered source provenance | Game knowledge base, domain ontology, curated reference dataset |
|
|
31
|
+
| **TypeScript library** | A generic TypeScript monorepo with best-practice validators — tsconfig, import boundaries, phantom deps, package exports, strict mode, barrel safety | Internal toolkit, shared library, TypeScript monorepo |
|
|
31
32
|
|
|
32
33
|
Each project type gets its own scaffold: the right folder structure, the right dependencies, the right tools. You don't need to know what any of those are — Forge sets them up for you.
|
|
33
34
|
|
|
@@ -46,7 +47,7 @@ For full project lifecycle management — missions, releases, deployment, certif
|
|
|
46
47
|
| [`@warpgogol/werkstatt-phaser-game`](https://github.com/syrokomskyi/werkstatt-phaser-game) | Phaser game plugin — phaser validators, Vite build, deploy adapters | Browser game projects (`phaser-turborepo`) |
|
|
47
48
|
| [`@warpgogol/werkstatt-godot-game`](https://github.com/syrokomskyi/werkstatt-godot-game) | Godot plugin — scene validators, dotnet build, itch.io deploy, Godot skills | Godot game projects (`godot-csharp`) |
|
|
48
49
|
| [`@warpgogol/werkstatt-knowledge`](https://github.com/syrokomskyi/werkstatt-knowledge) | Knowledge plugin — source binding, canonical verification, extraction, materialization, release checks | Knowledge system projects (`knowledge-typescript-turborepo`) |
|
|
49
|
-
| [`@warpgogol/werkstatt-typescript`](https://github.com/syrokomskyi/werkstatt) | TypeScript plugin — tsconfig, import boundaries, phantom deps, package exports, strict mode, barrel validators | TypeScript TurboRepo projects (`typescript-turborepo`) |
|
|
50
|
+
| [`@warpgogol/werkstatt-typescript`](https://github.com/syrokomskyi/werkstatt-typescript) | TypeScript plugin — tsconfig, import boundaries, phantom deps, package exports, strict mode, barrel validators | TypeScript TurboRepo projects (`typescript-turborepo`) |
|
|
50
51
|
|
|
51
52
|
### When you need these packages
|
|
52
53
|
|
package/README.uk.md
CHANGED
|
@@ -20,7 +20,7 @@ Install https://npmjs.com/package/@warpgogol/forge in this folder and set up my
|
|
|
20
20
|
|
|
21
21
|
## Що можна створити за допомогою Forge
|
|
22
22
|
|
|
23
|
-
Forge підтримує
|
|
23
|
+
Forge підтримує п'ять типів проєктів. Ви обираєте один на старті — все інше відбувається автоматично.
|
|
24
24
|
|
|
25
25
|
| Тип проєкту | Що це | Приклади |
|
|
26
26
|
| --- | --- | --- |
|
|
@@ -28,6 +28,7 @@ Forge підтримує чотири типи проєктів. Ви обира
|
|
|
28
28
|
| **Управління / бібліотека** | Бібліотека коду або проєкт лише з управлінською структурою — без сайту, без гри, без відео, лише структура та документація | npm-пакет, внутрішній інструмент, центр документації |
|
|
29
29
|
| **Гра Godot** | Десктопна або мобільна гра на Godot 4.x з C# — 2D, 3D, платформер, RPG | Пригода з виглядом зверху, 3D-платформер, головоломка |
|
|
30
30
|
| **Система знань** | База знань з підтримкою доказів — структуровані набори даних, де твердження підтверджені зареєстрованими джерелами | База знань гри, доменна онтологія, куратований довідковий набір даних |
|
|
31
|
+
| **Бібліотека TypeScript** | Універсальний TypeScript монорепозиторій з валідаторами найкращих практик — tsconfig, межі імпортів, фантомні залежності, експорти пакетів, strict mode, безпека barrel-експортів | Внутрішній інструмент, спільна бібліотека, TypeScript монорепозиторій |
|
|
31
32
|
|
|
32
33
|
Кожен тип проєкту отримує власний каркас: правильну структуру папок, правильні залежності, правильні інструменти. Вам не потрібно знати, що це таке — Forge налаштує все за вас.
|
|
33
34
|
|
|
@@ -46,6 +47,7 @@ Forge — це **шар управління**: навички, RFC/ADR робо
|
|
|
46
47
|
| [`@warpgogol/werkstatt-phaser-game`](https://github.com/syrokomskyi/werkstatt-phaser-game) | Плагін Phaser-гри — валідатори phaser, збірка Vite, адаптери розгортання | Проєкти браузерних ігор (`phaser-turborepo`) |
|
|
47
48
|
| [`@warpgogol/werkstatt-godot-game`](https://github.com/syrokomskyi/werkstatt-godot-game) | Плагін Godot — валідатори сцен, збірка dotnet, розгортання itch.io, навички Godot | Проєкти ігор Godot (`godot-csharp`) |
|
|
48
49
|
| [`@warpgogol/werkstatt-knowledge`](https://github.com/syrokomskyi/werkstatt-knowledge) | Плагін системи знань — прив'язка джерел, канонічна верифікація, екстракція, матеріалізація, перевірки релізу | Проєкти систем знань (`knowledge-typescript-turborepo`) |
|
|
50
|
+
| [`@warpgogol/werkstatt-typescript`](https://github.com/syrokomskyi/werkstatt-typescript) | Плагін TypeScript — tsconfig, межі імпортів, фантомні залежності, експорти пакетів, strict mode, barrel-валідатори | Проєкти TypeScript TurboRepo (`typescript-turborepo`) |
|
|
49
51
|
|
|
50
52
|
### Коли потрібні ці пакети
|
|
51
53
|
|
|
@@ -63,6 +65,9 @@ pnpm add -D @warpgogol/werkstatt @warpgogol/werkstatt-godot-game
|
|
|
63
65
|
|
|
64
66
|
# Проєкт системи знань
|
|
65
67
|
pnpm add -D @warpgogol/werkstatt @warpgogol/werkstatt-knowledge
|
|
68
|
+
|
|
69
|
+
# Проєкт TypeScript TurboRepo
|
|
70
|
+
pnpm add -D @warpgogol/werkstatt @warpgogol/werkstatt-typescript
|
|
66
71
|
```
|
|
67
72
|
|
|
68
73
|
> **Примітка для агентів:** Після створення проєкту командою `forge create` завжди встановлюйте рушій та відповідний плагін стеку. Прив'язки в `forge.yaml` посилаються на команди з цих пакунків (напр. `pnpm exec werkstatt run rfc.validate`), і вони не працюватимуть, якщо пакунки не встановлені.
|
|
@@ -343,6 +348,7 @@ pnpm exec forge skill.list
|
|
|
343
348
|
| `phaser-turborepo` | Браузерна гра | Phaser + TypeScript + pnpm + Turborepo | `games/my-game` | Браузерні ігри, інтерактивні досвіди |
|
|
344
349
|
| `godot-csharp` | Гра Godot | Godot 4.x + C# + pnpm + Turborepo | `games/my-game` | Десктопні/мобільні ігри, інтерактивні проєкти на Godot |
|
|
345
350
|
| `knowledge-typescript-turborepo` | Система знань | TypeScript + pnpm + Turborepo | `knowledge/my-kb` | Бази знань з підтримкою доказів, структуровані набори даних з джерельною простежуваністю |
|
|
351
|
+
| `typescript-turborepo` | Бібліотека TypeScript | TypeScript + pnpm + Turborepo | — | Універсальний TypeScript TurboRepo монорепозиторій з валідаторами найкращих практик |
|
|
346
352
|
|
|
347
353
|
```sh
|
|
348
354
|
# Список доступних профілів (після встановлення)
|
package/os/adr/adr.module.ts
CHANGED
|
@@ -87,6 +87,8 @@ export const forgeAdrModule: ForgeModule = {
|
|
|
87
87
|
|
|
88
88
|
registry.registerCommand({
|
|
89
89
|
name: "adr.validate",
|
|
90
|
+
contract: "adr",
|
|
91
|
+
rules: [],
|
|
90
92
|
description:
|
|
91
93
|
"Validate ADR frontmatter schema, required markdown sections, " +
|
|
92
94
|
"referential integrity (supersedes/supersededBy), and id/filename consistency. " +
|
|
@@ -62,6 +62,8 @@ export const forgeCompassModule: ForgeModule = {
|
|
|
62
62
|
});
|
|
63
63
|
registry.registerCommand({
|
|
64
64
|
name: "compass.validate",
|
|
65
|
+
contract: "compass",
|
|
66
|
+
rules: [],
|
|
65
67
|
description:
|
|
66
68
|
"Validate authored source files against current Compass scaffolding requirements.",
|
|
67
69
|
scope: "workspace",
|
|
@@ -77,6 +79,8 @@ export const forgeCompassModule: ForgeModule = {
|
|
|
77
79
|
});
|
|
78
80
|
registry.registerCommand({
|
|
79
81
|
name: "compass.changesummary.validate",
|
|
82
|
+
contract: "compass",
|
|
83
|
+
rules: [],
|
|
80
84
|
description:
|
|
81
85
|
"Validate CHANGE_SUMMARY blocks for boilerplate items and over-cap unprotected items (RFC-0349).",
|
|
82
86
|
scope: "workspace",
|
|
@@ -171,6 +175,8 @@ export const forgeCompassModule: ForgeModule = {
|
|
|
171
175
|
});
|
|
172
176
|
registry.registerCommand({
|
|
173
177
|
name: "compass.audit.validate",
|
|
178
|
+
contract: "compass",
|
|
179
|
+
rules: [],
|
|
174
180
|
description:
|
|
175
181
|
"Validate that no authored file is audit-overdue per the revision threshold (RFC-0352). Warns by default, fails with --strict.",
|
|
176
182
|
scope: "workspace",
|
package/os/core/core.module.ts
CHANGED
|
@@ -190,6 +190,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
190
190
|
});
|
|
191
191
|
registry.registerCommand({
|
|
192
192
|
name: "forge.skill.validate",
|
|
193
|
+
contract: "forge",
|
|
194
|
+
rules: [],
|
|
193
195
|
description:
|
|
194
196
|
"Validate all forge skills against frontmatter contract and invariants SKILL-01..SKILL-20.",
|
|
195
197
|
scope: "workspace",
|
|
@@ -209,6 +211,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
209
211
|
});
|
|
210
212
|
registry.registerCommand({
|
|
211
213
|
name: "forge.port.validate",
|
|
214
|
+
contract: "forge",
|
|
215
|
+
rules: [],
|
|
212
216
|
description: "Validate a single ported skill or command for compliance with forge contracts.",
|
|
213
217
|
scope: "workspace",
|
|
214
218
|
supportsAllSites: false,
|
|
@@ -374,6 +378,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
374
378
|
};
|
|
375
379
|
registry.registerCommand({
|
|
376
380
|
name: "forge.profile.validate",
|
|
381
|
+
contract: "forge",
|
|
382
|
+
rules: [],
|
|
377
383
|
description:
|
|
378
384
|
"Validate profile YAML files under packages/forge/profiles/ against the stack-profile schema (RFC-0640).",
|
|
379
385
|
scope: "workspace",
|
|
@@ -437,6 +443,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
437
443
|
// ── forge.validate (RFC-0674, RFC-0677) ───────────────────────────────────
|
|
438
444
|
registry.registerCommand({
|
|
439
445
|
name: "forge.validate",
|
|
446
|
+
contract: "forge",
|
|
447
|
+
rules: [],
|
|
440
448
|
description:
|
|
441
449
|
"Execute validate commands for all artifacts declared in the active stack profile. Use --dry-run to print resolved commands. Use --artifact to validate a single artifact.",
|
|
442
450
|
scope: "workspace",
|
|
@@ -464,6 +472,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
464
472
|
const { runDeterminismCheck } = await import("./handlers/determinism-check.ts");
|
|
465
473
|
registry.registerCommand({
|
|
466
474
|
name: "forge.determinism.check",
|
|
475
|
+
contract: "forge",
|
|
476
|
+
rules: [],
|
|
467
477
|
description:
|
|
468
478
|
"Verify artifact determinism by building twice and comparing output hashes. Reads determinism.inputs glob patterns from the active stack profile. Use --dry-run to print resolved inputs, --artifact to check a single artifact.",
|
|
469
479
|
scope: "workspace",
|
|
@@ -519,6 +529,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
519
529
|
const { runAssetsCheck } = await import("./handlers/assets-check.ts");
|
|
520
530
|
registry.registerCommand({
|
|
521
531
|
name: "forge.assets.check",
|
|
532
|
+
contract: "forge",
|
|
533
|
+
rules: [],
|
|
522
534
|
description:
|
|
523
535
|
"Check for missing, orphaned, and unreferenced assets. Use --strict to fail on orphaned assets, --dry-run to skip hashing.",
|
|
524
536
|
scope: "workspace",
|
|
@@ -641,6 +653,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
641
653
|
// ── pinned.validate ─────────────────────────────────────────────────────
|
|
642
654
|
registry.registerCommand({
|
|
643
655
|
name: "pinned.validate",
|
|
656
|
+
contract: "pinned",
|
|
657
|
+
rules: [],
|
|
644
658
|
description:
|
|
645
659
|
"Validate working tree against .forge/pinned.yaml manifest. " +
|
|
646
660
|
"Checks staged changes (default) or last commit (--mode ci) for " +
|
|
@@ -726,6 +740,8 @@ export const forgeCoreModule: ForgeModule = {
|
|
|
726
740
|
const { runForgeAutonomyValidate } = await import("./handlers/forge-autonomy-validate.ts");
|
|
727
741
|
registry.registerCommand({
|
|
728
742
|
name: "forge.autonomy.validate",
|
|
743
|
+
contract: "forge",
|
|
744
|
+
rules: [],
|
|
729
745
|
description:
|
|
730
746
|
"Scan packages/forge/os/** for forbidden @warpgogol/werkstatt-engine imports outside os/werkstatt/. " +
|
|
731
747
|
"Enforces FORGE-AUTONOMY-01 (RFC-0940, DNA-64). @warpgogol/werkstatt-shared is exempt. " +
|
|
@@ -20,6 +20,8 @@ export const forgeNamingModule: ForgeModule = {
|
|
|
20
20
|
const { runNamingConventionLint } = await import("./naming-convention.ts");
|
|
21
21
|
registry.registerCommand({
|
|
22
22
|
name: "naming.convention.lint",
|
|
23
|
+
contract: "naming",
|
|
24
|
+
rules: [],
|
|
23
25
|
description:
|
|
24
26
|
"Validate all filenames use kebab-case (no underscores) across registered workspace roots.",
|
|
25
27
|
scope: "workspace",
|
package/os/notes/notes.module.ts
CHANGED
|
@@ -50,6 +50,8 @@ export const forgeNotesModule: ForgeModule = {
|
|
|
50
50
|
|
|
51
51
|
registry.registerCommand({
|
|
52
52
|
name: "note.link.validate",
|
|
53
|
+
contract: "note",
|
|
54
|
+
rules: [],
|
|
53
55
|
description:
|
|
54
56
|
"Validate wikilink integrity across a markdown note vault. Scans [[wikilinks]] and resolves each against the note graph.",
|
|
55
57
|
scope: "workspace",
|
|
@@ -71,6 +73,8 @@ export const forgeNotesModule: ForgeModule = {
|
|
|
71
73
|
|
|
72
74
|
registry.registerCommand({
|
|
73
75
|
name: "note.frontmatter.validate",
|
|
76
|
+
contract: "note",
|
|
77
|
+
rules: [],
|
|
74
78
|
description:
|
|
75
79
|
"Validate frontmatter consistency across a markdown note vault. Checks for required fields in YAML frontmatter.",
|
|
76
80
|
scope: "workspace",
|
|
@@ -236,6 +236,8 @@ export const forgePluginModule: ForgeModule = {
|
|
|
236
236
|
async register(registry) {
|
|
237
237
|
registry.registerCommand({
|
|
238
238
|
name: "forge.plugin.validate",
|
|
239
|
+
contract: "forge",
|
|
240
|
+
rules: [],
|
|
239
241
|
description:
|
|
240
242
|
"Validate forge.plugin.yaml manifests for all declared skill packs (RFC-0941). Checks manifest schema (id: kebab-case, version: semver) and reports missing or invalid manifests.",
|
|
241
243
|
scope: "workspace",
|
|
@@ -26,6 +26,8 @@ export const forgeProgramModule: ForgeModule = {
|
|
|
26
26
|
|
|
27
27
|
registry.registerCommand({
|
|
28
28
|
name: "program.packet.validate",
|
|
29
|
+
contract: "program",
|
|
30
|
+
rules: [],
|
|
29
31
|
description:
|
|
30
32
|
"Validate a program packet against schema, source hashes, branch/head, " +
|
|
31
33
|
"and state machine rules. Read-only. " +
|
package/os/rfc/acceptance.ts
CHANGED
|
@@ -158,7 +158,7 @@ async function spawnSiteKernel(
|
|
|
158
158
|
.trim()
|
|
159
159
|
.split(/\s+/)
|
|
160
160
|
.filter(Boolean);
|
|
161
|
-
const binPath = path.join(workspaceRoot, "packages", "werkstatt", "bin", "werkstatt.mjs");
|
|
161
|
+
const binPath = path.join(workspaceRoot, "packages", "werkstatt-engine", "bin", "werkstatt.mjs");
|
|
162
162
|
|
|
163
163
|
return new Promise((resolve) => {
|
|
164
164
|
const child = spawn(process.execPath, [binPath, ...args], {
|
package/os/rfc/rfc.module.ts
CHANGED
|
@@ -114,6 +114,8 @@ export const forgeRfcModule: ForgeModule = {
|
|
|
114
114
|
// ── rfc.validate ─────────────────────────────────────────────────────────
|
|
115
115
|
registry.registerCommand({
|
|
116
116
|
name: "rfc.validate",
|
|
117
|
+
contract: "rfc",
|
|
118
|
+
rules: [],
|
|
117
119
|
description:
|
|
118
120
|
"Validate RFC frontmatter schema, required markdown sections, " +
|
|
119
121
|
"referential integrity (supersedes/supersededBy), date consistency, " +
|
|
@@ -130,6 +132,8 @@ export const forgeRfcModule: ForgeModule = {
|
|
|
130
132
|
// ── rfc.command-lifecycle.validate ───────────────────────────────────────
|
|
131
133
|
registry.registerCommand({
|
|
132
134
|
name: "rfc.command-lifecycle.validate",
|
|
135
|
+
contract: "rfc",
|
|
136
|
+
rules: [],
|
|
133
137
|
description:
|
|
134
138
|
"Validate RFC commands.proposed/added/changed/removed lifecycle metadata " +
|
|
135
139
|
"against live registered commands. Emits RFC-CMD-* diagnostics.",
|
|
@@ -145,6 +149,8 @@ export const forgeRfcModule: ForgeModule = {
|
|
|
145
149
|
// ── rfc.check ────────────────────────────────────────────────────────────
|
|
146
150
|
registry.registerCommand({
|
|
147
151
|
name: "rfc.check",
|
|
152
|
+
contract: "rfc",
|
|
153
|
+
rules: [],
|
|
148
154
|
description:
|
|
149
155
|
"Validate that artifacts declared by accepted/implemented RFCs exist on disk. " +
|
|
150
156
|
"Checks files from 'File system responsibilities' tables and feature flag references. " +
|
|
@@ -184,6 +190,8 @@ export const forgeRfcModule: ForgeModule = {
|
|
|
184
190
|
// ── rfc.index.validate ───────────────────────────────────────────────────
|
|
185
191
|
registry.registerCommand({
|
|
186
192
|
name: "rfc.index.validate",
|
|
193
|
+
contract: "rfc",
|
|
194
|
+
rules: [],
|
|
187
195
|
description:
|
|
188
196
|
"Validate that docs/rfcs/index.yaml exists, is parseable, and its entry count " +
|
|
189
197
|
"matches the number of RFC files on disk. Reports RFC-IDX-01 (missing), " +
|
|
@@ -254,6 +262,8 @@ export const forgeRfcModule: ForgeModule = {
|
|
|
254
262
|
// ── rfc.dna.trace.validate ───────────────────────────────────────────────
|
|
255
263
|
registry.registerCommand({
|
|
256
264
|
name: "rfc.dna.trace.validate",
|
|
265
|
+
contract: "rfc",
|
|
266
|
+
rules: [],
|
|
257
267
|
description:
|
|
258
268
|
"RFC-0331: validate the bidirectional DNA-trace matrix — which RFCs satisfy each DNA " +
|
|
259
269
|
"invariant and which invariants each RFC claims to satisfy. Reports DNA-TRACE-01 " +
|
|
@@ -80,6 +80,8 @@ export const forgeSessionModule: ForgeModule = {
|
|
|
80
80
|
|
|
81
81
|
registry.registerCommand({
|
|
82
82
|
name: "session.validate",
|
|
83
|
+
contract: "session",
|
|
84
|
+
rules: [],
|
|
83
85
|
description:
|
|
84
86
|
"Validate session frontmatter schema (SES-01), id-filename match (SES-02), " +
|
|
85
87
|
"RFC-id existence (SES-03), raw file hygiene (SES-04), and non-markdown " +
|
package/os/spec/spec.module.ts
CHANGED
|
@@ -29,6 +29,8 @@ export const forgeSpecModule: ForgeModule = {
|
|
|
29
29
|
|
|
30
30
|
registry.registerCommand({
|
|
31
31
|
name: "spec.validate",
|
|
32
|
+
contract: "spec",
|
|
33
|
+
rules: [],
|
|
32
34
|
description:
|
|
33
35
|
"Validate vendored spec packages under docs/specs/. " +
|
|
34
36
|
"Checks integrity (SHA-256), schema, dependency graph (acyclic), " +
|
|
@@ -123,6 +125,8 @@ export const forgeSpecModule: ForgeModule = {
|
|
|
123
125
|
|
|
124
126
|
registry.registerCommand({
|
|
125
127
|
name: "spec.live.validate",
|
|
128
|
+
contract: "spec",
|
|
129
|
+
rules: [],
|
|
126
130
|
description:
|
|
127
131
|
"Validate all living feature specs in docs/specs/live/. " +
|
|
128
132
|
"Checks V-LS-01 (frontmatter), V-LS-02 (domain/filename match), " +
|
|
@@ -51,6 +51,8 @@ export const forgeWerkstattModule: ForgeModule = {
|
|
|
51
51
|
});
|
|
52
52
|
registry.registerCommand({
|
|
53
53
|
name: "werkstatt.operation.validate",
|
|
54
|
+
contract: "werkstatt",
|
|
55
|
+
rules: [],
|
|
54
56
|
description:
|
|
55
57
|
"Validate that mutating Werkstatt commands use shared lock/idempotency/atomic-write helpers (RFC-0362).",
|
|
56
58
|
scope: "workspace",
|
|
@@ -22,6 +22,8 @@ export const forgeWorkflowModule: ForgeModule = {
|
|
|
22
22
|
await import("./handlers.ts");
|
|
23
23
|
registry.registerCommand({
|
|
24
24
|
name: "workflow.lint",
|
|
25
|
+
contract: "workflow",
|
|
26
|
+
rules: [],
|
|
25
27
|
description:
|
|
26
28
|
"Validate .agents/workflows AND .agents/workflows-amend markdown frontmatter, command references, " +
|
|
27
29
|
"and per-chain phase links (RFC-0075 + RFC-0136).",
|
package/package.json
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
Placeholders: projectName, projectStack, projectPm,
|
|
5
5
|
rfcsDir, adrsDir, plansDir, auditsDir, specsDir,
|
|
6
6
|
skillsDir, dynamicSections -->
|
|
7
|
+
|
|
7
8
|
# Agent Guide: {{projectName}}
|
|
8
9
|
|
|
9
|
-
> This file is generated by `forge.agents.generate` from `forge.yaml`.
|
|
10
|
-
> Do not edit by hand — edit `forge.yaml` and regenerate.
|
|
10
|
+
> This file is generated by `forge.agents.generate` from `forge.yaml`. It is a living document — edit freely to add project-specific guidance.
|
|
11
11
|
|
|
12
12
|
## Project
|
|
13
13
|
|
|
@@ -52,8 +52,7 @@ dotnet build
|
|
|
52
52
|
dotnet test
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
Run the editor or game through the installed Godot .NET binary.
|
|
56
|
-
Do not change the Godot version, .NET SDK, NuGet packages, or project settings without explicit need.
|
|
55
|
+
Run the editor or game through the installed Godot .NET binary. Do not change the Godot version, .NET SDK, NuGet packages, or project settings without explicit need.
|
|
57
56
|
|
|
58
57
|
## C# Rules
|
|
59
58
|
|
|
@@ -103,6 +102,7 @@ Before completing a task:
|
|
|
103
102
|
- When facing an unclear architectural decision, propose options and ask for direction first.
|
|
104
103
|
|
|
105
104
|
{{dynamicSections}}
|
|
105
|
+
|
|
106
106
|
## RTK — Token Optimization
|
|
107
107
|
|
|
108
108
|
Always prefix shell commands with `rtk` to minimize token consumption. RTK filters and compresses command output before it reaches the LLM context, cutting up to 90% of bash output on common operations.
|
|
@@ -136,7 +136,7 @@ Ask the operator:
|
|
|
136
136
|
1. **Stack** — ask what stack the project uses (TypeScript, Python, Rust, etc.). Write into `forge.yaml` `project.stack`.
|
|
137
137
|
2. **Package manager** — confirm or override `project.packageManager` in `forge.yaml`.
|
|
138
138
|
3. **Stack bindings** — fill `commands.typecheck`, `commands.test`, `commands.scopedBuild` in `forge.yaml`. Ask the operator for the exact commands (e.g. `tsc --noEmit`, `vitest`, `turbo run build`).
|
|
139
|
-
4. **Git init** —
|
|
139
|
+
4. **Git init** — `forge.create` already initializes git with `--initial-branch=main` programmatically. Verify `.git` exists in the project root. If it does, proceed. If not (e.g. `forge.create` failed to init git), run `git init --initial-branch=main` manually. Make an initial commit with all project files on the `main` branch.
|
|
140
140
|
|
|
141
141
|
#### Transplant (adapter-driven migration)
|
|
142
142
|
|
|
@@ -168,11 +168,11 @@ The transplant mode performs real code migration via a migration-adapter registr
|
|
|
168
168
|
|
|
169
169
|
If yes: the skill uses `git format-patch` (export all commits from source) + `git am` (apply them in the new project) to preserve history without copying the `.git` directory. If the source has many commits, warn that this may take a moment.
|
|
170
170
|
|
|
171
|
-
If no: the skill runs `git init` in the new project (clean repository, no history from source).
|
|
171
|
+
If no: the skill runs `git init --initial-branch=main` in the new project (clean repository, no history from source).
|
|
172
172
|
|
|
173
|
-
If git history transfer fails (complex history, submodules, replace objects): warn and continue with a clean `git init`.
|
|
173
|
+
If git history transfer fails (complex history, submodules, replace objects): warn and continue with a clean `git init --initial-branch=main`.
|
|
174
174
|
|
|
175
|
-
If the source project has no `.git` directory: skip the git history question and run `git init` directly.
|
|
175
|
+
If the source project has no `.git` directory: skip the git history question and run `git init --initial-branch=main` directly.
|
|
176
176
|
|
|
177
177
|
6. **Post-setup** — call `postSetup()`. The adapter initializes git (clean init or format-patch + git am based on operator's choice), updates `pnpm-workspace.yaml` (adds `apps/<appName>`), `turbo.json` (adds workspace to pipeline), runs the install command derived from `ref(forge.yaml project.packageManager)`. The skill captures bin-link ENOENT warnings from the install output — these are cosmetic and indicate that `dist/` does not exist yet (the project has not been built). If the install itself fails (exit code != 0), report the dependency resolution error in human language (in `aiLanguage`), skip build verification, and continue to the welcoming report. The operator can fix and re-run onboarding.
|
|
178
178
|
|
|
@@ -434,8 +434,8 @@ A short description after every onboarding, read from `forge-about.md`. The skil
|
|
|
434
434
|
- `doctor` is unavailable → skill skips auto-doctor and notes it in the report; operator can ask the agent to run it later.
|
|
435
435
|
- Auto-ADR creation fails → skill reports the error silently (in agent logs, not to operator), leaves the ADR file in place for the agent to fix, and continues to the welcoming report. The operator is never told about the ADR.
|
|
436
436
|
- Project analysis finds nothing recommendable (transplant) → skill skips recommendations and proceeds to the welcoming report with a direct invitation to start creating.
|
|
437
|
-
- Git history transfer fails → skill warns and continues with clean `git init`.
|
|
438
|
-
- Source project has no `.git` → skill skips the git history question and runs `git init` directly.
|
|
437
|
+
- Git history transfer fails → skill warns and continues with clean `git init --initial-branch=main`.
|
|
438
|
+
- Source project has no `.git` → skill skips the git history question and runs `git init --initial-branch=main` directly.
|
|
439
439
|
- Post-setup `pnpm install` fails → skill reports the error; the operator can fix and re-run `pnpm install` manually.
|
|
440
440
|
- Migration interrupted mid-copy (process crash, agent termination) → `migrate()` is idempotent: re-running skips files that already exist at the target with matching content. The operator can also delete `apps/<appName>/` and re-run for a clean migration.
|
|
441
441
|
- Concurrent execution (two agents running `forge-bootstrap` transplant on the same forge project simultaneously) → the skill does not lock `apps/<appName>/`; concurrent writes may conflict. The operator should not run two transplant operations on the same project in parallel.
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<CHANGE_SUMMARY>
|
|
10
10
|
<item>RFC-0547: extract shared postSetup git logic from duplicated adapter implementations.</item>
|
|
11
11
|
<item>Replace fs.rmSync with trashSync for patch directory cleanup (trash bin for LLM-initiated deletions).</item>
|
|
12
|
+
<item>Use --initial-branch=main for all git init calls so new repos default to main instead of master.</item>
|
|
12
13
|
</CHANGE_SUMMARY>
|
|
13
14
|
*/
|
|
14
15
|
|
|
@@ -32,7 +33,7 @@ export function runPostSetup(
|
|
|
32
33
|
execFileSync("git", ["-C", sourceDir, "format-patch", "--all", "-o", patchDir], {
|
|
33
34
|
stdio: "pipe",
|
|
34
35
|
});
|
|
35
|
-
execFileSync("git", ["init"], { cwd: targetDir, stdio: "pipe" });
|
|
36
|
+
execFileSync("git", ["init", "--initial-branch=main"], { cwd: targetDir, stdio: "pipe" });
|
|
36
37
|
execFileSync("git", ["config", "user.email", "forge@warpgogol.dev"], {
|
|
37
38
|
cwd: targetDir,
|
|
38
39
|
stdio: "pipe",
|
|
@@ -60,12 +61,12 @@ export function runPostSetup(
|
|
|
60
61
|
`forge: git history transfer failed, falling back to clean git init: ${err instanceof Error ? err.message : String(err)}`,
|
|
61
62
|
);
|
|
62
63
|
if (!fs.existsSync(gitDir)) {
|
|
63
|
-
execFileSync("git", ["init"], { cwd: targetDir, stdio: "pipe" });
|
|
64
|
+
execFileSync("git", ["init", "--initial-branch=main"], { cwd: targetDir, stdio: "pipe" });
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
} else {
|
|
67
68
|
if (!fs.existsSync(gitDir)) {
|
|
68
|
-
execFileSync("git", ["init"], { cwd: targetDir, stdio: "pipe" });
|
|
69
|
+
execFileSync("git", ["init", "--initial-branch=main"], { cwd: targetDir, stdio: "pipe" });
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
}
|
package/src/onboarding/create.ts
CHANGED
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
<item>RFC-0643: pass profileId to runInit so forge.yaml gets a `profile` field.</item>
|
|
18
18
|
<item>RFC-0664: scaffold memory layer (.agents/memory/) after init.</item>
|
|
19
19
|
<item>RFC-0877: in-place mode only — --in-place flag required, no subdirectory creation, name derived from folder, strict empty-directory check (only .git/ tolerated).</item>
|
|
20
|
+
<item>Initialize git repo with --initial-branch=main if .git does not exist (hard guard for main branch default).</item>
|
|
20
21
|
</CHANGE_SUMMARY>
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
24
|
import fs from "node:fs";
|
|
24
25
|
import path from "node:path";
|
|
26
|
+
import { execFileSync } from "node:child_process";
|
|
25
27
|
import { stringify as stringifyYaml } from "yaml";
|
|
26
28
|
import { runScaffoldProject } from "./scaffold-project.ts";
|
|
27
29
|
import { runInit, type InitResult, type InitDomainFields } from "./init.ts";
|
|
@@ -229,6 +231,23 @@ export async function runCreate(
|
|
|
229
231
|
};
|
|
230
232
|
}
|
|
231
233
|
|
|
234
|
+
// 6.5. Initialize git repo with main branch (if not already initialized)
|
|
235
|
+
// This is a hard guard — forge.create programmatically ensures main branch,
|
|
236
|
+
// so greenfield projects don't depend on the agent running git init correctly.
|
|
237
|
+
const gitDir = path.join(targetDir, ".git");
|
|
238
|
+
if (!fs.existsSync(gitDir)) {
|
|
239
|
+
try {
|
|
240
|
+
execFileSync("git", ["init", "--initial-branch=main"], { cwd: targetDir, stdio: "pipe" });
|
|
241
|
+
if (outputFormat === "pretty") {
|
|
242
|
+
logger.info("Initialized git repository with main branch");
|
|
243
|
+
}
|
|
244
|
+
} catch {
|
|
245
|
+
if (outputFormat === "pretty") {
|
|
246
|
+
logger.warn("Failed to initialize git repository — run 'git init --initial-branch=main' manually");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
232
251
|
// 7. Resolve forge root
|
|
233
252
|
let forgeRoot: string;
|
|
234
253
|
if (context.forgeRoot) {
|
package/src/onboarding/doctor.ts
CHANGED
|
@@ -23,6 +23,7 @@ Checks for forge.yaml, AGENTS.md, PREFERENCES.md, .agents/skills/, docs/rfcs/,
|
|
|
23
23
|
<item>RFC-0664: added memory-layer health check (budget usage, gitignore coverage, daily-file leak risk).</item>
|
|
24
24
|
<item>RFC-0704: added independent-version-packages check — validates that paths in independentVersionPackages exist and contain package.json.</item>
|
|
25
25
|
<item>RFC-0941: added pack-manifests advisory check — validates forge.plugin.yaml existence and schema for each declared skill pack.</item>
|
|
26
|
+
<item>Added git-branch check — warns when current branch is 'master' instead of 'main'.</item>
|
|
26
27
|
</CHANGE_SUMMARY>
|
|
27
28
|
*/
|
|
28
29
|
|
|
@@ -1074,6 +1075,39 @@ function checkMemoryLayer(workspaceRoot: string): DoctorCheck {
|
|
|
1074
1075
|
};
|
|
1075
1076
|
}
|
|
1076
1077
|
|
|
1078
|
+
// ---------------------------------------------------------------------------
|
|
1079
|
+
// Git branch check — warn if current branch is 'master' instead of 'main'
|
|
1080
|
+
// ---------------------------------------------------------------------------
|
|
1081
|
+
|
|
1082
|
+
function checkGitBranch(workspaceRoot: string): DoctorCheck {
|
|
1083
|
+
const gitDir = join(workspaceRoot, ".git");
|
|
1084
|
+
if (!existsSync(gitDir)) {
|
|
1085
|
+
return { name: "git-branch", status: "pass", message: "No git repository — git branch check skipped" };
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
try {
|
|
1089
|
+
const branch = execSync("git branch --show-current", { cwd: workspaceRoot, stdio: "pipe", timeout: 5000 })
|
|
1090
|
+
.toString()
|
|
1091
|
+
.trim();
|
|
1092
|
+
|
|
1093
|
+
if (branch === "master") {
|
|
1094
|
+
return {
|
|
1095
|
+
name: "git-branch",
|
|
1096
|
+
status: "warn",
|
|
1097
|
+
message: "Current branch is 'master' — rename to 'main' with: git branch -m main",
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return {
|
|
1102
|
+
name: "git-branch",
|
|
1103
|
+
status: "pass",
|
|
1104
|
+
message: `Current branch: ${branch}`,
|
|
1105
|
+
};
|
|
1106
|
+
} catch {
|
|
1107
|
+
return { name: "git-branch", status: "pass", message: "Unable to determine git branch — check skipped" };
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1077
1111
|
// ---------------------------------------------------------------------------
|
|
1078
1112
|
// Prerequisite checks (profile-declared system dependencies)
|
|
1079
1113
|
// ---------------------------------------------------------------------------
|
|
@@ -1177,6 +1211,9 @@ export async function runDoctor(
|
|
|
1177
1211
|
: "docs/adrs/ directory not found — run 'forge create' to create ADR directory",
|
|
1178
1212
|
});
|
|
1179
1213
|
|
|
1214
|
+
// Check git branch is 'main' (not 'master')
|
|
1215
|
+
checks.push(checkGitBranch(workspaceRoot));
|
|
1216
|
+
|
|
1180
1217
|
// Check @warpgogol/* forbidden imports (autonomy guard)
|
|
1181
1218
|
let forgeRoot: string;
|
|
1182
1219
|
try {
|
|
@@ -184,6 +184,7 @@ export function buildNestedAgentsMd(
|
|
|
184
184
|
filePath: "AGENTS.md",
|
|
185
185
|
ownerCommand: "forge.agents.generate",
|
|
186
186
|
commandPrefix: "forge",
|
|
187
|
+
editable: true,
|
|
187
188
|
});
|
|
188
189
|
|
|
189
190
|
const lines: string[] = [];
|
|
@@ -200,7 +201,7 @@ export function buildNestedAgentsMd(
|
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
lines.push(`> This file is generated by \`forge.agents.generate\`.`);
|
|
203
|
-
lines.push(`>
|
|
204
|
+
lines.push(`> It is a living document — edit freely to add workspace-specific guidance.`);
|
|
204
205
|
lines.push("");
|
|
205
206
|
|
|
206
207
|
lines.push(`**Workspace type:** ${TYPE_LABEL[workspace.type]}`);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# Agent Guide: {{projectName}}
|
|
8
8
|
|
|
9
9
|
> This file is generated by `forge.agents.generate` from `forge.yaml`.
|
|
10
|
-
>
|
|
10
|
+
> It is a living document — edit freely to add project-specific guidance.
|
|
11
11
|
|
|
12
12
|
## Project
|
|
13
13
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# Agent Guide: {{projectName}}
|
|
8
8
|
|
|
9
9
|
> This file is generated by `forge.agents.generate` from `forge.yaml`.
|
|
10
|
-
>
|
|
10
|
+
> It is a living document — edit freely to add project-specific guidance.
|
|
11
11
|
|
|
12
12
|
## Project
|
|
13
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
GENERATED.
|
|
3
|
-
|
|
2
|
+
GENERATED. Safe to edit — project-specific changes are expected and encouraged.
|
|
3
|
+
This file is generated but editable — your changes are preserved on regeneration.
|
|
4
4
|
Owner command: forge.agents.generate
|
|
5
5
|
Edit instead: the forge.agents.generate generator source (not this file).
|
|
6
6
|
Regenerate: forge forge.agents.generate
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
# Agent Guide: test-project
|
|
17
17
|
|
|
18
18
|
> This file is generated by `forge.agents.generate` from `forge.yaml`.
|
|
19
|
-
>
|
|
19
|
+
> It is a living document — edit freely to add project-specific guidance.
|
|
20
20
|
|
|
21
21
|
## Project
|
|
22
22
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { test, expect, describe } from "vitest";
|
|
2
2
|
import {
|
|
3
3
|
GENERATED_MARKER,
|
|
4
|
+
EDITABLE_GENERATED_MARKER,
|
|
4
5
|
hasGeneratedMarker,
|
|
5
6
|
stripGeneratedMarker,
|
|
6
7
|
buildGeneratedHeader,
|
|
@@ -141,6 +142,19 @@ describe("buildGeneratedHeader", () => {
|
|
|
141
142
|
});
|
|
142
143
|
expect(header).toContain("Edit instead: templates/foo.ts");
|
|
143
144
|
});
|
|
145
|
+
|
|
146
|
+
test("editable mode uses permissive marker and advisory", () => {
|
|
147
|
+
const header = buildGeneratedHeader({
|
|
148
|
+
filePath: "AGENTS.md",
|
|
149
|
+
ownerCommand: "forge.agents.generate",
|
|
150
|
+
editable: true,
|
|
151
|
+
});
|
|
152
|
+
expect(header).toContain(EDITABLE_GENERATED_MARKER);
|
|
153
|
+
expect(header).not.toContain(GENERATED_MARKER);
|
|
154
|
+
expect(header).not.toContain("DO NOT EDIT");
|
|
155
|
+
expect(header).toContain("Safe to edit");
|
|
156
|
+
expect(header).toContain("editable");
|
|
157
|
+
});
|
|
144
158
|
});
|
|
145
159
|
|
|
146
160
|
describe("isGeneratedMarkerTextCandidate", () => {
|
|
@@ -293,6 +293,27 @@ test("migrate excludes .git directory from copy", async () => {
|
|
|
293
293
|
expect(existsSync(join(appDir, ".git"))).toBe(false);
|
|
294
294
|
});
|
|
295
295
|
|
|
296
|
+
test("postSetup creates main branch (not master) when gitHistory is false", async () => {
|
|
297
|
+
const sourceDir = join(tempDir, "source-main");
|
|
298
|
+
const targetDir = join(tempDir, "target-main");
|
|
299
|
+
|
|
300
|
+
await mkdir(sourceDir, { recursive: true });
|
|
301
|
+
await mkdir(targetDir, { recursive: true });
|
|
302
|
+
await writeFile(join(sourceDir, "package.json"), JSON.stringify({ name: "test-app" }));
|
|
303
|
+
await writeFile(join(sourceDir, "tsconfig.json"), "{}");
|
|
304
|
+
await writeFile(join(sourceDir, "pnpm-lock.yaml"), "");
|
|
305
|
+
|
|
306
|
+
const analysis = nodeTypescriptPnpmAdapter.analyze(sourceDir);
|
|
307
|
+
nodeTypescriptPnpmAdapter.postSetup(sourceDir, targetDir, analysis);
|
|
308
|
+
|
|
309
|
+
expect(existsSync(join(targetDir, ".git"))).toBe(true);
|
|
310
|
+
const { execSync } = await import("node:child_process");
|
|
311
|
+
const branch = execSync("git branch --show-current", { cwd: targetDir, stdio: "pipe" })
|
|
312
|
+
.toString()
|
|
313
|
+
.trim();
|
|
314
|
+
expect(branch).toBe("main");
|
|
315
|
+
});
|
|
316
|
+
|
|
296
317
|
test("postSetup runs git init when analysis.gitHistory is false", async () => {
|
|
297
318
|
const sourceDir = join(tempDir, "source");
|
|
298
319
|
const targetDir = join(tempDir, "target");
|
|
@@ -18,6 +18,10 @@ declare module "@warpgogol/werkstatt-engine/os/werkstatt-commands-validate-modul
|
|
|
18
18
|
export const werkstattCommandsValidateModule: any;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
declare module "@warpgogol/werkstatt-engine/os/werkstatt-e2e-module" {
|
|
22
|
+
export const werkstattE2eModule: any;
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
declare module "@warpgogol/werkstatt-engine/kernel" {
|
|
22
26
|
export function discoverSiteWorkspaces(...args: any[]): any;
|
|
23
27
|
export function loadKernelAppConfig(...args: any[]): any;
|
|
@@ -36,6 +40,7 @@ declare module "@warpgogol/werkstatt-engine/kernel" {
|
|
|
36
40
|
export const listRegisteredKernelPipelines: any;
|
|
37
41
|
export const loadAppRuntime: any;
|
|
38
42
|
export const executeKernelCommand: any;
|
|
43
|
+
export const executeRegisteredCommand: any;
|
|
39
44
|
export const executeKernelPipeline: any;
|
|
40
45
|
export const buildSchedule: any;
|
|
41
46
|
export const executeScheduledSteps: any;
|
|
@@ -61,6 +66,8 @@ declare module "@warpgogol/werkstatt-engine/kernel" {
|
|
|
61
66
|
export interface KernelNextStep {}
|
|
62
67
|
export interface KernelCommandResult {}
|
|
63
68
|
export interface CheckResult {}
|
|
69
|
+
export interface GeneratedArtifactSpec {}
|
|
70
|
+
export interface GeneratorOwnershipEntry {}
|
|
64
71
|
export interface KernelRuntimeContext {}
|
|
65
72
|
export interface KernelCommandDefinition {}
|
|
66
73
|
export interface KernelPipelineStep {}
|
|
@@ -182,6 +189,7 @@ declare module "@warpgogol/werkstatt-engine/kernel" {
|
|
|
182
189
|
export const WriteFileAtomicOptions: any;
|
|
183
190
|
export const writeFileIfChanged: any;
|
|
184
191
|
export const GENERATED_MARKER: any;
|
|
192
|
+
export const EDITABLE_GENERATED_MARKER: any;
|
|
185
193
|
export const hasGeneratedMarker: any;
|
|
186
194
|
export const stripGeneratedMarker: any;
|
|
187
195
|
export const isGeneratedMarkerTextCandidate: any;
|
|
@@ -200,6 +208,7 @@ declare module "@warpgogol/werkstatt-engine/kernel" {
|
|
|
200
208
|
export const parseNameOnlyOutput: any;
|
|
201
209
|
export const isExempt: any;
|
|
202
210
|
export const pipelineBudgetModule: any;
|
|
211
|
+
export const validatorInventoryModule: any;
|
|
203
212
|
export const changeImpactModule: any;
|
|
204
213
|
export const commandManifestModule: any;
|
|
205
214
|
export const gitmeshModule: any;
|
|
@@ -255,8 +264,7 @@ declare module "@warpgogol/werkstatt-engine/kernel/change-impact-module" {
|
|
|
255
264
|
}
|
|
256
265
|
|
|
257
266
|
declare module "@warpgogol/werkstatt-engine/kernel/cli" {
|
|
258
|
-
|
|
259
|
-
export = _;
|
|
267
|
+
export function main(...args: any[]): any;
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
declare module "@warpgogol/werkstatt-engine/kernel/commit-message" {
|
|
@@ -294,6 +302,7 @@ declare module "@warpgogol/werkstatt-engine/kernel/generated-marker" {
|
|
|
294
302
|
export function buildGeneratedHeader(...args: any[]): any;
|
|
295
303
|
export function isGeneratedMarkerTextCandidate(...args: any[]): any;
|
|
296
304
|
export const GENERATED_MARKER: any;
|
|
305
|
+
export const EDITABLE_GENERATED_MARKER: any;
|
|
297
306
|
export interface StripGeneratedMarkerResult {}
|
|
298
307
|
export interface GeneratedHeaderInput {}
|
|
299
308
|
}
|
|
@@ -340,6 +349,10 @@ declare module "@warpgogol/werkstatt-engine/kernel/pipeline-budgets" {
|
|
|
340
349
|
export interface PipelineBudgetGenerateResult {}
|
|
341
350
|
}
|
|
342
351
|
|
|
352
|
+
declare module "@warpgogol/werkstatt-engine/kernel/validator-inventory" {
|
|
353
|
+
export const validatorInventoryModule: any;
|
|
354
|
+
}
|
|
355
|
+
|
|
343
356
|
declare module "@warpgogol/werkstatt-engine/kernel/semantic" {
|
|
344
357
|
export const semanticModule: any;
|
|
345
358
|
}
|
|
@@ -354,6 +367,7 @@ declare module "@warpgogol/werkstatt-engine/kernel/runtime" {
|
|
|
354
367
|
export const listRegisteredKernelPipelines: any;
|
|
355
368
|
export const loadAppRuntime: any;
|
|
356
369
|
export const executeKernelCommand: any;
|
|
370
|
+
export const executeRegisteredCommand: any;
|
|
357
371
|
export const executeKernelPipeline: any;
|
|
358
372
|
export const buildSchedule: any;
|
|
359
373
|
export const executeScheduledSteps: any;
|
|
@@ -382,6 +396,8 @@ declare module "@warpgogol/werkstatt-engine/kernel/types" {
|
|
|
382
396
|
export interface KernelNextStep {}
|
|
383
397
|
export interface KernelCommandResult {}
|
|
384
398
|
export interface CheckResult {}
|
|
399
|
+
export interface GeneratedArtifactSpec {}
|
|
400
|
+
export interface GeneratorOwnershipEntry {}
|
|
385
401
|
export interface KernelRuntimeContext {}
|
|
386
402
|
export interface KernelCommandDefinition {}
|
|
387
403
|
export interface KernelPipelineStep {}
|
|
@@ -505,10 +521,13 @@ declare module "@warpgogol/werkstatt-engine/mission" {
|
|
|
505
521
|
export const MissionResumeData: any;
|
|
506
522
|
export const runMissionJournalShow: any;
|
|
507
523
|
export const MissionJournalShowData: any;
|
|
524
|
+
export const runMissionPreflight: any;
|
|
525
|
+
export const MissionPreflightInput: any;
|
|
526
|
+
export const PreflightCheckResult: any;
|
|
527
|
+
export const MissionPreflightData: any;
|
|
508
528
|
}
|
|
509
529
|
|
|
510
530
|
declare module "@warpgogol/werkstatt-engine/sternsystem" {
|
|
511
|
-
export function createSternsystemModule(...args: any[]): any;
|
|
512
531
|
export const runSternsystemRegister: any;
|
|
513
532
|
export const SternsystemRegisterData: any;
|
|
514
533
|
export const runSternsystemList: any;
|
|
@@ -525,6 +544,35 @@ declare module "@warpgogol/werkstatt-engine/sternsystem" {
|
|
|
525
544
|
export const SternsystemStatusData: any;
|
|
526
545
|
export const runSternsystemDiscover: any;
|
|
527
546
|
export const SternsystemDiscoverData: any;
|
|
547
|
+
export const runSternsystemPassportGenerate: any;
|
|
548
|
+
export const SternsystemPassportGenerateData: any;
|
|
549
|
+
export const runSternsystemPassportVerify: any;
|
|
550
|
+
export const SternsystemPassportVerifyData: any;
|
|
551
|
+
export const runSternsystemHandoverPrepare: any;
|
|
552
|
+
export const SternsystemHandoverPrepareData: any;
|
|
553
|
+
export const runSternsystemHandoverComplete: any;
|
|
554
|
+
export const SternsystemHandoverCompleteData: any;
|
|
555
|
+
export const runSternsystemHandoverCancel: any;
|
|
556
|
+
export const SternsystemHandoverCancelData: any;
|
|
557
|
+
export const buildPassportPayload: any;
|
|
558
|
+
export const signPassport: any;
|
|
559
|
+
export const verifyPassport: any;
|
|
560
|
+
export const derivePublicKey: any;
|
|
561
|
+
export const computePassportHash: any;
|
|
562
|
+
export const SitePassportV1: any;
|
|
563
|
+
export const SignedSitePassport: any;
|
|
564
|
+
export const computeAuthorizationHash: any;
|
|
565
|
+
export const signAuthorization: any;
|
|
566
|
+
export const verifyAuthorization: any;
|
|
567
|
+
export const resolveAuthorizationPath: any;
|
|
568
|
+
export const readAuthorization: any;
|
|
569
|
+
export const writeAuthorization: any;
|
|
570
|
+
export const removeAuthorization: any;
|
|
571
|
+
export const isAuthorizationExpired: any;
|
|
572
|
+
export const HandoverAuthorizationV1: any;
|
|
573
|
+
export const SignedHandoverAuthorization: any;
|
|
574
|
+
export const HandoverCompleteResult: any;
|
|
575
|
+
export const HandoverEventMetadata: any;
|
|
528
576
|
export const resolveCacheClonePath: any;
|
|
529
577
|
export const resolveWorkpiecePath: any;
|
|
530
578
|
export const readSystemConfig: any;
|
|
@@ -549,6 +597,9 @@ declare module "@warpgogol/werkstatt-engine/sternsystem" {
|
|
|
549
597
|
export const resolveMirrorPath: any;
|
|
550
598
|
export const MirrorResolution: any;
|
|
551
599
|
export const resolveMirrors: any;
|
|
600
|
+
export const readPassport: any;
|
|
601
|
+
export const writePassport: any;
|
|
602
|
+
export const resolvePassportPath: any;
|
|
552
603
|
}
|
|
553
604
|
|
|
554
605
|
declare module "@warpgogol/werkstatt-engine/bordbuch" {
|
|
@@ -622,6 +673,18 @@ declare module "@warpgogol/werkstatt-engine/release" {
|
|
|
622
673
|
export const runReleaseStateValidate: any;
|
|
623
674
|
export const ReleaseStateValidateData: any;
|
|
624
675
|
export const ReleaseStateCheck: any;
|
|
676
|
+
export const runBootSmokeCommand: any;
|
|
677
|
+
export const BootSmokeCommandData: any;
|
|
678
|
+
export const runBootSmoke: any;
|
|
679
|
+
export const BootSmokeResult: any;
|
|
680
|
+
export const BootSmokeRequestSpec: any;
|
|
681
|
+
export const BootSmokeRequestResult: any;
|
|
682
|
+
export const planBootSmokeRequests: any;
|
|
683
|
+
export const detectBootSmokeLanguages: any;
|
|
684
|
+
export const resolveWranglerConfig: any;
|
|
685
|
+
export const resolveLanguages: any;
|
|
686
|
+
export const WranglerResolution: any;
|
|
687
|
+
export const simulateBindings: any;
|
|
625
688
|
}
|
|
626
689
|
|
|
627
690
|
declare module "@warpgogol/werkstatt-engine/leitstand" {
|
|
@@ -727,15 +790,6 @@ declare module "@warpgogol/werkstatt-engine/handoff" {
|
|
|
727
790
|
export const hasPlatformScopeFiles: any;
|
|
728
791
|
export const extractTrailer: any;
|
|
729
792
|
export const hasTrailer: any;
|
|
730
|
-
export const createSternsystemModule: any;
|
|
731
|
-
export const runSternsystemRegister: any;
|
|
732
|
-
export const runSternsystemList: any;
|
|
733
|
-
export const runSternsystemValidate: any;
|
|
734
|
-
export const runSternsystemPin: any;
|
|
735
|
-
export const SternsystemRegisterData: any;
|
|
736
|
-
export const SternsystemListData: any;
|
|
737
|
-
export const SternsystemValidateData: any;
|
|
738
|
-
export const SternsystemPinData: any;
|
|
739
793
|
export const runWerkstattLockStatus: any;
|
|
740
794
|
export const WerkstattLockStatusData: any;
|
|
741
795
|
export const runWerkstattLockRecover: any;
|
|
@@ -815,6 +869,29 @@ declare module "@warpgogol/werkstatt-engine/leitstand-module" {
|
|
|
815
869
|
export function createLeitstandModule(...args: any[]): any;
|
|
816
870
|
}
|
|
817
871
|
|
|
872
|
+
declare module "@warpgogol/werkstatt-engine/fleet" {
|
|
873
|
+
export const runFleetSitesGenerate: any;
|
|
874
|
+
export const validateFleetSitesDrift: any;
|
|
875
|
+
export const FleetSiteRecord: any;
|
|
876
|
+
export const runFleetApply: any;
|
|
877
|
+
export const FleetApplyResult: any;
|
|
878
|
+
export const FleetApplyReportEntry: any;
|
|
879
|
+
export const registerOwnership: any;
|
|
880
|
+
export const verifyOwnership: any;
|
|
881
|
+
export const transferOwnership: any;
|
|
882
|
+
export const deriveInstanceId: any;
|
|
883
|
+
export const OwnershipError: any;
|
|
884
|
+
export const OwnershipClaim: any;
|
|
885
|
+
export const RegisterResult: any;
|
|
886
|
+
export const VerifyResult: any;
|
|
887
|
+
export const TransferResult: any;
|
|
888
|
+
export const createFleetModule: any;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
declare module "@warpgogol/werkstatt-engine/fleet-module" {
|
|
892
|
+
export function createFleetModule(...args: any[]): any;
|
|
893
|
+
}
|
|
894
|
+
|
|
818
895
|
declare module "@warpgogol/werkstatt-engine/subdomain-module" {
|
|
819
896
|
export function createSubdomainModule(...args: any[]): any;
|
|
820
897
|
}
|
|
@@ -1098,6 +1175,10 @@ declare module "@warpgogol/werkstatt-engine/testing/e2e" {
|
|
|
1098
1175
|
export = _;
|
|
1099
1176
|
}
|
|
1100
1177
|
|
|
1178
|
+
declare module "@warpgogol/werkstatt-engine/e2e" {
|
|
1179
|
+
export const runColdE2e: any;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1101
1182
|
declare module "@warpgogol/werkstatt-engine/component-runtime/reflection" {
|
|
1102
1183
|
export function toReflectedState(...args: any[]): any;
|
|
1103
1184
|
export function createCapabilityCatalog(...args: any[]): any;
|
package/src/types.ts
CHANGED
|
@@ -67,6 +67,10 @@ export interface ForgeCommandMetadata {
|
|
|
67
67
|
mutatesState?: boolean;
|
|
68
68
|
requiresNetwork?: boolean;
|
|
69
69
|
supportsAllSites?: boolean;
|
|
70
|
+
/** RFC-0963: validation contract this command belongs to (e.g. "content", "seo"). */
|
|
71
|
+
contract?: string;
|
|
72
|
+
/** RFC-0963: rule IDs this validator can emit. Empty for non-emitting validators. */
|
|
73
|
+
rules?: string[];
|
|
70
74
|
timeoutMs?: number;
|
|
71
75
|
expectedDurationMs?: number;
|
|
72
76
|
longRunning?: boolean;
|
|
@@ -10,14 +10,22 @@ source (dependency inversion).</purpose>
|
|
|
10
10
|
<CHANGE_SUMMARY>
|
|
11
11
|
<item>Moved from @warpgogol/site-kernel/generated-marker to forge as canonical source.</item>
|
|
12
12
|
<item>Added optional commandPrefix to GeneratedHeaderInput — defaults to "forge" for autonomous mode; site-kernel passes "pnpm exec werkstatt run".</item>
|
|
13
|
+
<item>Added editable flag to GeneratedHeaderInput — when true, emits a permissive marker and advisory that encourages agents to edit the file. AGENTS.md files use editable: true; other generated files keep the restrictive marker.</item>
|
|
13
14
|
</CHANGE_SUMMARY>
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
export const GENERATED_MARKER =
|
|
17
18
|
"GENERATED. Do not change this line unless the file contains project specific changes.";
|
|
18
19
|
|
|
20
|
+
export const EDITABLE_GENERATED_MARKER =
|
|
21
|
+
"GENERATED. Safe to edit — project-specific changes are expected and encouraged.";
|
|
22
|
+
|
|
23
|
+
const DO_NOT_EDIT_LINE = "DO NOT EDIT THIS FILE. Changes are overwritten on the next build.";
|
|
24
|
+
const SAFE_TO_EDIT_LINE =
|
|
25
|
+
"This file is generated but editable — your changes are preserved on regeneration.";
|
|
26
|
+
|
|
19
27
|
export function hasGeneratedMarker(content: string): boolean {
|
|
20
|
-
return content.includes(GENERATED_MARKER);
|
|
28
|
+
return content.includes(GENERATED_MARKER) || content.includes(EDITABLE_GENERATED_MARKER);
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
export interface StripGeneratedMarkerResult {
|
|
@@ -29,10 +37,7 @@ function escapeRegex(str: string): string {
|
|
|
29
37
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
30
38
|
}
|
|
31
39
|
|
|
32
|
-
const DO_NOT_EDIT_LINE = "DO NOT EDIT THIS FILE. Changes are overwritten on the next build.";
|
|
33
|
-
|
|
34
40
|
export function stripGeneratedMarker(content: string): StripGeneratedMarkerResult {
|
|
35
|
-
const raw = GENERATED_MARKER;
|
|
36
41
|
let result = content;
|
|
37
42
|
let changed = false;
|
|
38
43
|
|
|
@@ -46,38 +51,43 @@ export function stripGeneratedMarker(content: string): StripGeneratedMarkerResul
|
|
|
46
51
|
}
|
|
47
52
|
};
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
])
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
for (const [raw, advisory] of [
|
|
55
|
+
[GENERATED_MARKER, DO_NOT_EDIT_LINE],
|
|
56
|
+
[EDITABLE_GENERATED_MARKER, SAFE_TO_EDIT_LINE],
|
|
57
|
+
] as const) {
|
|
58
|
+
applyAll([
|
|
59
|
+
new RegExp(`<!--[ \\t]*\\n[\\s\\S]*?${escapeRegex(raw)}[\\s\\S]*?-->\\n?`, "g"),
|
|
60
|
+
new RegExp(`/\\*[ \\t]*\\n[\\s\\S]*?${escapeRegex(raw)}[\\s\\S]*?\\*/\\n?`, "g"),
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
for (const token of ["//", "#"]) {
|
|
64
|
+
const t = escapeRegex(token);
|
|
65
|
+
const advisoryLine = (suffix: string): string => `(?:[ \\t]*${t}[ \\t]*${suffix}\\n)?`;
|
|
66
|
+
const pattern = new RegExp(
|
|
67
|
+
`^[ \\t]*${t}[ \\t]*${escapeRegex(raw)}\\n` +
|
|
68
|
+
advisoryLine(escapeRegex(advisory)) +
|
|
69
|
+
advisoryLine("Owner command:.*") +
|
|
70
|
+
advisoryLine("Edit instead:.*") +
|
|
71
|
+
advisoryLine("Regenerate:.*"),
|
|
72
|
+
"gm",
|
|
73
|
+
);
|
|
74
|
+
applyAll([pattern]);
|
|
75
|
+
}
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
applyAll([
|
|
78
|
+
new RegExp(`^\\s*<!--\\s*${escapeRegex(raw)}\\s*-->\\n?`, "gm"),
|
|
79
|
+
new RegExp(`^\\s*//\\s*${escapeRegex(raw)}\\n?`, "gm"),
|
|
80
|
+
new RegExp(`^\\s*/\\*\\s*${escapeRegex(raw)}\\s*\\*/\\n?`, "gm"),
|
|
81
|
+
new RegExp(`^\\s*#\\s*${escapeRegex(raw)}\\n?`, "gm"),
|
|
82
|
+
new RegExp(`^\\s*${escapeRegex(raw)}\\n?`, "gm"),
|
|
83
|
+
]);
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
if (result.includes(raw)) {
|
|
86
|
+
const next = result.split(raw).join("");
|
|
87
|
+
if (next !== result) {
|
|
88
|
+
changed = true;
|
|
89
|
+
result = next;
|
|
90
|
+
}
|
|
81
91
|
}
|
|
82
92
|
}
|
|
83
93
|
|
|
@@ -94,6 +104,12 @@ export interface GeneratedHeaderInput {
|
|
|
94
104
|
* Site-kernel callers pass "pnpm exec werkstatt run" for Warpgogol context.
|
|
95
105
|
*/
|
|
96
106
|
commandPrefix?: string;
|
|
107
|
+
/**
|
|
108
|
+
* When true, emits a permissive marker and advisory lines that encourage agents
|
|
109
|
+
* to edit the file. Used for AGENTS.md files that are generated but should be
|
|
110
|
+
* freely editable by agents working on the project. Defaults to false.
|
|
111
|
+
*/
|
|
112
|
+
editable?: boolean;
|
|
97
113
|
}
|
|
98
114
|
|
|
99
115
|
type GeneratedHeaderCommentStyle = "line-slash" | "line-hash" | "block-html" | "block-css";
|
|
@@ -119,6 +135,9 @@ function commentStyleForPath(filePath: string): GeneratedHeaderCommentStyle {
|
|
|
119
135
|
export function buildGeneratedHeader(input: GeneratedHeaderInput): string {
|
|
120
136
|
const style = commentStyleForPath(input.filePath);
|
|
121
137
|
const prefix = input.commandPrefix ?? "forge";
|
|
138
|
+
const editable = input.editable ?? false;
|
|
139
|
+
const marker = editable ? EDITABLE_GENERATED_MARKER : GENERATED_MARKER;
|
|
140
|
+
const advisoryLine = editable ? SAFE_TO_EDIT_LINE : DO_NOT_EDIT_LINE;
|
|
122
141
|
const regenerateCommand = input.site
|
|
123
142
|
? `${prefix} ${input.ownerCommand} --site ${input.site}`
|
|
124
143
|
: `${prefix} ${input.ownerCommand}`;
|
|
@@ -127,8 +146,8 @@ export function buildGeneratedHeader(input: GeneratedHeaderInput): string {
|
|
|
127
146
|
: `Edit instead: the ${input.ownerCommand} generator source (not this file).`;
|
|
128
147
|
|
|
129
148
|
const lines = [
|
|
130
|
-
|
|
131
|
-
|
|
149
|
+
marker,
|
|
150
|
+
advisoryLine,
|
|
132
151
|
`Owner command: ${input.ownerCommand}`,
|
|
133
152
|
editInstead,
|
|
134
153
|
`Regenerate: ${regenerateCommand}`,
|
package/src/utils/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { writeFileAtomic, type WriteFileAtomicOptions } from "./fs-atomic.ts";
|
|
|
15
15
|
export { writeFileIfChanged } from "./fs-idempotent.ts";
|
|
16
16
|
export {
|
|
17
17
|
GENERATED_MARKER,
|
|
18
|
+
EDITABLE_GENERATED_MARKER,
|
|
18
19
|
hasGeneratedMarker,
|
|
19
20
|
stripGeneratedMarker,
|
|
20
21
|
buildGeneratedHeader,
|