forgeos 0.1.0-alpha.21 → 0.1.0-alpha.22

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 (49) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +17 -2
  3. package/adapters/java/target/forge-java-adapter-0.1.0-alpha.11.jar +0 -0
  4. package/adapters/java-spring-boot-starter/target/forge-java-spring-boot-starter-0.1.0-alpha.11.jar +0 -0
  5. package/docs/changelog.md +13 -0
  6. package/examples/java-billing/target/java-billing-0.1.0-alpha.11-all.jar +0 -0
  7. package/examples/java-billing/target/java-billing-0.1.0-alpha.11.jar +0 -0
  8. package/package.json +1 -1
  9. package/src/forge/_generated/releaseManifest.json +1 -1
  10. package/src/forge/_generated/releaseManifest.ts +3 -3
  11. package/src/forge/agent-adapters/types.ts +3 -0
  12. package/src/forge/agent-memory/bridge.ts +12 -0
  13. package/src/forge/agent-memory/context-pack.ts +106 -8
  14. package/src/forge/agent-memory/types.ts +4 -1
  15. package/src/forge/cli/commands.ts +47 -0
  16. package/src/forge/cli/main.ts +4 -0
  17. package/src/forge/cli/new.ts +3 -1
  18. package/src/forge/cli/parse.ts +64 -11
  19. package/src/forge/cli/studio.ts +54 -0
  20. package/src/forge/cli/verify.ts +2 -0
  21. package/src/forge/compiler/frontend-graph/build.ts +58 -2
  22. package/src/forge/delta/index.ts +12 -0
  23. package/src/forge/delta/recorder.ts +60 -0
  24. package/src/forge/delta/status.ts +639 -2
  25. package/src/forge/delta/store.ts +204 -5
  26. package/src/forge/delta/timeline.ts +75 -1
  27. package/src/forge/version.ts +1 -1
  28. package/templates/nuxt-web/.vscode/settings.json +14 -0
  29. package/templates/nuxt-web/README.md +30 -0
  30. package/templates/nuxt-web/forge.config.ts +3 -0
  31. package/templates/nuxt-web/package.json +33 -0
  32. package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
  33. package/templates/nuxt-web/src/commands/createNote.ts +26 -0
  34. package/templates/nuxt-web/src/forge/schema.ts +12 -0
  35. package/templates/nuxt-web/src/policies.ts +6 -0
  36. package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
  37. package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
  38. package/templates/nuxt-web/tsconfig.json +17 -0
  39. package/templates/nuxt-web/web/app.vue +67 -0
  40. package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
  41. package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
  42. package/templates/nuxt-web/web/composables/forge.ts +13 -0
  43. package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
  44. package/templates/nuxt-web/web/nuxt.config.ts +11 -0
  45. package/templates/nuxt-web/web/package.json +17 -0
  46. package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
  47. package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
  48. package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
  49. package/templates/nuxt-web/web/tsconfig.json +3 -0
package/AGENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- // @forge-generated generator=0.1.0-alpha.21 input=e2f2a360a9fd4118a2d21dd1353365628fe3927c40f9f9ce870f448f3e221f90 content=0d493cf0e41b71cb652d5e0e1b0c1f83d2a1281b748321f0b00f0773ba93074e
1
+ // @forge-generated generator=0.1.0-alpha.22 input=d8837238db9cf8868eceae3e3cc5a7d9bb46a0955c55f0143caa802597d5a3a5 content=0d493cf0e41b71cb652d5e0e1b0c1f83d2a1281b748321f0b00f0773ba93074e
2
2
  # AGENTS.md
3
3
 
4
4
  <!-- forge-generated:start -->
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # forgeos
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.1.0-alpha.22
6
+
7
+ ### Patch Changes
8
+
9
+ - Improve the post-alpha.21 agent workflow without adding new MCP tools.
10
+
11
+ - Add `forge agent context` scopes for entry, change, proof, and handoff context packs.
12
+ - Add DeltaDB verbose health details for queue redaction, operation age, semantic projection state, and overhead posture.
13
+ - Add `forge delta compact`, `forge delta prune`, and redacted `forge delta export` for local Delta maintenance and support bundles.
14
+ - Add `forge doctor delta` for recorder writability, queue drain, redaction, and gitignore checks.
15
+ - Add Semantic Timeline summary data for stale proofs and causal chains.
16
+ - Record CAIR snapshot/query/action activity as Delta timeline events without adding new MCP tools.
17
+ - Add a Studio snapshot handoff block and a dedicated CAIR Protocol documentation page.
18
+ - Add an official `nuxt-web` template with a Forge notes backend, client/server Nuxt plugins, a `useNotes` composable, a Nitro runtime-config route, and generated Vue composables.
19
+
3
20
  ## 0.1.0-alpha.21
