@warpgogol/forge 5.2.0 → 5.2.4

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.
Files changed (120) hide show
  1. package/AGENTS.md +4 -1
  2. package/dist/os/adr/adr.module.js +1 -1
  3. package/dist/os/adr/adr.module.js.map +1 -1
  4. package/dist/os/compass/compass.module.js +2 -2
  5. package/dist/os/compass/compass.module.js.map +1 -1
  6. package/dist/os/compass/handlers/compass-audit-handler.d.ts +3 -0
  7. package/dist/os/compass/handlers/compass-audit-handler.d.ts.map +1 -1
  8. package/dist/os/compass/handlers/compass-audit-handler.js +87 -8
  9. package/dist/os/compass/handlers/compass-audit-handler.js.map +1 -1
  10. package/dist/os/core/core.module.d.ts.map +1 -1
  11. package/dist/os/core/core.module.js +15 -1
  12. package/dist/os/core/core.module.js.map +1 -1
  13. package/dist/os/mission/handlers/archive.d.ts.map +1 -1
  14. package/dist/os/mission/handlers/archive.js +22 -6
  15. package/dist/os/mission/handlers/archive.js.map +1 -1
  16. package/dist/os/program/lease.js +1 -1
  17. package/dist/os/program/lease.js.map +1 -1
  18. package/dist/os/queue/queue.module.js +1 -1
  19. package/dist/os/queue/queue.module.js.map +1 -1
  20. package/dist/os/rfc/handlers/list-create.d.ts.map +1 -1
  21. package/dist/os/rfc/handlers/list-create.js +79 -32
  22. package/dist/os/rfc/handlers/list-create.js.map +1 -1
  23. package/dist/os/rfc/rfc.module.js +1 -1
  24. package/dist/os/rfc/rfc.module.js.map +1 -1
  25. package/dist/os/spec/spec.module.d.ts.map +1 -1
  26. package/dist/os/spec/spec.module.js +2 -0
  27. package/dist/os/spec/spec.module.js.map +1 -1
  28. package/dist/src/config/forge-config.d.ts +9 -0
  29. package/dist/src/config/forge-config.d.ts.map +1 -1
  30. package/dist/src/config/forge-config.js +7 -0
  31. package/dist/src/config/forge-config.js.map +1 -1
  32. package/dist/src/knowledge/index.d.ts +1 -0
  33. package/dist/src/knowledge/index.d.ts.map +1 -1
  34. package/dist/src/knowledge/index.js +1 -0
  35. package/dist/src/knowledge/index.js.map +1 -1
  36. package/dist/src/knowledge/sync.d.ts +24 -0
  37. package/dist/src/knowledge/sync.d.ts.map +1 -0
  38. package/dist/src/knowledge/sync.js +127 -0
  39. package/dist/src/knowledge/sync.js.map +1 -0
  40. package/dist/src/onboarding/agents-generate.d.ts +2 -0
  41. package/dist/src/onboarding/agents-generate.d.ts.map +1 -1
  42. package/dist/src/onboarding/agents-generate.js +14 -18
  43. package/dist/src/onboarding/agents-generate.js.map +1 -1
  44. package/dist/src/onboarding/doctor.d.ts +6 -0
  45. package/dist/src/onboarding/doctor.d.ts.map +1 -1
  46. package/dist/src/onboarding/doctor.js +124 -14
  47. package/dist/src/onboarding/doctor.js.map +1 -1
  48. package/dist/src/onboarding/init.d.ts.map +1 -1
  49. package/dist/src/onboarding/init.js +15 -8
  50. package/dist/src/onboarding/init.js.map +1 -1
  51. package/dist/src/onboarding/memory-compact.d.ts +22 -0
  52. package/dist/src/onboarding/memory-compact.d.ts.map +1 -0
  53. package/dist/src/onboarding/memory-compact.js +147 -0
  54. package/dist/src/onboarding/memory-compact.js.map +1 -0
  55. package/dist/src/onboarding/memory-scaffold.d.ts +1 -0
  56. package/dist/src/onboarding/memory-scaffold.d.ts.map +1 -1
  57. package/dist/src/onboarding/memory-scaffold.js +1 -1
  58. package/dist/src/onboarding/memory-scaffold.js.map +1 -1
  59. package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -1
  60. package/dist/src/onboarding/nested-agents-generate.js +1 -1
  61. package/dist/src/onboarding/nested-agents-generate.js.map +1 -1
  62. package/dist/src/onboarding/upgrade.d.ts.map +1 -1
  63. package/dist/src/onboarding/upgrade.js +42 -13
  64. package/dist/src/onboarding/upgrade.js.map +1 -1
  65. package/dist/src/onboarding/workspace-discovery.d.ts +1 -1
  66. package/dist/src/onboarding/workspace-discovery.d.ts.map +1 -1
  67. package/dist/src/onboarding/workspace-discovery.js +14 -2
  68. package/dist/src/onboarding/workspace-discovery.js.map +1 -1
  69. package/dist/src/types.d.ts +1 -2
  70. package/dist/src/types.d.ts.map +1 -1
  71. package/dist/src/types.js +1 -0
  72. package/dist/src/types.js.map +1 -1
  73. package/dist/src/utils/fs-atomic.js +1 -1
  74. package/dist/src/utils/fs-atomic.js.map +1 -1
  75. package/docs/reference/forge-yaml.md +12 -0
  76. package/os/adr/adr.module.ts +1 -1
  77. package/os/compass/compass.module.ts +2 -2
  78. package/os/compass/handlers/compass-audit-handler.ts +116 -8
  79. package/os/compass/handlers/tests/compass-audit-plan.test.ts +98 -0
  80. package/os/compass/handlers/tests/compass-audit-record.test.ts +105 -0
  81. package/os/compass/handlers/tests/compass-audit-validate.test.ts +133 -0
  82. package/os/compass/handlers/tests/compass-ledger-scope.test.ts +124 -0
  83. package/os/core/core.module.ts +16 -1
  84. package/os/mission/handlers/archive.test.ts +49 -0
  85. package/os/mission/handlers/archive.ts +21 -6
  86. package/os/program/lease.ts +1 -1
  87. package/os/queue/queue.module.ts +1 -1
  88. package/os/rfc/handlers/list-create.ts +83 -35
  89. package/os/rfc/handlers/validate-rules.test.ts +50 -0
  90. package/os/rfc/rfc-create-concurrent.test.ts +118 -0
  91. package/os/rfc/rfc-create-hint.test.ts +90 -0
  92. package/os/rfc/rfc-read-only-no-side-effects.test.ts +83 -0
  93. package/os/rfc/rfc.module.ts +1 -1
  94. package/os/spec/spec.module.ts +2 -0
  95. package/package.json +1 -1
  96. package/profiles/forge-shell.yaml +3 -0
  97. package/profiles/godot-game.yaml +3 -0
  98. package/profiles/phaser-game.yaml +3 -0
  99. package/profiles/site-workshop.yaml +567 -564
  100. package/src/config/forge-config.ts +11 -0
  101. package/src/knowledge/__tests__/sync.test.ts +227 -0
  102. package/src/knowledge/index.ts +7 -0
  103. package/src/knowledge/sync.ts +161 -0
  104. package/src/onboarding/agents-generate.ts +18 -18
  105. package/src/onboarding/doctor.ts +141 -15
  106. package/src/onboarding/init.ts +15 -8
  107. package/src/onboarding/memory-compact.ts +188 -0
  108. package/src/onboarding/memory-scaffold.ts +1 -1
  109. package/src/onboarding/nested-agents-generate.ts +5 -1
  110. package/src/onboarding/upgrade.ts +47 -16
  111. package/src/onboarding/workspace-discovery.ts +15 -1
  112. package/src/tests/agents-generate.test.ts +26 -3
  113. package/src/tests/doctor-dist-freshness.test.ts +110 -0
  114. package/src/tests/doctor-fix.test.ts +145 -0
  115. package/src/tests/doctor-nested-agents.test.ts +108 -0
  116. package/src/tests/memory-compact.test.ts +143 -0
  117. package/src/tests/upgrade.test.ts +53 -0
  118. package/src/tests/workspace-discovery.test.ts +28 -0
  119. package/src/types.ts +2 -2
  120. package/src/utils/fs-atomic.ts +1 -1