4
21
 
5
22
  ### Patch Changes
@@ -23,8 +40,6 @@
23
40
  - Preserve empty stdio command arguments, diagnose malformed command strings, and support structured `service.commandArgs` in external manifests.
24
41
  - Include the basic example client demo in typecheck coverage.
25
42
 
26
- ## Unreleased
27
-
28
43
  ## 0.1.0-alpha.19
29
44
 
30
45
  Alpha hardening:
package/docs/changelog.md CHANGED
@@ -6,6 +6,19 @@ The canonical source file in the repository is `CHANGELOG.md`.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.1.0-alpha.22
10
+
11
+ - Added focused post-alpha.21 workflow improvements without expanding MCP tools:
12
+ scoped Agent Memory context packs, DeltaDB verbose health details, Semantic
13
+ Timeline stale-proof/causal summaries, Studio snapshot handoff metadata,
14
+ local Delta maintenance commands (`compact`, `prune`, redacted `export`),
15
+ `forge doctor delta`, CAIR timeline events, and a dedicated CAIR Protocol
16
+ documentation page.
17
+ - Added an official `nuxt-web` template: a Forge notes backend plus Nuxt app
18
+ using client/server Forge plugins, `web/composables/useNotes.ts`, generated
19
+ Vue composables, a Nitro runtime-config route, and
20
+ `NUXT_PUBLIC_FORGE_URL`.
21
+
9
22
  ## 0.1.0-alpha.21
10
23
 
11
24
  Alpha.21 hardens external-agent privacy and brownfield import polish:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgeos",
3
- "version": "0.1.0-alpha.21",
3
+ "version": "0.1.0-alpha.22",
4
4
  "description": "Agent-native application framework and compiler for building Forge apps without a mandatory dashboard.",
5
5
  "type": "module",