@@ -0,0 +1,90 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>
4
+ RFC-1139 AC-1: the next-steps hint printed by rfc.create must be an
5
+ executable command — correct binary (werkstatt, not forge), correct
6
+ subcommand (run), and correct flags (--id, not --file).
7
+ </purpose>
8
+ </MODULE_CONTRACT>
9
+ */
10
+
11
+ import { describe, expect, it, beforeEach, afterEach } from "vitest";
12
+ import * as fs from "node:fs/promises";
13
+ import * as os from "node:os";
14
+ import * as path from "node:path";
15
+ import { runRfcCreate } from "./handlers/list-create.ts";
16
+ import type { ForgeCommandInput, ForgeRuntimeContext } from "../../src/types.ts";
17
+
18
+ const RFC_DIR = "docs/rfcs";
19
+
20
+ const TEMPLATE = `---
21
+ id: RFC-0000
22
+ title: "Template"
23
+ status: draft
24
+ kind: command
25
+ scope: workspace
26
+ createdAt: YYYY-MM-DD
27
+ updatedAt: YYYY-MM-DD
28
+ satisfies: []
29
+ ---
30
+
31
+ # RFC-0000: Template
32
+ `;
33
+
34
+ function makeContext(root: string): ForgeRuntimeContext {
35
+ return {
36
+ workspaceRoot: root,
37
+ logger: {
38
+ info() {},
39
+ warn() {},
40
+ error() {},
41
+ success() {},
42
+ section() {},
43
+ getEvents: () => [],
44
+ },
45
+ outputFormat: "json",
46
+ dryRun: false,
47
+ } as unknown as ForgeRuntimeContext;
48
+ }
49
+
50
+ function makeInput(flags: Record<string, unknown>): ForgeCommandInput {
51
+ return { argv: [], flags } as unknown as ForgeCommandInput;
52
+ }
53
+
54
+ describe("rfc.create — RFC-1139 next-steps hint accuracy", () => {
55
+ let root: string;
56
+ let rfcDir: string;
57
+
58
+ beforeEach(async () => {
59
+ root = await fs.mkdtemp(path.join(os.tmpdir(), "rfc-hint-"));
60
+ rfcDir = path.join(root, RFC_DIR);
61
+ await fs.mkdir(rfcDir, { recursive: true });
62
+ await fs.writeFile(path.join(rfcDir, "rfc-0000-template.md"), TEMPLATE, "utf8");
63
+ });
64
+
65
+ afterEach(async () => {
66
+ await fs.rm(root, { recursive: true, force: true });
67
+ });
68
+
69
+ it("AC-1: next-steps hint is an executable werkstatt command with --id", async () => {
70
+ const result = await runRfcCreate(
71
+ makeInput({ title: "Hint Test", kind: "command", scope: "workspace" }),
72
+ makeContext(root),
73
+ );
74
+
75
+ const steps = result.nextSteps ?? [];
76
+ expect(steps.length).toBeGreaterThan(0);
77
+
78
+ for (const step of steps) {
79
+ const match = step.action.match(/pnpm exec (\S+) run ([\w.]+)/);
80
+ expect(match, `hint "${step.action}" must contain a runnable command`).not.toBeNull();
81
+ const [, binary, command] = match!;
82
+ // forge has no `run` subcommand — werkstatt does
83
+ expect(binary).toBe("werkstatt");
84
+ expect(command).toBe("rfc.validate");
85
+ // rfc.validate takes --id, not --file
86
+ expect(step.action).toContain("--id RFC-");
87
+ expect(step.action).not.toContain("--file");
88
+ }
89
+ });
90
+ });
@@ -0,0 +1,83 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>
4
+ RFC-1139 AC-4: read-only commands must not write to the working tree —
5
+ no ambient regeneration of tracked generated files as a side effect.
6
+ Guards against future read-path writers.
7
+ </purpose>
8
+ </MODULE_CONTRACT>
9
+ */
10
+
11
+ import { describe, expect, it, beforeEach, afterEach } from "vitest";
12
+ import * as fs from "node:fs/promises";
13
+ import * as os from "node:os";
14
+ import * as path from "node:path";
15
+ import { runRfcList } from "./handlers/list-create.ts";
16
+ import type { ForgeCommandInput, ForgeRuntimeContext } from "../../src/types.ts";
17
+
18
+ function makeContext(root: string): ForgeRuntimeContext {
19
+ return {
20
+ workspaceRoot: root,
21
+ logger: {
22
+ info() {},
23
+ warn() {},
24
+ error() {},
25
+ success() {},
26
+ section() {},
27
+ getEvents: () => [],
28
+ },
29
+ outputFormat: "json",
30
+ dryRun: false,
31
+ } as unknown as ForgeRuntimeContext;
32
+ }
33
+
34
+ function makeInput(flags: Record<string, unknown> = {}): ForgeCommandInput {
35
+ return { argv: [], flags } as unknown as ForgeCommandInput;
36
+ }
37
+
38
+ async function snapshotTree(root: string): Promise<Map<string, string>> {
39
+ const snapshot = new Map<string, string>();
40
+ async function walk(dir: string): Promise<void> {
41
+ for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
42
+ const full = path.join(dir, entry.name);
43
+ if (entry.isDirectory()) {
44
+ await walk(full);
45
+ } else if (entry.isFile()) {
46
+ snapshot.set(path.relative(root, full), await fs.readFile(full, "utf8"));
47
+ }
48
+ }
49
+ }
50
+ await walk(root);
51
+ return snapshot;
52
+ }
53
+
54
+ describe("read-only commands — RFC-1139 no side-effect writes", () => {
55
+ let root: string;
56
+
57
+ beforeEach(async () => {
58
+ root = await fs.mkdtemp(path.join(os.tmpdir(), "rfc-readonly-"));
59
+ const rfcDir = path.join(root, "docs", "rfcs");
60
+ await fs.mkdir(rfcDir, { recursive: true });
61
+ await fs.writeFile(
62
+ path.join(rfcDir, "rfc-0001-test.md"),
63
+ `---\nid: RFC-0001\ntitle: "Test"\nstatus: draft\n---\n\n# RFC-0001\n`,
64
+ "utf8",
65
+ );
66
+ });
67
+
68
+ afterEach(async () => {
69
+ await fs.rm(root, { recursive: true, force: true });
70
+ });
71
+
72
+ it("AC-4: rfc.list leaves the working tree byte-identical", async () => {
73
+ const before = await snapshotTree(root);
74
+ const result = await runRfcList(makeInput(), makeContext(root));
75
+ expect(result.data?.status).toBe("ok");
76
+ const after = await snapshotTree(root);
77
+
78
+ expect([...after.keys()].sort()).toEqual([...before.keys()].sort());
79
+ for (const [file, content] of before) {
80
+ expect(after.get(file), `${file} modified by read-only command`).toBe(content);
81
+ }
82
+ });
83
+ });
@@ -396,7 +396,7 @@ const {
396
396
  "RFC-0476: the exclusive atomic path for accepted → implemented transitions. " +
397
397
  "Verifies preconditions (accepted status, checked+evidenced criteria, clean tree, " +
398
398
  "reachable RFC-referencing commit, passing probe evidence) then atomically sets " +
399
- "status: implemented, implementedAt, and updatedAt. Use --dry-run to preview.",
399
+ "status: implemented, implementedAt, and updatedAt. Use --dry-run to preview. Required flags: `--id`.",
400
400
  scope: "workspace",
401
401
  mutatesState: true,
402
402
  writes: ["docs/rfcs/**/*.md"],
@@ -103,6 +103,7 @@ const { runSpecValidate } = await import("./spec-validate.ts");
103
103
  "List all living feature specs in docs/specs/live/. " +
104
104
  "Returns domain, title, lastMergedRfc, updatedAt, and historyCount for each spec.",
105
105
  scope: "workspace",
106
+ flags: {},
106
107
  reads: ["docs/specs/live/*.md"],
107
108
  execute: runSpecLiveList,
108
109
  },
@@ -128,6 +129,7 @@ const { runSpecValidate } = await import("./spec-validate.ts");
128
129
  "V-LS-03 (lastMergedRfc is archived), V-LS-04 (history entries are archived), " +
129
130
  "V-LS-05 (no duplicate domains).",
130
131
  scope: "workspace",
132
+ flags: {},
131
133
  reads: ["docs/specs/live/*.md", "docs/rfcs/**/*.md"],
132
134
  execute: runSpecLiveValidate,
133
135
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warpgogol/forge",
3
- "version": "5.2.0",
3
+ "version": "5.2.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -70,6 +70,9 @@ workspace:
70
70
  - name: Install dependencies
71
71
  run: pnpm install --frozen-lockfile
72
72
 
73
+ - name: Forge doctor
74
+ run: pnpm exec forge run forge.doctor
75
+
73
76
  - name: Format check
74
77
  run: pnpm format:check
75
78
 
@@ -292,6 +292,9 @@ workspace:
292
292
  - name: Install dependencies
293
293
  run: pnpm install --frozen-lockfile
294
294
 
295
+ - name: Forge doctor
296
+ run: pnpm exec forge run forge.doctor
297
+
295
298
  - name: Format check
296
299
  run: pnpm format:check
297
300
 
@@ -118,6 +118,9 @@ workspace:
118
118
  - name: Install dependencies
119
119
  run: pnpm install --frozen-lockfile
120
120
 
121
+ - name: Forge doctor
122
+ run: pnpm exec forge run forge.doctor
123
+
121
124
  - name: Format check
122
125
  run: pnpm format:check
123
126