6
6
  "files": [
@@ -1 +1 @@
1
- {"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.21","releaseId":"forgeos@0.1.0-alpha.21+unknown","schemaVersion":"0.1.0"}
1
+ {"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.22","releaseId":"forgeos@0.1.0-alpha.22+unknown","schemaVersion":"0.1.0"}
@@ -1,4 +1,4 @@
1
- // @forge-generated generator=0.1.0-alpha.21 input=e2f2a360a9fd4118a2d21dd1353365628fe3927c40f9f9ce870f448f3e221f90 content=dcf914c56ed71bee812d0015d689e1dbf4e952b17ad03cd835f396f5fc8fee1a
1
+ // @forge-generated generator=0.1.0-alpha.22 input=d8837238db9cf8868eceae3e3cc5a7d9bb46a0955c55f0143caa802597d5a3a5 content=bb05ee3635ddeea2d84168f8bf728e07f82e896061e8494b17f56dea26000770
2
2
  export const releaseManifest = {
3
3
  "defaultProvider": "local",
4
4
  "diagnostics": [],
@@ -19,7 +19,7 @@ export const releaseManifest = {
19
19
  "custom"
20
20
  ],
21
21
  "packageName": "forgeos",
22
- "packageVersion": "0.1.0-alpha.21",
23
- "releaseId": "forgeos@0.1.0-alpha.21+unknown",
22
+ "packageVersion": "0.1.0-alpha.22",
23
+ "releaseId": "forgeos@0.1.0-alpha.22+unknown",
24
24
  "schemaVersion": "0.1.0"
25
25
  } as const;
@@ -34,6 +34,9 @@ export interface AgentCommandOptions {
34
34
  hookAction?: "smoke" | "status" | string;
35
35
  input?: unknown;
36
36
  entry?: string;
37
+ change?: string;
38
+ proof?: string;
39
+ handoff?: boolean;
37
40
  current?: boolean;
38
41
  limit?: number;
39
42
  watch?: boolean;
@@ -29,6 +29,9 @@ export interface AgentMemoryCommandOptions {
29
29
  eventName?: string;
30
30
  input?: unknown;
31
31
  entry?: string;
32
+ change?: string;
33
+ proof?: string;
34
+ handoff?: boolean;
32
35
  current?: boolean;
33
36
  dryRun?: boolean;
34
37
  force?: boolean;
@@ -308,6 +311,9 @@ export async function runAgentMemoryCommand(options: AgentMemoryCommandOptions):
308
311
  return await buildAgentMemoryContext({
309
312
  workspaceRoot: options.workspaceRoot,
310
313
  entry: options.entry,
314
+ change: options.change,
315
+ proof: options.proof,
316
+ handoff: options.handoff,
311
317
  limit: options.limit,
312
318
  });
313
319
  } catch (error) {
@@ -1052,6 +1058,12 @@ function formatAgentMemoryContextHuman(result: AgentMemoryContextPack): string {
1052
1058
  lines.push(` - ${question}`);
1053
1059
  }
1054
1060
  }
1061
+ if (result.recommendedCommands.length > 0) {
1062
+ lines.push("", "Next:");
1063
+ for (const command of result.recommendedCommands.slice(0, 6)) {
1064
+ lines.push(` ${command}`);
1065
+ }
1066
+ }
1055
1067
  return `${lines.join("\n")}\n`;
1056
1068
  }
1057
1069
 
@@ -4,14 +4,26 @@ import type { AgentMemoryContextEvent, AgentMemoryContextPack, AgentMemoryEventR
4
4
  export async function buildAgentMemoryContext(input: {
5
5
  workspaceRoot: string;
6
6
  entry?: string;
7
+ change?: string;
8
+ proof?: string;
9
+ handoff?: boolean;
7
10
  limit?: number;
8
11
  }): Promise<AgentMemoryContextPack> {
9
12
  const store = await DeltaStore.open(input.workspaceRoot, { access: "read" });
10
13
  try {
11
- const target = input.entry;
12
- const events = await store.listAgentMemoryEvents({ target, limit: input.limit ?? 50 });
13
- const timeline = target ? await store.semanticTimeline({ target, limit: input.limit ?? 50 }) : undefined;
14
- const current = target ? timeline?.currentState ?? {} : await currentSessionState(store);
14
+ const scope = contextScope(input);
15
+ const target = contextTarget(input, scope);
16
+ const currentSession = await store.currentWorkSession();
17
+ const sessionId = scope === "change" && (input.change === "current" || !input.change)
18
+ ? currentSession?.id
19
+ : undefined;
20
+ const events = await store.listAgentMemoryEvents({ target: eventTarget(input, scope), limit: input.limit ?? 50 });
21
+ const timeline = target || sessionId
22
+ ? await store.semanticTimeline({ target, workSessionId: sessionId, limit: input.limit ?? 50 })
23
+ : undefined;
24
+ const current = timeline?.currentState && Object.keys(timeline.currentState).length > 0
25
+ ? timeline.currentState
26
+ : await currentSessionState(store, currentSession);
15
27
  const goals = events
16
28
  .filter((event) => event.normalizedKind === "agent.prompt.submitted")
17
29
  .map((event) => ({
@@ -35,9 +47,12 @@ export async function buildAgentMemoryContext(input: {
35
47
  const openQuestions = timeline?.openQuestions ?? [];
36
48
  return {
37
49
  ok: true,
38
- scope: target ? "entry" : "current",
39
- entry: target,
50
+ scope,
51
+ entry: input.entry,
52
+ change: input.change,
53
+ proof: input.proof,
40
54
  currentState: current,
55
+ recommendedCommands: recommendedCommands(scope, input, currentSession?.id),
41
56
  agentMemory: {
42
57
  summary: {
43
58
  events: contextEventItems.length,
@@ -68,6 +83,84 @@ export async function buildAgentMemoryContext(input: {
68
83
  }
69
84
  }
70
85
 
86
+ function contextScope(input: {
87
+ entry?: string;
88
+ change?: string;
89
+ proof?: string;
90
+ handoff?: boolean;
91
+ }): AgentMemoryContextPack["scope"] {
92
+ if (input.handoff) {
93
+ return "handoff";
94
+ }
95
+ if (input.proof) {
96
+ return "proof";
97
+ }
98
+ if (input.change) {
99
+ return "change";
100
+ }
101
+ return input.entry ? "entry" : "current";
102
+ }
103
+
104
+ function contextTarget(
105
+ input: { entry?: string; change?: string; proof?: string },
106
+ scope: AgentMemoryContextPack["scope"],
107
+ ): string | undefined {
108
+ if (scope === "entry") {
109
+ return input.entry;
110
+ }
111
+ if (scope === "proof") {
112
+ return input.proof?.includes(":") ? input.proof : `proof:${input.proof}`;
113
+ }
114
+ if (scope === "change" && input.change && input.change !== "current") {
115
+ return input.change.includes(":") ? input.change : `session:${input.change}`;
116
+ }
117
+ return undefined;
118
+ }
119
+
120
+ function eventTarget(
121
+ input: { entry?: string; change?: string; proof?: string },
122
+ scope: AgentMemoryContextPack["scope"],
123
+ ): string | undefined {
124
+ if (scope === "entry") {
125
+ return input.entry;
126
+ }
127
+ if (scope === "proof") {
128
+ return input.proof;
129
+ }
130
+ if (scope === "change" && input.change !== "current") {
131
+ return input.change;
132
+ }
133
+ return undefined;
134
+ }
135
+
136
+ function recommendedCommands(
137
+ scope: AgentMemoryContextPack["scope"],
138
+ input: { entry?: string; change?: string; proof?: string },
139
+ currentSessionId: string | undefined,
140
+ ): string[] {
141
+ const commands = [
142
+ "forge agent timeline --json",
143
+ "forge delta status --verbose --json",
144
+ ];
145
+ if (scope === "entry" && input.entry) {
146
+ commands.push(`forge timeline ${input.entry} --json`);
147
+ commands.push(`forge explain ${input.entry} --json`);
148
+ }
149
+ if (scope === "proof" && input.proof) {
150
+ commands.push(`forge timeline proof:${input.proof.replace(/^proof:/u, "")} --json`);
151
+ }
152
+ if (scope === "change") {
153
+ commands.push(`forge timeline --session ${input.change === "current" || !input.change ? "current" : input.change} --json`);
154
+ commands.push("forge changed --json");
155
+ }
156
+ if (scope === "handoff") {
157
+ commands.push("forge handoff --json");
158
+ commands.push(`forge timeline --session ${currentSessionId ?? "current"} --json`);
159
+ commands.push("forge changed --json");
160
+ }
161
+ return [...new Set(commands)];
162
+ }
163
+
71
164
  function contextEvents(events: AgentMemoryEventRecord[]): AgentMemoryContextEvent[] {
72
165
  return events.map((event) => {
73
166
  const eventBindings = bindings(event);
@@ -92,8 +185,8 @@ function contextEvents(events: AgentMemoryEventRecord[]): AgentMemoryContextEven
92
185
  });
93
186
  }
94
187
 
95
- async function currentSessionState(store: DeltaStore): Promise<Record<string, unknown>> {
96
- const session = await store.currentWorkSession();
188
+ async function currentSessionState(store: DeltaStore, currentSession?: Awaited<ReturnType<DeltaStore["currentWorkSession"]>>): Promise<Record<string, unknown>> {
189
+ const session = currentSession ?? await store.currentWorkSession();
97
190
  return session
98
191
  ? {
99
192
  sessionId: session.id,
@@ -101,6 +194,11 @@ async function currentSessionState(store: DeltaStore): Promise<Record<string, un
101
194
  inferredIntent: session.inferredIntent,
102
195
  confidence: session.confidence,
103
196
  status: session.status,
197
+ reasons: session.reasons.slice(0, 5).map((reason) => ({
198
+ signal: reason.signal,
199
+ weight: reason.weight,
200
+ ...(reason.value ? { value: reason.value } : {}),
201
+ })),
104
202
  }
105
203
  : {};
106
204
  }
@@ -92,9 +92,12 @@ export interface AgentMemoryContextEvent {
92
92
 
93
93
  export interface AgentMemoryContextPack {
94
94
  ok: true;
95
- scope: "current" | "entry";
95
+ scope: "current" | "entry" | "change" | "proof" | "handoff";
96
96
  entry?: string;
97
+ change?: string;
98
+ proof?: string;
97
99
  currentState: Record<string, unknown>;
100
+ recommendedCommands: string[];
98
101
  agentMemory: {
99
102
  summary: {
100
103
  events: number;
@@ -128,6 +128,14 @@ import {
128
128
  import {
129
129
  formatDeltaExplainHuman,
130
130
  formatDeltaExplainJson,
131
+ formatDeltaCompactHuman,
132
+ formatDeltaCompactJson,
133
+ formatDeltaDoctorHuman,
134
+ formatDeltaDoctorJson,
135
+ formatDeltaExportHuman,
136
+ formatDeltaExportJson,
137
+ formatDeltaPruneHuman,
138
+ formatDeltaPruneJson,
131
139
  formatDeltaRepairHuman,
132
140
  formatDeltaRepairJson,
133
141
  formatDeltaStatusHuman,
@@ -137,6 +145,10 @@ import {
137
145
  formatDeltaTimelineHuman,
138
146
  formatDeltaTimelineJson,
139
147
  runDeltaExplain,
148
+ runDeltaCompact,
149
+ runDeltaDoctor,
150
+ runDeltaExport,
151
+ runDeltaPrune,
140
152
  runDeltaRepair,
141
153
  runDeltaSessionCommand,
142
154
  runDeltaStatus,
@@ -1641,6 +1653,11 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
1641
1653
  }
1642
1654
  return result.exitCode;
1643
1655
  }
1656
+ if (command.target === "delta") {
1657
+ const result = await runDeltaDoctor(command.workspaceRoot);
1658
+ process.stdout.write(command.json ? formatDeltaDoctorJson(result) : formatDeltaDoctorHuman(result));
1659
+ return result.exitCode;
1660
+ }
1644
1661
  const result = await runDoctorCommand({ workspaceRoot: command.workspaceRoot });
1645
1662
  if (command.json) {
1646
1663
  process.stdout.write(formatDoctorJson(result));
@@ -1808,6 +1825,34 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
1808
1825
  return result.exitCode;
1809
1826
  }
1810
1827
  case "delta": {
1828
+ if (command.subcommand === "compact") {
1829
+ const result = await runDeltaCompact({
1830
+ workspaceRoot: command.workspaceRoot,
1831
+ dryRun: command.dryRun,
1832
+ });
1833
+ process.stdout.write(command.json ? formatDeltaCompactJson(result) : formatDeltaCompactHuman(result));
1834
+ return result.exitCode;
1835
+ }
1836
+ if (command.subcommand === "prune") {
1837
+ const result = await runDeltaPrune({
1838
+ workspaceRoot: command.workspaceRoot,
1839
+ olderThan: command.olderThan,
1840
+ dryRun: command.dryRun,
1841
+ yes: command.yes,
1842
+ });
1843
+ process.stdout.write(command.json ? formatDeltaPruneJson(result) : formatDeltaPruneHuman(result));
1844
+ return result.exitCode;
1845
+ }
1846
+ if (command.subcommand === "export") {
1847
+ const result = await runDeltaExport({
1848
+ workspaceRoot: command.workspaceRoot,
1849
+ redacted: command.redacted,
1850
+ output: command.output,
1851
+ limit: command.limit,
1852
+ });
1853
+ process.stdout.write(command.json ? formatDeltaExportJson(result) : formatDeltaExportHuman(result));
1854
+ return result.exitCode;
1855
+ }
1811
1856
  if (command.subcommand === "repair") {
1812
1857
  const result = await runDeltaRepair({
1813
1858
  workspaceRoot: command.workspaceRoot,
@@ -1842,6 +1887,8 @@ export async function executeCommand(command: ForgeCommand): Promise<number> {
1842
1887
  session: command.sessionId,
1843
1888
  limit: command.limit,
1844
1889
  rebuild: command.rebuild,
1890
+ causal: command.causal,
1891
+ staleProofs: command.staleProofs,
1845
1892
  });
1846
1893
  process.stdout.write(command.json ? formatDeltaTimelineJson(result) : formatDeltaTimelineHuman(result));
1847
1894
  return result.exitCode;
@@ -18,6 +18,7 @@ function formatHelp(): string {
18
18
  " forge handoff --json Compact work handoff for the next external code agent",
19
19
  " forge agent onboard --target codex --json Prepare adapter, hooks, memory, and dev snapshot",
20
20
  " forge doctor agent --target codex --json Check adapter, hooks, and Agent Memory readiness",
21
+ " forge doctor delta --json Check DeltaDB writability, queue drain, redaction, and gitignore posture",
21
22
  " forge agent ingest codex --watch --file .forge/agent/events.ndjson --json",
22
23
  " forge docs check --json Check public docs, ReadTheDocs config, links, and local MkDocs tooling",
23
24
  " forge docs check --build --install-venv --json Build docs strictly in a local RTD-style venv",
@@ -25,6 +26,9 @@ function formatHelp(): string {
25
26
  " forge release check --allow-missing-local-release --json Gate release readiness without failing on unprepared local artifacts",
26
27
  " forge self-host check --prepared-only --json Report compose readiness without creating deploy files",
27
28
  " forge delta status --verbose --json Include Delta schema, lock, and aggregate count details",
29
+ " forge delta compact --json Compact redacted local agent queue history",
30
+ " forge delta prune --older-than 30d --dry-run --json Plan local Delta operational retention",
31
+ " forge delta export --redacted --json Export redacted Delta status, timeline, and agent memory",
28
32
  " forge studio open <app-path> --preview-port 5174 --target codex --json",
29
33
  " forge studio snapshot <app-path> --preview-port 5174 --target codex --probe-codex-server --json",
30
34
  " forge studio bridge <app-path> --preview-port 5174 --target codex --studio-url http://127.0.0.1:3765 --probe-codex-server --json",
@@ -8,7 +8,7 @@ import { run as runGenerate } from "../compiler/orchestrator/run.ts";
8
8
  import { resolvePackageManagerArgv } from "../compiler/package-manager/executor.ts";
9
9
  import { moduleDir } from "../platform/module.ts";
10
10
 
11
- export type NewTemplateName = "agent-workroom" | "b2b-support-web" | "minimal-web";
11
+ export type NewTemplateName = "agent-workroom" | "b2b-support-web" | "minimal-web" | "nuxt-web";
12
12
  export type NewPackageManager = "bun" | "npm" | "pnpm" | "yarn";
13
13
 
14
14
  export interface NewCommandOptions {
@@ -46,6 +46,8 @@ const REQUIRED_GITIGNORE_PATHS = [
46
46
  ".forge/cache/",
47
47
  ".forge/pglite/",
48
48
  ".forge/delta/",
49
+ ".forge/agent/*.ndjson",
50
+ ".forge/agent/*.history",
49
51
  ".forge/local/",
50
52
  ".forge/test-cache/",
51
53
  ".forge/test-runs/",
@@ -112,7 +112,7 @@ export type ForgeCommand =
112
112
  json: boolean;
113
113
  workspaceRoot: string;
114
114
  }
115
- | { kind: "doctor"; target?: "project" | "windows" | "agent"; agentTarget?: AgentAdapterTarget; json: boolean; workspaceRoot: string }
115
+ | { kind: "doctor"; target?: "project" | "windows" | "agent" | "delta"; agentTarget?: AgentAdapterTarget; json: boolean; workspaceRoot: string }
116
116
  | { kind: "setup"; target: "windows"; json: boolean; yes: boolean; workspaceRoot: string }
117
117
  | {
118
118
  kind: "security";
@@ -183,7 +183,19 @@ export type ForgeCommand =
183
183
  | { kind: "ui"; options: UiCommandOptions }
184
184
  | { kind: "manifest"; subcommand: "validate" | "import"; path: string; json: boolean; workspaceRoot: string }
185
185
  | { kind: "import"; options: BrownfieldImportCommandOptions }
186
- | { kind: "delta"; subcommand: "status" | "repair"; json: boolean; workspaceRoot: string; dryRun: boolean; yes: boolean; verbose: boolean }
186
+ | {
187
+ kind: "delta";
188
+ subcommand: "status" | "repair" | "compact" | "prune" | "export";
189
+ json: boolean;
190
+ workspaceRoot: string;
191
+ dryRun: boolean;
192
+ yes: boolean;
193
+ verbose: boolean;
194
+ olderThan?: string;
195
+ output?: string;
196
+ limit?: number;
197
+ redacted: boolean;
198
+ }
187
199
  | { kind: "status"; json: boolean; workspaceRoot: string }
188
200
  | { kind: "changed"; json: boolean; authoredOnly: boolean; workspaceRoot: string }
189
201
  | { kind: "diff"; target: "authored" | "generated" | "full"; json: boolean; workspaceRoot: string }
@@ -212,7 +224,7 @@ export type ForgeCommand =
212
224
  force: boolean;
213
225
  workspaceRoot: string;
214
226
  }
215
- | { kind: "timeline"; target?: string; kindFilter?: string; sessionId?: string; limit?: number; json: boolean; rebuild: boolean; forAgent: boolean; workspaceRoot: string }
227
+ | { kind: "timeline"; target?: string; kindFilter?: string; sessionId?: string; limit?: number; json: boolean; rebuild: boolean; forAgent: boolean; causal: boolean; staleProofs: boolean; workspaceRoot: string }
216
228
  | { kind: "explain"; thing: string; json: boolean; workspaceRoot: string }
217
229
  | {
218
230
  kind: "session";
@@ -480,7 +492,7 @@ export const INSPECT_TARGETS: InspectTarget[] = [
480
492
  "map",
481
493
  ];
482
494
 
483
- const NEW_TEMPLATES: NewTemplateName[] = ["agent-workroom", "b2b-support-web", "minimal-web"];
495
+ const NEW_TEMPLATES: NewTemplateName[] = ["agent-workroom", "b2b-support-web", "minimal-web", "nuxt-web"];
484
496
  const NEW_PACKAGE_MANAGERS: NewPackageManager[] = ["bun", "npm", "pnpm", "yarn"];
485
497
  const SELF_HOST_SUBCOMMANDS: SelfHostSubcommand[] = ["compose", "env", "check", "clean"];
486
498
  const AGENT_CONTRACT_SUBCOMMANDS: AgentContractSubcommand[] = [
@@ -1015,6 +1027,22 @@ export function parseCli(argv: string[]): ParsedCli {
1015
1027
  (subcommand === "timeline" ? rest[1] : undefined) ??
1016
1028
  (subcommand === "timeline" ? "all" : undefined) ??
1017
1029
  (subcommand === "hooks" || subcommand === "onboard" ? "codex" : "generic");
1030
+ const contextOptionValues = new Set(
1031
+ [
1032
+ parseOptionValue(argv, "--entry"),
1033
+ parseOptionValue(argv, "--change"),
1034
+ parseOptionValue(argv, "--proof"),
1035
+ parseOptionValue(argv, "--event"),
1036
+ parseOptionValue(argv, "--input"),
1037
+ parseOptionValue(argv, "--target"),
1038
+ parseOptionValue(argv, "--file"),
1039
+ limitRaw,
1040
+ pollIntervalRaw,
1041
+ ].filter((value): value is string => typeof value === "string"),
1042
+ );
1043
+ const contextEntry = subcommand === "context"
1044
+ ? rest.slice(1).find((part) => !part.startsWith("--") && !contextOptionValues.has(part))
1045
+ : undefined;
1018
1046
  return {
1019
1047
  command: {
1020
1048
  kind: "agent",
@@ -1031,7 +1059,10 @@ export function parseCli(argv: string[]): ParsedCli {
1031
1059
  eventName: parseOptionValue(argv, "--event"),
1032
1060
  hookAction: subcommand === "hooks" ? rest[1] : undefined,
1033
1061
  input,
1034
- entry: parseOptionValue(argv, "--entry") ?? (subcommand === "context" && rest[1] !== "--current" ? rest[1] : undefined),
1062
+ entry: parseOptionValue(argv, "--entry") ?? contextEntry,
1063
+ change: parseOptionValue(argv, "--change"),
1064
+ proof: parseOptionValue(argv, "--proof"),
1065
+ handoff: parseFlag(argv, "--handoff"),
1035
1066
  current: parseFlag(argv, "--current"),
1036
1067
  limit: limit ? Math.floor(limit) : undefined,
1037
1068
  watch: parseFlag(argv, "--watch"),
@@ -1152,14 +1183,14 @@ export function parseCli(argv: string[]): ParsedCli {
1152
1183
  };
1153
1184
  }
1154
1185
  case "doctor":
1155
- if (rest[0] && rest[0] !== "windows" && rest[0] !== "agent") {
1156
- errors.push("forge doctor supports subcommand: windows or agent");
1186
+ if (rest[0] && rest[0] !== "windows" && rest[0] !== "agent" && rest[0] !== "delta") {
1187
+ errors.push("forge doctor supports subcommand: windows, agent, or delta");
1157
1188
  return { command: null, workspaceRoot, errors };
1158
1189
  }
1159
1190
  return {
1160
1191
  command: {
1161
1192
  kind: "doctor",
1162
- target: rest[0] === "windows" ? "windows" : rest[0] === "agent" ? "agent" : "project",
1193
+ target: rest[0] === "windows" ? "windows" : rest[0] === "agent" ? "agent" : rest[0] === "delta" ? "delta" : "project",
1163
1194
  agentTarget: rest[0] === "agent"
1164
1195
  ? (parseOptionValue(argv, "--target") as AgentAdapterTarget | undefined) ?? (rest[1] as AgentAdapterTarget | undefined) ?? "codex"
1165
1196
  : undefined,
@@ -1882,10 +1913,12 @@ export function parseCli(argv: string[]): ParsedCli {
1882
1913
  }
1883
1914
  case "delta": {
1884
1915
  const subcommand = rest[0];
1885
- if (subcommand !== "status" && subcommand !== "repair") {
1886
- errors.push("forge delta requires subcommand: status or repair");
1916
+ if (subcommand !== "status" && subcommand !== "repair" && subcommand !== "compact" && subcommand !== "prune" && subcommand !== "export") {
1917
+ errors.push("forge delta requires subcommand: status, repair, compact, prune, or export");
1887
1918
  return { command: null, workspaceRoot, errors };
1888
1919
  }
1920
+ const limitRaw = parseOptionValue(argv, "--limit");
1921
+ const limit = limitRaw ? Number(limitRaw) : undefined;
1889
1922
  return {
1890
1923
  command: {
1891
1924
  kind: "delta",
@@ -1894,6 +1927,10 @@ export function parseCli(argv: string[]): ParsedCli {
1894
1927
  dryRun: parseFlag(argv, "--dry-run"),
1895
1928
  yes: parseFlag(argv, "--yes"),
1896
1929
  verbose: parseFlag(argv, "--verbose"),
1930
+ olderThan: parseOptionValue(argv, "--older-than"),
1931
+ output: parseOptionValue(argv, "--output"),
1932
+ limit: Number.isFinite(limit) ? limit : undefined,
1933
+ redacted: parseFlag(argv, "--redacted"),
1897
1934
  workspaceRoot,
1898
1935
  },
1899
1936
  workspaceRoot,
@@ -1933,7 +1970,10 @@ export function parseCli(argv: string[]): ParsedCli {
1933
1970
  const kindFilter = parseOptionValue(argv, "--kind");
1934
1971
  const sessionId = parseOptionValue(argv, "--session");
1935
1972
  const rebuild = rest[0] === "rebuild";
1936
- const target = rebuild ? undefined : rest.find((item) => item !== kindFilter && item !== limitRaw && item !== sessionId);
1973
+ const optionValues = new Set([limitRaw, kindFilter, sessionId].filter((value): value is string => typeof value === "string"));
1974
+ const target = rebuild
1975
+ ? undefined
1976
+ : rest.find((item) => !item.startsWith("--") && !optionValues.has(item));
1937
1977
  const limit = limitRaw ? Number(limitRaw) : undefined;
1938
1978
  if (limitRaw !== undefined && (!Number.isFinite(limit) || limit! < 1)) {
1939
1979
  errors.push("--limit must be a number >= 1");
@@ -1948,6 +1988,8 @@ export function parseCli(argv: string[]): ParsedCli {
1948
1988
  json: parseFlag(argv, "--json"),
1949
1989
  rebuild,
1950
1990
  forAgent: parseFlag(argv, "--for-agent"),
1991
+ causal: parseFlag(argv, "--causal"),
1992
+ staleProofs: parseFlag(argv, "--stale-proofs"),
1951
1993
  workspaceRoot,
1952
1994
  },
1953
1995
  workspaceRoot,
@@ -2642,6 +2684,8 @@ export function hasUnknownOption(argv: string[]): string | null {
2642
2684
  "--json",
2643
2685
  "--human",
2644
2686
  "--for-agent",
2687
+ "--causal",
2688
+ "--stale-proofs",
2645
2689
  "--dry-run",
2646
2690
  "--plan",
2647
2691
  "--staged",
@@ -2698,6 +2742,9 @@ export function hasUnknownOption(argv: string[]): string | null {
2698
2742
  "--emit",
2699
2743
  "--event",
2700
2744
  "--entry",
2745
+ "--change",
2746
+ "--proof",
2747
+ "--handoff",
2701
2748
  "--current",
2702
2749
  "--trigger",
2703
2750
  "--component",
@@ -2794,6 +2841,8 @@ export function hasUnknownOption(argv: string[]): string | null {
2794
2841
  "--env-file",
2795
2842
  "--skip-startup-console",
2796
2843
  "--redacted",
2844
+ "--older-than",
2845
+ "--output",
2797
2846
  "--mock-ai",
2798
2847
  "--ai",
2799
2848
  "--provider",
@@ -2857,6 +2906,8 @@ export function hasUnknownOption(argv: string[]): string | null {
2857
2906
  arg === "--emit" ||
2858
2907
  arg === "--event" ||
2859
2908
  arg === "--entry" ||
2909
+ arg === "--change" ||
2910
+ arg === "--proof" ||
2860
2911
  arg === "--trigger" ||
2861
2912
  arg === "--component" ||
2862
2913
  arg === "--package" ||
@@ -2901,6 +2952,8 @@ export function hasUnknownOption(argv: string[]): string | null {
2901
2952
  arg === "--db" ||
2902
2953
  arg === "--database-url" ||
2903
2954
  arg === "--limit" ||
2955
+ arg === "--older-than" ||
2956
+ arg === "--output" ||
2904
2957
  arg === "--kind" ||
2905
2958
  arg === "--session" ||
2906
2959
  arg === "--input" ||