@moretea-labs/forge 1.5.0-rc.1 → 1.5.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.
Files changed (264) hide show
  1. package/AGENTS.md +9 -11
  2. package/CHANGELOG.md +27 -1
  3. package/CLAUDE.md +8 -10
  4. package/DESIGN.md +278 -0
  5. package/README.md +70 -90
  6. package/README.zh-CN.md +52 -55
  7. package/SKILL.md +8 -11
  8. package/THIRD_PARTY_NOTICES.md +9 -3
  9. package/assets/branding/README.md +4 -2
  10. package/assets/hooks/lib/workflow-state.sh +19 -18
  11. package/assets/hooks/post-edit-guard.sh +6 -75
  12. package/assets/hooks/prompt-guard.sh +24 -12
  13. package/assets/hooks/session-start-context.sh +17 -17
  14. package/assets/hooks/stop-orchestrator.sh +2 -2
  15. package/assets/partials/04-project-structure.partial.md +6 -6
  16. package/assets/partials/05-workflow.partial.md +6 -6
  17. package/assets/partials/07-footer.partial.md +1 -1
  18. package/assets/partials/08-orchestration.partial.md +2 -2
  19. package/assets/partials-agents/02-operating-mode.partial.md +5 -5
  20. package/assets/partials-agents/03-orchestration.partial.md +3 -3
  21. package/assets/partials-agents/04-task-protocol.partial.md +3 -3
  22. package/assets/partials-agents/08-deep-docs.partial.md +1 -1
  23. package/assets/reference-configs/agentic-development-flow.md +49 -86
  24. package/assets/reference-configs/external-tooling.md +21 -61
  25. package/assets/reference-configs/handoff-protocol.md +25 -20
  26. package/assets/reference-configs/harness-overview.md +8 -8
  27. package/assets/skill-version.json +2 -2
  28. package/assets/templates/helpers/archive-workflow.sh +0 -1
  29. package/assets/templates/helpers/capture-plan.sh +3 -3
  30. package/assets/templates/helpers/check-task-workflow.sh +2 -2
  31. package/assets/templates/helpers/codex-handoff-resume.sh +5 -5
  32. package/assets/templates/helpers/ensure-task-workflow.sh +37 -35
  33. package/assets/templates/helpers/new-plan.sh +1 -1
  34. package/assets/templates/helpers/plan-to-todo.sh +2 -2
  35. package/assets/templates/helpers/prepare-codex-handoff.sh +2 -2
  36. package/assets/templates/helpers/prepare-handoff.sh +9 -9
  37. package/assets/templates/helpers/refresh-current-status.sh +2 -2
  38. package/assets/templates/helpers/ship-worktrees.sh +1 -1
  39. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  40. package/assets/templates/helpers/verify-sprint.sh +7 -7
  41. package/assets/templates/helpers/workstream-sync.sh +1 -1
  42. package/assets/templates/plan.template.md +1 -1
  43. package/assets/workflow-contract.v1.json +6 -3
  44. package/docs/architecture/current/README.md +10 -9
  45. package/docs/architecture/current/approved-target-architecture.zh-CN.md +2 -2
  46. package/docs/architecture/current/architecture-invariants.md +2 -2
  47. package/docs/architecture/current/automation-and-schedule-engine.md +15 -15
  48. package/docs/architecture/current/codegraph-context-provider.md +2 -2
  49. package/docs/architecture/current/control-plane-authority-inventory.md +12 -0
  50. package/docs/architecture/current/dispatch-and-agent-strategy.md +11 -11
  51. package/docs/architecture/current/entity-model.md +0 -27
  52. package/docs/architecture/current/failure-recovery.md +0 -24
  53. package/docs/architecture/current/governance.md +1 -1
  54. package/docs/architecture/current/implementation-status.md +1 -1
  55. package/docs/architecture/current/legacy-governance.md +58 -0
  56. package/docs/architecture/current/multi-repository-execution.md +1 -1
  57. package/docs/architecture/current/peer-supercontroller-runtime.md +1 -1
  58. package/docs/architecture/current/personal-assistant-plugin-baseline.md +4 -5
  59. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -10
  60. package/docs/architecture/current/target-requirements-traceability.md +2 -2
  61. package/docs/architecture/current/thin-harness-governance-policy.md +1 -1
  62. package/docs/architecture/current/thin-harness-v1.md +9 -11
  63. package/docs/architecture/domains/runtime-harness.md +1 -1
  64. package/docs/forge-autonomous-goal-loop.md +3 -1
  65. package/docs/forge-chatgpt-mcp-setup.md +67 -176
  66. package/docs/images/forge-banner-cn.svg +26 -1
  67. package/docs/images/forge-banner.svg +26 -1
  68. package/docs/operations/features.md +1 -1
  69. package/docs/operations/homebrew.md +3 -3
  70. package/docs/operations/mcp-tool-exposure.md +5 -5
  71. package/docs/operations/platform-support.md +45 -37
  72. package/docs/operations/platform-support.zh-CN.md +40 -38
  73. package/docs/operations/releasing.md +3 -3
  74. package/docs/operations/releasing.zh-CN.md +3 -3
  75. package/docs/operations/troubleshooting.md +11 -6
  76. package/docs/operations/troubleshooting.zh-CN.md +11 -6
  77. package/docs/public-usage-guide.md +28 -21
  78. package/docs/public-usage-guide.zh-CN.md +28 -21
  79. package/docs/tutorials/01-install-and-start.md +58 -35
  80. package/docs/tutorials/01-install-and-start.zh-CN.md +58 -35
  81. package/docs/tutorials/02-connect-chatgpt.md +118 -46
  82. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +119 -45
  83. package/docs/wiki/Architecture.md +1 -1
  84. package/docs/wiki/Installation.md +2 -2
  85. package/docs/wiki/Quick-Start.md +1 -1
  86. package/docs/wiki/Work-Lifecycle.md +1 -1
  87. package/install.ps1 +4 -5
  88. package/install.sh +4 -6
  89. package/package.json +15 -5
  90. package/scripts/archive-workflow.sh +0 -1
  91. package/scripts/benchmark-thin-harness-mcp-e2e.ts +27 -14
  92. package/scripts/build-controller-ui.ts +28 -0
  93. package/scripts/capture-plan.sh +4 -4
  94. package/scripts/check-agent-tooling.sh +12 -1
  95. package/scripts/check-platform-support.mjs +2 -2
  96. package/scripts/check-public-docs.sh +16 -5
  97. package/scripts/check-release-readiness.sh +5 -11
  98. package/scripts/check-runtime-architecture.mjs +14 -22
  99. package/scripts/check-tarball-install-smoke.sh +3 -2
  100. package/scripts/check-task-workflow.sh +2 -2
  101. package/scripts/codex-handoff-resume.sh +5 -5
  102. package/scripts/create-project-dirs.sh +3 -3
  103. package/scripts/ensure-task-workflow.sh +45 -35
  104. package/scripts/init-project.sh +4 -4
  105. package/scripts/install-desktop-operator-registration.ts +2 -2
  106. package/scripts/lib/project-init-lib.sh +46 -44
  107. package/scripts/migrate-project-template.sh +7 -4
  108. package/scripts/new-plan.sh +1 -1
  109. package/scripts/plan-to-todo.sh +2 -2
  110. package/scripts/prepare-codex-handoff.sh +2 -2
  111. package/scripts/prepare-handoff.sh +9 -9
  112. package/scripts/refresh-current-status.sh +2 -2
  113. package/scripts/setup-plugins.sh +3 -3
  114. package/scripts/ship-worktrees.sh +1 -1
  115. package/scripts/sprint-backlog.sh +5 -5
  116. package/scripts/sync-codex-installed-copies.sh +2 -3
  117. package/scripts/verify-sprint.sh +7 -7
  118. package/scripts/workstream-sync.sh +1 -1
  119. package/src/cli/chatgpt-browser/bridge-extension.ts +1 -1
  120. package/src/cli/chatgpt-browser/bridge-provider.ts +29 -0
  121. package/src/cli/chatgpt-browser/file-policy.ts +3 -1
  122. package/src/cli/commands/chatgpt.ts +51 -0
  123. package/src/cli/commands/doctor.ts +1 -1
  124. package/src/cli/commands/init-hook.ts +213 -70
  125. package/src/cli/commands/init.ts +5 -6
  126. package/src/cli/commands/mcp.ts +31 -5
  127. package/src/cli/commands/runtime.ts +61 -8
  128. package/src/cli/commands/setup-profile.ts +472 -0
  129. package/src/cli/controller/check-runner.ts +20 -3
  130. package/src/cli/controller/codex-continuation.ts +4 -7
  131. package/src/cli/controller/context-pack.ts +118 -14
  132. package/src/cli/controller/operational-plan.ts +4 -4
  133. package/src/cli/controller/task-ledger.ts +10 -10
  134. package/src/cli/controller/work-mode.ts +6 -13
  135. package/src/cli/index.ts +14 -35
  136. package/src/cli/local-bridge/connector-freshness.ts +1 -1
  137. package/src/cli/local-bridge/console-assets.ts +17 -0
  138. package/src/cli/local-bridge/console-automations.ts +160 -0
  139. package/src/cli/local-bridge/dashboard.ts +8 -1303
  140. package/src/cli/local-bridge/facade-api.ts +16 -7
  141. package/src/cli/local-bridge/frontend/src/App.tsx +10 -0
  142. package/src/cli/local-bridge/frontend/src/components/Button.tsx +1 -0
  143. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +3 -0
  144. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +1 -0
  145. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +1 -0
  146. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +1 -0
  147. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +3 -0
  148. package/src/cli/local-bridge/frontend/src/design/icons.tsx +11 -0
  149. package/src/cli/local-bridge/frontend/src/design/tokens.css +8 -0
  150. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +3 -0
  151. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +4 -0
  152. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +3 -0
  153. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +2 -0
  154. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +2 -0
  155. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +2 -0
  156. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +3 -0
  157. package/src/cli/local-bridge/frontend/src/lib/api.ts +23 -0
  158. package/src/cli/local-bridge/frontend/src/lib/format.ts +4 -0
  159. package/src/cli/local-bridge/frontend/src/lib/routes.ts +7 -0
  160. package/src/cli/local-bridge/frontend/src/main.tsx +1 -0
  161. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +1 -0
  162. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +2 -0
  163. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +3 -0
  164. package/src/cli/local-bridge/frontend/src/styles.css +12 -0
  165. package/src/cli/local-bridge/frontend/src/types.ts +14 -0
  166. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +1 -0
  167. package/src/cli/local-bridge/frontend/vite.config.ts +27 -0
  168. package/src/cli/local-bridge/server.ts +45 -0
  169. package/src/cli/local-bridge/ui-dist/app.css +1 -0
  170. package/src/cli/local-bridge/ui-dist/app.js +11094 -0
  171. package/src/cli/mcp/auth.ts +2 -0
  172. package/src/cli/mcp/instructions.ts +3 -3
  173. package/src/cli/mcp/legacy-tool-service.ts +27 -23
  174. package/src/cli/mcp/multi-repository.ts +2 -0
  175. package/src/cli/mcp/policy.ts +7 -5
  176. package/src/cli/mcp/repository-tools.ts +53 -14
  177. package/src/cli/mcp/server.ts +5 -10
  178. package/src/cli/mcp/setup.ts +220 -276
  179. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  180. package/src/cli/mcp/toolset-names.ts +1 -1
  181. package/src/cli/mcp/toolset.ts +0 -1
  182. package/src/cli/mcp/transports/http.ts +51 -4
  183. package/src/cli/repositories/branch-name-policy.ts +0 -7
  184. package/src/cli/repositories/controller-home.ts +110 -3
  185. package/src/cli/repositories/safe-patch.ts +1 -1
  186. package/src/cli/repositories/selected-path-actions.ts +26 -24
  187. package/src/core/adoption/gitignore-plan.ts +2 -3
  188. package/src/core/adoption/plan.ts +4 -1
  189. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +556 -0
  190. package/src/runtime/control-plane/execution/work-handle-store.ts +128 -1
  191. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +277 -0
  192. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +169 -0
  193. package/src/runtime/control-plane/facade/capability-registry.ts +0 -10
  194. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +44 -25
  195. package/src/runtime/control-plane/facade/suggested-actions.ts +7 -1
  196. package/src/runtime/control-plane/facade/types.ts +0 -1
  197. package/src/runtime/control-plane/global-scheduler/scheduler.ts +30 -8
  198. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +114 -0
  199. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +175 -0
  200. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +161 -0
  201. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +64 -0
  202. package/src/runtime/control-plane/launcher/thin-launcher.ts +117 -24
  203. package/src/runtime/control-plane/routing/route-policy.ts +14 -14
  204. package/src/runtime/control-plane/runtime-cleanup.ts +0 -1
  205. package/src/runtime/evidence/event-ledger.ts +1 -1
  206. package/src/runtime/{workflow/campaigns/workspace.ts → execution/managed-workspace.ts} +41 -43
  207. package/src/runtime/execution/process-runtime/check-facade.ts +6 -0
  208. package/src/runtime/execution/process-runtime/check-result.ts +45 -0
  209. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -16
  210. package/src/runtime/execution/process-runtime/command-facade.ts +4 -2
  211. package/src/runtime/execution/process-runtime/gc.ts +1 -1
  212. package/src/runtime/execution/process-runtime/index.ts +3 -0
  213. package/src/runtime/execution/process-runtime/interactive-admission.ts +14 -5
  214. package/src/runtime/execution/process-runtime/persisted-check.ts +285 -0
  215. package/src/runtime/execution/process-runtime/resource-claims.ts +30 -0
  216. package/src/runtime/execution/process-runtime/runtime.ts +18 -1
  217. package/src/runtime/execution/process-runtime/store.ts +1 -19
  218. package/src/runtime/execution/process-runtime/types.ts +4 -0
  219. package/src/runtime/execution/thin-harness/execution-router.ts +0 -1
  220. package/src/runtime/execution/thin-harness/fast-executor.ts +1 -1
  221. package/src/runtime/execution/thin-harness/lightweight-lanes.ts +1 -2
  222. package/src/runtime/execution/thin-harness/types.ts +0 -1
  223. package/src/runtime/execution/workers/executor.ts +1 -1
  224. package/src/runtime/gateway/mcp/execution-tools.ts +1 -1
  225. package/src/runtime/gateway/mcp/persisted-check-process.ts +3 -255
  226. package/src/runtime/gateway/mcp/process-tools.ts +16 -3
  227. package/src/runtime/gateway/mcp/resource-policy.ts +0 -9
  228. package/src/runtime/gateway/mcp/router.ts +117 -182
  229. package/src/runtime/gateway/mcp/runtime-tools.ts +331 -409
  230. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +1 -219
  231. package/src/runtime/plugins/browser-adapter.ts +5 -0
  232. package/src/runtime/plugins/desktop-operator-registration.ts +1 -1
  233. package/src/runtime/plugins/store.ts +14 -6
  234. package/src/runtime/plugins/types.ts +1 -1
  235. package/src/runtime/projections/materialized-view.ts +1 -23
  236. package/src/runtime/recovery/maintenance-executor.ts +9 -0
  237. package/src/runtime/resources/ownership.ts +0 -1
  238. package/src/runtime/root/gateway-adapter.ts +2 -0
  239. package/src/runtime/root/mcp-transport.ts +10 -2
  240. package/src/runtime/root/package-connector-service.ts +136 -0
  241. package/src/runtime/root/package-runtime-release.ts +161 -0
  242. package/src/runtime/root/package-runtime-service.ts +185 -0
  243. package/src/runtime/root/release-materialize.ts +19 -0
  244. package/src/runtime/root/runtime.ts +2 -1
  245. package/src/runtime/root/scheduler.ts +5 -0
  246. package/src/runtime/root/service.ts +18 -5
  247. package/src/runtime/standalone-recovery/core.ts +65 -16
  248. package/src/runtime/workflow/findings/store.ts +145 -106
  249. package/src/runtime/workflow/findings/types.ts +22 -0
  250. package/src/runtime/workflow/schedules/engine.ts +132 -2
  251. package/src/runtime/workflow/schedules/pending-activation.ts +139 -0
  252. package/src/runtime/workflow/schedules/work-continuation.ts +218 -0
  253. package/docs/CHANGELOG.md +0 -978
  254. package/docs/README.zh-CN.md +0 -58
  255. package/scripts/smoke-chatgpt-supervised-automation.ts +0 -208
  256. package/src/cli/commands/controller.ts +0 -680
  257. package/src/runtime/workflow/campaigns/cleanup.ts +0 -263
  258. package/src/runtime/workflow/campaigns/engine.ts +0 -112
  259. package/src/runtime/workflow/campaigns/normalize.ts +0 -100
  260. package/src/runtime/workflow/campaigns/review.ts +0 -315
  261. package/src/runtime/workflow/campaigns/store.ts +0 -526
  262. package/src/runtime/workflow/campaigns/supervisor.ts +0 -112
  263. package/src/runtime/workflow/campaigns/types.ts +0 -290
  264. /package/src/runtime/{workflow/campaigns → execution}/workspace-agent.ts +0 -0
@@ -79,10 +79,10 @@ ${packet.allowedActions.map((action) => `- ${action}`).join('\n')}
79
79
  ${packet.stopConditions.map((condition) => `- ${condition}`).join('\n')}
80
80
 
81
81
  ## Instructions
82
- 1. Prefer \`forge controller completion-backlog --json\` before starting new work.
83
- 2. Run \`forge controller finish-ready-runs --apply --json\` only for low/medium auto-finishable runs.
82
+ 1. Read current Runtime state through \`rh_status\`, pending decisions through \`rh_inbox\`, and bounded Work through \`rh_work\` before starting new work.
83
+ 2. Use \`rh_work.verify/finalize/repair\` for current Work lifecycle changes; do not mutate legacy Issue/Task state.
84
84
  3. For high/destructive work, prepare a concise review summary instead of auto-approving it.
85
- 4. If a command fails because of tool/platform limitations, write a small patch or a continuation note rather than retrying the same blocked path repeatedly.
85
+ 4. If a command fails because of tool/platform limitations, create a Runtime HandoffItem or bounded continuation note rather than retrying the same blocked path repeatedly.
86
86
  5. Finish with checks, a short status summary, and explicit remaining blockers.
87
87
  `;
88
88
  }
@@ -101,7 +101,7 @@ export function prepareCodexContinuation(repoRoot: string, options: CodexContinu
101
101
  controller: 'codex-cli',
102
102
  allowedActions: [
103
103
  'inspect compact backlog and stuck-state reports',
104
- 'finish low/medium successful runs using forge controller finish-ready-runs',
104
+ 'finish validated bounded Work through rh_work.finalize when policy allows',
105
105
  'prepare patches for remaining blocker classes',
106
106
  'run declared checks and summarize failures',
107
107
  ],
@@ -123,9 +123,6 @@ export function prepareCodexContinuation(repoRoot: string, options: CodexContinu
123
123
  recommendations: stuckStates.recommendations,
124
124
  },
125
125
  nextCommands: [
126
- 'forge controller completion-backlog --json',
127
- 'forge controller stuck-states --json',
128
- ...(backlog.finishableRunIds.length ? ['forge controller finish-ready-runs --apply --json'] : []),
129
126
  'npm run check:type',
130
127
  'npm run check:mcp-compatibility',
131
128
  ],
@@ -13,7 +13,7 @@ import {
13
13
  type CodeGraphReadProviderResponse,
14
14
  } from "../../runtime/context/codegraph-read-provider";
15
15
 
16
- const CONTEXT_PACK_SCHEMA_VERSION = 4;
16
+ const CONTEXT_PACK_SCHEMA_VERSION = 6;
17
17
  const DEFAULT_MAX_FILES = 8;
18
18
  const DEFAULT_MAX_SNIPPETS = 20;
19
19
  const DEFAULT_SNIPPET_CONTEXT_BEFORE = 12;
@@ -38,6 +38,22 @@ const STOPWORDS = new Set([
38
38
  ]);
39
39
 
40
40
  export type StructuralContextMode = "off" | "auto" | "required";
41
+ export type ControllerContextRetrievalMode = "implementation" | "plan" | "debug" | "review";
42
+ export const CONTROLLER_CONTEXT_IMPACT_DOMAINS = [
43
+ "persistence", "scheduler", "notification", "timeline", "events", "cache", "api", "concurrency",
44
+ ] as const;
45
+ export type ControllerContextImpactDomain = (typeof CONTROLLER_CONTEXT_IMPACT_DOMAINS)[number];
46
+
47
+ const IMPACT_DOMAIN_TERMS: Record<ControllerContextImpactDomain, readonly string[]> = {
48
+ persistence: ["persist", "database", "repository"],
49
+ scheduler: ["scheduler", "schedule", "reminder"],
50
+ notification: ["notification", "notify", "push"],
51
+ timeline: ["timeline", "history", "activity"],
52
+ events: ["event", "publish", "subscribe"],
53
+ cache: ["cache", "invalidate", "memo"],
54
+ api: ["api", "dto", "controller"],
55
+ concurrency: ["transaction", "lock", "atomic"],
56
+ };
41
57
 
42
58
  export interface ControllerContextPackOptions {
43
59
  description?: string;
@@ -51,6 +67,9 @@ export interface ControllerContextPackOptions {
51
67
  maxSnippets?: number;
52
68
  maxCharsPerSnippet?: number;
53
69
  structuralContext?: StructuralContextMode;
70
+ retrievalMode?: ControllerContextRetrievalMode;
71
+ /** GPT-selected cross-cutting evidence dimensions. Forge expands mechanically and never treats them as semantic completeness proof. */
72
+ impactDomains?: ControllerContextImpactDomain[];
54
73
  }
55
74
 
56
75
  export interface ControllerContextPackDependencies {
@@ -97,6 +116,15 @@ export interface ControllerContextPackProjection {
97
116
  };
98
117
  search: {
99
118
  terms: string[];
119
+ impactDomains: ControllerContextImpactDomain[];
120
+ impactCoverage: Array<{
121
+ domain: ControllerContextImpactDomain;
122
+ queries: string[];
123
+ matchedFiles: string[];
124
+ selectedFiles: string[];
125
+ omittedFileCount: number;
126
+ status: "selected" | "matched_not_selected" | "no_evidence";
127
+ }>;
100
128
  includeGlobs: string[];
101
129
  excludeGlobs: string[];
102
130
  scannedFiles: number;
@@ -132,7 +160,10 @@ export interface ControllerContextPackProjection {
132
160
  };
133
161
  contextContract: {
134
162
  strategy: string;
135
- rawCodeRequiredForImplementation: true;
163
+ retrievalMode: ControllerContextRetrievalMode;
164
+ semanticSufficiencyAuthority: "chatgpt";
165
+ rawCodeRequiredForImplementation: boolean;
166
+ expansionSignals: string[];
136
167
  notes: string[];
137
168
  };
138
169
  next: string[];
@@ -364,6 +395,7 @@ export function buildControllerContextPack(
364
395
  options: ControllerContextPackOptions = {},
365
396
  dependencies: ControllerContextPackDependencies = {},
366
397
  ): ControllerContextPackProjection {
398
+ const retrievalMode = options.retrievalMode ?? "implementation";
367
399
  const maxFiles = clamp(options.maxFiles, DEFAULT_MAX_FILES, 1, 30);
368
400
  const maxSnippets = clamp(options.maxSnippets, DEFAULT_MAX_SNIPPETS, 1, 80);
369
401
  const maxCharsPerSnippet = clamp(options.maxCharsPerSnippet, DEFAULT_MAX_CHARS_PER_SNIPPET, 500, 50_000);
@@ -395,6 +427,23 @@ export function buildControllerContextPack(
395
427
  ].filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0);
396
428
  const goal = goalParts.join("\n").trim();
397
429
  const terms = cleanList([...(options.searchTerms ?? []), ...textTokens(goal)]).slice(0, 14);
430
+ const impactDomains = cleanList(options.impactDomains)
431
+ .filter((domain): domain is ControllerContextImpactDomain => CONTROLLER_CONTEXT_IMPACT_DOMAINS.includes(domain as ControllerContextImpactDomain))
432
+ .slice(0, 6);
433
+ const impactTermDomains = new Map<string, ControllerContextImpactDomain[]>();
434
+ for (const domain of impactDomains) {
435
+ for (const term of IMPACT_DOMAIN_TERMS[domain]) {
436
+ const existing = impactTermDomains.get(term) ?? [];
437
+ if (!existing.includes(domain)) existing.push(domain);
438
+ impactTermDomains.set(term, existing);
439
+ }
440
+ }
441
+ const searchQueries = cleanList([
442
+ ...(terms[0] ? [terms[0]] : []),
443
+ ...impactDomains.flatMap((domain) => IMPACT_DOMAIN_TERMS[domain]),
444
+ ...terms.slice(1),
445
+ ]).slice(0, 32);
446
+ const impactCandidatePaths = new Map<ControllerContextImpactDomain, Set<string>>(impactDomains.map((domain) => [domain, new Set<string>()]));
398
447
  const candidates = new Map<string, { reasons: Set<string>; lines: Set<number> }>();
399
448
  const deniedPaths: Array<{ path: string; reason: string }> = [];
400
449
  const omitted: Array<{ path: string; reason: string }> = [];
@@ -415,7 +464,7 @@ export function buildControllerContextPack(
415
464
  };
416
465
 
417
466
  if (structuralMode !== "off") {
418
- const structuralQuery = goal || terms.join(" ");
467
+ const structuralQuery = [goal || terms.join(" "), ...impactDomains].filter(Boolean).join(" ");
419
468
  if (!structuralQuery) {
420
469
  structuralContext = {
421
470
  ...structuralContext,
@@ -504,9 +553,9 @@ export function buildControllerContextPack(
504
553
  // Lexical retrieval is one bounded pass across candidate files for all terms.
505
554
  // The old per-term loop reread the same source files up to 14 times even
506
555
  // though inventory itself was cached.
507
- if (terms.length > 0 && candidates.size < maxFiles * 3) {
556
+ if (searchQueries.length > 0 && candidates.size < maxFiles * 3) {
508
557
  const search = searchRepositoryMany(repoRoot, policy, {
509
- queries: terms,
558
+ queries: searchQueries,
510
559
  includeGlobs: searchIncludeGlobs,
511
560
  excludeGlobs,
512
561
  maxResultsPerQuery: Math.max(maxFiles * 4, 12),
@@ -521,7 +570,16 @@ export function buildControllerContextPack(
521
570
  searchTruncated = searchTruncated || search.truncated;
522
571
  for (const hit of search.results) {
523
572
  if (!coveredByGlob(hit.path, searchIncludeGlobs) || excludeGlobs.some((glob) => globMatches(glob, hit.path))) continue;
524
- addReason(candidates, hit.path, `search:${hit.query}`, hit.line);
573
+ if (terms.includes(hit.query)) {
574
+ addReason(candidates, hit.path, `search:${hit.query}`, hit.line);
575
+ } else {
576
+ const domains = impactTermDomains.get(hit.query) ?? [];
577
+ if (domains.length === 0) addReason(candidates, hit.path, `search:${hit.query}`, hit.line);
578
+ for (const domain of domains) {
579
+ impactCandidatePaths.get(domain)?.add(hit.path);
580
+ addReason(candidates, hit.path, `impact:${domain}:${hit.query}`, hit.line);
581
+ }
582
+ }
525
583
  if (candidates.size >= maxFiles * 3) break;
526
584
  }
527
585
  }
@@ -541,6 +599,19 @@ export function buildControllerContextPack(
541
599
  return left.path.localeCompare(right.path);
542
600
  });
543
601
  const selected = rankedCandidates.slice(0, maxFiles);
602
+ const selectedPaths = new Set(selected.map((entry) => entry.path));
603
+ const impactCoverage = impactDomains.map((domain) => {
604
+ const matchedFiles = [...(impactCandidatePaths.get(domain) ?? new Set<string>())].sort();
605
+ const selectedFiles = matchedFiles.filter((path) => selectedPaths.has(path));
606
+ return {
607
+ domain,
608
+ queries: [...IMPACT_DOMAIN_TERMS[domain]],
609
+ matchedFiles: matchedFiles.slice(0, 20),
610
+ selectedFiles: selectedFiles.slice(0, 20),
611
+ omittedFileCount: Math.max(0, matchedFiles.length - selectedFiles.length),
612
+ status: selectedFiles.length > 0 ? "selected" as const : matchedFiles.length > 0 ? "matched_not_selected" as const : "no_evidence" as const,
613
+ };
614
+ });
544
615
  for (const entry of rankedCandidates.slice(maxFiles)) omitted.push({ path: entry.path, reason: "max_files" });
545
616
 
546
617
  let remainingSnippets = maxSnippets;
@@ -595,6 +666,26 @@ export function buildControllerContextPack(
595
666
  omitted.push({ path: "<none>", reason: "No description, search_terms, issue/task focus, or known_paths produced search terms." });
596
667
  }
597
668
 
669
+ const rawSnippetTruncated = files.some((file) => file.snippets.some((snippet) => snippet.truncated));
670
+ const expansionSignals = [
671
+ ...(files.length === 0 ? ["no_current_raw_source"] : []),
672
+ ...(rawSnippetTruncated ? ["raw_snippet_truncated"] : []),
673
+ ...(searchTruncated ? ["candidate_search_truncated"] : []),
674
+ ...(structuralContext.truncated ? ["structural_context_truncated"] : []),
675
+ ...(structuralContext.status === "stale" ? ["structural_context_stale"] : []),
676
+ ...(structuralMode === "required" && !structuralContext.requiredSatisfied ? ["required_structural_context_unavailable"] : []),
677
+ ...(policyDeniedFiles > 0 || deniedPaths.length > 0 ? ["policy_denied_candidates"] : []),
678
+ ...(skippedLargeFiles > 0 ? ["large_candidates_skipped"] : []),
679
+ ...impactCoverage.flatMap((coverage) => coverage.status === "no_evidence"
680
+ ? [`impact_domain_without_evidence:${coverage.domain}`]
681
+ : coverage.status === "matched_not_selected"
682
+ ? [`impact_domain_not_selected:${coverage.domain}`]
683
+ : []),
684
+ ];
685
+ const rawCodeRequiredForImplementation = retrievalMode !== "implementation"
686
+ || files.length === 0
687
+ || rawSnippetTruncated;
688
+
598
689
  return {
599
690
  schemaVersion: CONTEXT_PACK_SCHEMA_VERSION,
600
691
  generatedAt: new Date().toISOString(),
@@ -609,7 +700,9 @@ export function buildControllerContextPack(
609
700
  goal,
610
701
  git,
611
702
  search: {
612
- terms,
703
+ terms: searchQueries,
704
+ impactDomains,
705
+ impactCoverage,
613
706
  includeGlobs: searchIncludeGlobs,
614
707
  excludeGlobs,
615
708
  scannedFiles,
@@ -628,13 +721,22 @@ export function buildControllerContextPack(
628
721
  checks: taskChecks,
629
722
  },
630
723
  contextContract: {
631
- strategy: "Use this pack for scoped investigation only. Do not treat search ranking or summaries as proof of the correct implementation.",
632
- rawCodeRequiredForImplementation: true,
724
+ strategy: retrievalMode === "implementation"
725
+ ? "Use the returned current raw snippets plus bounded impact evidence as the implementation decision input. ChatGPT owns semantic sufficiency; do not re-read source solely because it arrived through search."
726
+ : "Use this pack as investigation evidence and deliberately expand the evidence surface when the selected mode requires planning, debugging, or review.",
727
+ retrievalMode,
728
+ semanticSufficiencyAuthority: "chatgpt",
729
+ rawCodeRequiredForImplementation,
730
+ expansionSignals,
633
731
  notes: [
634
- "The pack includes bounded raw snippets around explicit paths, text search hits, and any policy-approved structural candidates; expand exact files/ranges before editing important code.",
732
+ retrievalMode === "implementation"
733
+ ? "The pack already contains policy-approved current raw source snippets with file SHA identities. Request wider/exact ranges only when ChatGPT judges the impact surface ambiguous or an expansion signal makes the raw evidence mechanically incomplete."
734
+ : "Investigation modes may intentionally expand exact ranges, structural relationships, tests, and neighboring modules before any implementation decision.",
735
+ "Search/CodeGraph ranking is discovery evidence, not a business-semantics authority. Forge never decides that the returned files are the complete semantic impact surface.",
736
+ impactDomains.length > 0 ? `Impact domains were selected by ChatGPT and expanded mechanically in this same retrieval call: ${impactDomains.join(", ")}. Missing or omitted domain evidence is an ambiguity signal, not proof that the domain is irrelevant.` : "No explicit cross-cutting impact domains were requested; ChatGPT may add them when state, scheduling, notifications, events, caching, API, or concurrency could materially change the implementation.",
635
737
  "CodeGraph structural evidence is discovery evidence. Raw source access still passes through Forge repository policy and current-file reads.",
636
738
  structuralContext.status === "stale" ? "CodeGraph reports stale structural evidence. Treat graph relationships as hints and prefer the returned current raw source for changed files." : structuralContext.status === "unavailable" || structuralContext.status === "degraded" ? "Structural context was unavailable or degraded; bounded text search remains the fallback." : structuralContext.status === "ready" ? "CodeGraph structural context was queried read-only with index sync disabled." : "Structural context was not requested.",
637
- "Run focused validation after patching; a context pack is not a substitute for tests, typecheck, diff review, or source review.",
739
+ "Run focused validation after a coherent edit batch; expensive full-suite checks belong at candidate/release boundaries unless debugging specifically needs them earlier.",
638
740
  taskChecks.length > 0 ? `Task checks advertised by the board: ${taskChecks.join(", ")}.` : "No task-specific checks were found in the compact ledger.",
639
741
  ],
640
742
  },
@@ -643,10 +745,12 @@ export function buildControllerContextPack(
643
745
  ? ["Structural context was required but is not current/ready. Refresh CodeGraph explicitly or continue only with the degraded-plan warning visible."]
644
746
  : []),
645
747
  files.length > 0
646
- ? "Inspect the returned snippets and request exact wider ranges for files that will be edited."
748
+ ? retrievalMode === "implementation"
749
+ ? "ChatGPT should decide whether the impact coverage is sufficient; if yes, edit directly from the returned current raw snippets and SHA identities instead of issuing a mandatory second read."
750
+ : "Expand exact ranges or neighboring evidence when that investigation can materially change the plan, diagnosis, or review conclusion."
647
751
  : "Provide known_paths or narrower search_terms before attempting implementation.",
648
- "After editing, review diff projection plus the raw diff for the edited files.",
649
- "Use targeted validation before accepting or merging the change.",
752
+ "After editing, review the bounded diff/evidence for the coherent edit batch.",
753
+ "Use targeted validation before acceptance; defer expensive full-suite validation to a stable candidate or release boundary unless the task specifically requires it earlier.",
650
754
  ],
651
755
  };
652
756
  }
@@ -71,7 +71,7 @@ export interface ControllerOperationalPlan {
71
71
  source: "task-recovery-loop";
72
72
  continuationState: string;
73
73
  nextActions: string[];
74
- handoffArtifacts: string[];
74
+ recoveryArtifacts: string[];
75
75
  };
76
76
  }
77
77
 
@@ -173,7 +173,7 @@ export function buildControllerOperationalPlan(repoRoot: string, ledger: TaskLed
173
173
  label: "Fresh-session recovery",
174
174
  when: "A controller session restarts or loses conversation state.",
175
175
  steps: ["load task ledger", "read operational plan", "select continuation state", "expand only needed context", "resume or ask for the one missing decision"],
176
- requiredEvidence: ["taskLedger", "operationalPlan", "handoffArtifacts"],
176
+ requiredEvidence: ["taskLedger", "operationalPlan", "recoveryArtifacts"],
177
177
  },
178
178
  ],
179
179
  },
@@ -195,7 +195,7 @@ export function buildControllerOperationalPlan(repoRoot: string, ledger: TaskLed
195
195
  },
196
196
  mcpToolSchemaConvergence: {
197
197
  source: "mcp-tool-schema-convergence",
198
- readModels: ["controller_context", "controller_context_pack", "work_status_digest", "prepare_handoff_artifacts"],
198
+ readModels: ["controller_context", "controller_context_pack", "work_status_digest", "prepare_transfer_artifacts"],
199
199
  commandModels: ["work_submit", "run_check", "dispatch_task", "verify_task", "accept_task", "repository_safe_patch_apply"],
200
200
  compatibilityRules: [
201
201
  "compact reads are default",
@@ -220,7 +220,7 @@ export function buildControllerOperationalPlan(repoRoot: string, ledger: TaskLed
220
220
  source: "task-recovery-loop",
221
221
  continuationState: ledger.status.kind,
222
222
  nextActions: ledger.suggestedNextActions.slice(0, 6),
223
- handoffArtifacts: [".ai/harness/controller/task-ledger.json", ".ai/harness/handoff/controller-current.md"],
223
+ recoveryArtifacts: [".ai/harness/controller/task-ledger.json", ".ai/harness/projections/controller-task-ledger.md"],
224
224
  },
225
225
  };
226
226
  }
@@ -7,7 +7,7 @@ import { resolveRepoPreferredControllerHome } from "../repositories/controller-h
7
7
  import { buildRequirementBoard } from "../../runtime/control-plane/facade/requirement-board";
8
8
 
9
9
  const LEDGER_JSON_PATH = ".ai/harness/controller/task-ledger.json";
10
- const LEDGER_HANDOFF_PATH = ".ai/harness/handoff/controller-current.md";
10
+ const LEDGER_PROJECTION_PATH = ".ai/harness/projections/controller-task-ledger.md";
11
11
  const LEDGER_SCHEMA_VERSION = 2;
12
12
 
13
13
  export interface TaskLedgerTaskProjection {
@@ -539,7 +539,7 @@ function renderTaskRow(task: TaskLedgerTaskProjection): string {
539
539
  return `| ${tableEscape(task.issueId)} | ${tableEscape(task.taskId)} | ${tableEscape(task.title)} | ${tableEscape(task.effectiveStatus)} | ${tableEscape(task.verificationStatus)} | ${tableEscape(task.latestRunStatus ?? task.activeRunStatus ?? "")} |`;
540
540
  }
541
541
 
542
- export function renderControllerTaskLedgerHandoff(projection: TaskLedgerProjection, reason: string): string {
542
+ export function renderControllerTaskLedgerProjection(projection: TaskLedgerProjection, reason: string): string {
543
543
  const current = projection.currentIssueId
544
544
  ? projection.issues.find((issue) => issue.id === projection.currentIssueId)
545
545
  : undefined;
@@ -547,7 +547,7 @@ export function renderControllerTaskLedgerHandoff(projection: TaskLedgerProjecti
547
547
  ? projection.attention.map(renderTaskRow)
548
548
  : ["| - | - | No attention tasks | - | - | - |"];
549
549
  return [
550
- "# Controller Task Ledger Handoff",
550
+ "# Controller Task Ledger Projection",
551
551
  "",
552
552
  `> **Generated**: ${projection.generatedAt}`,
553
553
  `> **Reason**: ${reason}`,
@@ -608,25 +608,25 @@ function artifactPreview(repoRoot: string, path: string): TaskLedgerArtifactPrev
608
608
 
609
609
  export function writeControllerTaskLedgerArtifacts(repoRoot: string, input: { reason?: unknown } = {}): WrittenTaskLedgerArtifacts {
610
610
  if (legacyIssueCutoverState(repoRoot).retired) {
611
- throw new Error("LEGACY_TASK_LEDGER_WRITES_RETIRED: task-ledger and handoff files are frozen after SQLite cutover.");
611
+ throw new Error("LEGACY_TASK_LEDGER_WRITES_RETIRED: task-ledger projection files are frozen after SQLite cutover.");
612
612
  }
613
613
  const reason = String(input.reason ?? "manual").trim() || "manual";
614
614
  const projection = buildControllerTaskLedgerProjection(repoRoot);
615
615
  const jsonPath = join(repoRoot, LEDGER_JSON_PATH);
616
- const handoffPath = join(repoRoot, LEDGER_HANDOFF_PATH);
616
+ const projectionPath = join(repoRoot, LEDGER_PROJECTION_PATH);
617
617
  mkdirSync(dirname(jsonPath), { recursive: true });
618
- mkdirSync(dirname(handoffPath), { recursive: true });
618
+ mkdirSync(dirname(projectionPath), { recursive: true });
619
619
  writeFileSync(jsonPath, `${JSON.stringify(projection, null, 2)}\n`, "utf-8");
620
- writeFileSync(handoffPath, renderControllerTaskLedgerHandoff(projection, reason), "utf-8");
620
+ writeFileSync(projectionPath, renderControllerTaskLedgerProjection(projection, reason), "utf-8");
621
621
  return {
622
622
  projection,
623
623
  artifacts: [
624
624
  artifactPreview(repoRoot, LEDGER_JSON_PATH),
625
- artifactPreview(repoRoot, LEDGER_HANDOFF_PATH),
625
+ artifactPreview(repoRoot, LEDGER_PROJECTION_PATH),
626
626
  ],
627
627
  };
628
628
  }
629
629
 
630
- export function controllerTaskLedgerArtifactPaths(): { json: string; handoff: string } {
631
- return { json: LEDGER_JSON_PATH, handoff: LEDGER_HANDOFF_PATH };
630
+ export function controllerTaskLedgerArtifactPaths(): { json: string; projection: string } {
631
+ return { json: LEDGER_JSON_PATH, projection: LEDGER_PROJECTION_PATH };
632
632
  }
@@ -6,8 +6,8 @@ import {
6
6
  type RoutePolicyInput,
7
7
  } from '../../runtime/control-plane/routing/route-policy';
8
8
 
9
- export type WorkMode = 'direct_edit' | 'bounded_work' | 'quick_agent' | 'issue_task' | 'campaign';
10
- export type ExecutionPathPreference = 'fast' | 'durable' | 'campaign';
9
+ export type WorkMode = 'direct_edit' | 'bounded_work' | 'quick_agent' | 'issue_task';
10
+ export type ExecutionPathPreference = 'fast' | 'durable';
11
11
  export type EffectiveTaskMode = ExplicitTaskMode | 'bounded';
12
12
 
13
13
  export interface WorkModeAssessmentInput {
@@ -38,7 +38,6 @@ export interface WorkModeAssessment {
38
38
  reasons: string[];
39
39
  nextTools: string[];
40
40
  issueRequired: boolean;
41
- campaignRequired: boolean;
42
41
  taskMode: EffectiveTaskMode;
43
42
  explicitMode: ExplicitTaskMode | null;
44
43
  modeBehavior: {
@@ -57,7 +56,7 @@ export function parseExplicitTaskMode(value: unknown): ExplicitTaskMode | undefi
57
56
  if (typeof value !== 'string') return undefined;
58
57
  if (!value) return undefined;
59
58
  const normalized = value.replace(/^-/, '') as ExplicitTaskMode;
60
- return ['direct', 'plan', 'debug', 'review', 'campaign', 'release', 'scale'].includes(normalized)
59
+ return ['direct', 'plan', 'debug', 'review', 'release', 'scale'].includes(normalized)
61
60
  ? normalized
62
61
  : undefined;
63
62
  }
@@ -80,10 +79,6 @@ function behaviorFor(mode: EffectiveTaskMode): WorkModeAssessment['modeBehavior'
80
79
  structuralContext: 'off', mutationPhase: 'read_only', issueRequired: false, planRequired: false, worktreeRequired: false,
81
80
  workflow: ['read diff and affected source', 'review correctness and regressions', 'review lifecycle, concurrency, and security', 'report test gaps without mutation'],
82
81
  };
83
- case 'campaign': return {
84
- structuralContext: 'off', mutationPhase: 'coordinate', issueRequired: false, planRequired: false, worktreeRequired: true,
85
- workflow: ['decompose independent deliverables', 'create campaign', 'schedule isolated Tasks', 'reconcile and review campaign evidence'],
86
- };
87
82
  case 'release': return {
88
83
  structuralContext: 'off', mutationPhase: 'release_gate', issueRequired: false, planRequired: false, worktreeRequired: false,
89
84
  workflow: ['verify checks and changelog', 'build immutable complete release', 'run integration and deployment gates', 'activate through Recovery', 'verify rollback evidence'],
@@ -100,7 +95,6 @@ function behaviorFor(mode: EffectiveTaskMode): WorkModeAssessment['modeBehavior'
100
95
  }
101
96
 
102
97
  function nextTools(decision: RouteDecision, investigation: boolean): string[] {
103
- if (decision.workMode === 'campaign') return ['create_campaign', 'add_campaign_task', 'reconcile_campaign', 'get_campaign_review_packet'];
104
98
  if (decision.workMode === 'issue_task') return ['inspect_issue_readiness', 'create_issue or append_task', 'dispatch_task', 'verify_task', 'accept_task'];
105
99
  if (decision.workMode === 'quick_agent') return ['search_repository', 'rh_work(operation=delegate)', 'rh_work(operation=continue)', 'rh_work(operation=verify)'];
106
100
  if (decision.workMode === 'bounded_work') return [
@@ -129,7 +123,7 @@ export function assessWorkMode(input: WorkModeAssessmentInput): WorkModeAssessme
129
123
  const explicitMode = parseExplicitTaskMode(input.explicitMode);
130
124
  const investigationMode = explicitMode === 'plan' || explicitMode === 'debug' || explicitMode === 'review';
131
125
  const readonlyMode = investigationMode;
132
- const parallelMode = explicitMode === 'campaign' || explicitMode === 'scale';
126
+ const parallelMode = explicitMode === 'scale';
133
127
  const routeDecision = decideRoute(input.routePolicyInput ?? {
134
128
  intent: {
135
129
  objective: input.description,
@@ -160,7 +154,7 @@ export function assessWorkMode(input: WorkModeAssessmentInput): WorkModeAssessme
160
154
  },
161
155
  });
162
156
  const taskMode: EffectiveTaskMode = explicitMode
163
- ?? (routeDecision.workMode === 'campaign' ? 'campaign' : routeDecision.workMode === 'direct_edit' ? 'direct' : 'bounded');
157
+ ?? (routeDecision.workMode === 'direct_edit' ? 'direct' : 'bounded');
164
158
  const modeBehavior = behaviorFor(taskMode);
165
159
  return {
166
160
  recommendedMode: routeDecision.workMode,
@@ -174,10 +168,9 @@ export function assessWorkMode(input: WorkModeAssessmentInput): WorkModeAssessme
174
168
  : explicitMode === 'release'
175
169
  ? ['run_check', 'request_release_gate', 'capability_recovery_plan', 'capability_recovery_apply', 'controller_ready']
176
170
  : explicitMode === 'scale'
177
- ? ['create_campaign', 'process_start', 'process_wait', 'benchmark evidence']
171
+ ? ['rh_work(operation=plan_create)', 'rh_work(operation=start)', 'process_get', 'process_wait', 'benchmark evidence']
178
172
  : nextTools(routeDecision, input.requiresInvestigation === true || investigationMode || (input.knownPaths?.length ?? 0) === 0),
179
173
  issueRequired: routeDecision.workMode === 'issue_task',
180
- campaignRequired: routeDecision.workMode === 'campaign',
181
174
  taskMode,
182
175
  explicitMode: explicitMode ?? null,
183
176
  modeBehavior,
package/src/cli/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * Forge CLI entry with forge compatibility aliases.
3
+ * Forge CLI entry. Public commands expose product workflows; compatibility and host-dispatch commands stay hidden.
4
4
  *
5
5
  * Wires commander.js to the global runtime bootstrap, repo-local update,
6
6
  * hook adapter, status, doctor, migrate, security, and tool command bodies.
@@ -13,7 +13,7 @@ import { runInit, runInteractiveInit, type InitBrainMode } from './commands/init
13
13
  import { runHook } from './commands/hook';
14
14
  import { CLI_VERSION, formatStatus, runStatus } from './commands/status';
15
15
  import { formatDoctor, runDoctor } from './commands/doctor';
16
- import { buildInitHookCommand, buildSetupCommand, formatInitHook, runInitHook } from './commands/init-hook';
16
+ import { buildSetupCommand, formatInitHook, runInitHook } from './commands/init-hook';
17
17
  import { formatMigratePlan, runMigrate } from './commands/migrate';
18
18
  import { buildToolsCommand } from './commands/tools';
19
19
  import { buildPluginCommand } from './commands/plugin';
@@ -23,7 +23,6 @@ import { buildDocsCommand } from './commands/docs';
23
23
  import { buildMcpCommand } from './commands/mcp';
24
24
  import { buildChatgptCommand } from './commands/chatgpt';
25
25
  import { buildRunCommand } from './commands/run';
26
- import { buildControllerCommand } from './commands/controller';
27
26
  import { buildRepositoryCommand } from './commands/repository';
28
27
  import { buildRuntimeCommand } from './commands/runtime';
29
28
  import { buildRecoveryCommand } from './commands/recovery';
@@ -41,8 +40,6 @@ import { bootstrapManagedRuntimeEnv } from '../runtime/shared/managed-env';
41
40
  bootstrapManagedRuntimeEnv();
42
41
 
43
42
  export const SUBCOMMANDS = [
44
- 'init',
45
- 'init-hook',
46
43
  'install',
47
44
  'uninstall',
48
45
  'hook',
@@ -76,7 +73,7 @@ interface GlobalRuntimeCommandOptions {
76
73
  cli?: boolean;
77
74
  syncSkill?: boolean;
78
75
  hooks?: string | false;
79
- externalSkills?: boolean;
76
+ withExternalSkills?: boolean;
80
77
  codegraph?: boolean;
81
78
  brainRoot?: string;
82
79
  json?: boolean;
@@ -94,7 +91,7 @@ function runGlobalRuntimeBootstrap(commandName: 'init' | 'install', rawOpts: Glo
94
91
  installCli: rawOpts.cli !== false,
95
92
  syncSkill: rawOpts.syncSkill !== false,
96
93
  hostAdapters: rawOpts.hooks !== false,
97
- externalSkills: rawOpts.externalSkills !== false,
94
+ externalSkills: rawOpts.withExternalSkills === true,
98
95
  codegraph: rawOpts.codegraph !== false,
99
96
  brainRoot: rawOpts.brainRoot,
100
97
  });
@@ -114,21 +111,6 @@ export function buildProgram(): Command {
114
111
  .addHelpText('after', '\nGlobal shortcuts:\n -V, --version output the version number')
115
112
  .exitOverride();
116
113
 
117
- program
118
- .command('init')
119
- .description('Install the Forge CLI, global hook adapters, and required runtime dependencies')
120
- .option('--target <target>', `Host target for adapters and runtime skills: ${VALID_TARGETS.join('|')}`, 'both')
121
- .option('--no-cli', 'Skip installing the Forge CLI globally')
122
- .option('--no-sync-skill', 'Skip refreshing legacy forge skill aliases under host skill roots')
123
- .option('--no-hooks', 'Skip global hook adapter installation')
124
- .option('--no-external-skills', 'Skip Waza, Mermaid, and cross-review (codex-review/claude-review) skill bootstrap')
125
- .option('--no-codegraph', 'Skip CodeGraph CLI/MCP configuration')
126
- .option('--brain-root <path>', 'Brain vault root to persist for Forge brain commands')
127
- .option('--refresh', 'Compatibility no-op; init already refreshes the idempotent user-level runtime')
128
- .option('--json', 'Output JSON instead of human-readable text')
129
- .action((rawOpts: GlobalRuntimeCommandOptions & { refresh?: boolean }) => {
130
- runGlobalRuntimeBootstrap('init', rawOpts);
131
- });
132
114
 
133
115
  program
134
116
  .command('update')
@@ -140,7 +122,7 @@ export function buildProgram(): Command {
140
122
  .option('--check-updates', 'Include network-backed version update advisories in setup check output')
141
123
  .option('--no-runtime-refresh', 'Skip runtime refresh and run the read-only setup check only')
142
124
  .option('--no-cli', 'Skip installing the Forge CLI globally')
143
- .option('--no-sync-skill', 'Skip refreshing legacy forge skill aliases under host skill roots')
125
+ .option('--no-sync-skill', 'Skip refreshing Forge host skill aliases')
144
126
  .option('--no-hooks', 'Skip global hook adapter installation')
145
127
  .option('--with-external-skills', 'Also bootstrap third-party Waza, Mermaid, and cross-review skills')
146
128
  .option('--no-external-skills', 'Compatibility no-op; update no longer bootstraps third-party skills by default')
@@ -164,7 +146,6 @@ export function buildProgram(): Command {
164
146
  syncSkill?: boolean;
165
147
  hooks?: string | false;
166
148
  withExternalSkills?: boolean;
167
- externalSkills?: boolean;
168
149
  codegraph?: boolean;
169
150
  configureCodegraph?: boolean;
170
151
  brainRoot?: string;
@@ -220,7 +201,7 @@ export function buildProgram(): Command {
220
201
 
221
202
  program
222
203
  .command('adopt')
223
- .description('Install or refresh the repo-local harness workflow in an existing repo')
204
+ .description('Install or refresh the repo-local Forge workflow in an existing repo')
224
205
  .argument('[action]', 'Optional action: rollback')
225
206
  .option('--repo <path>', 'Target repository path (defaults to cwd)')
226
207
  .option('--archive <path>', 'Runtime reclaim archive to restore when action is rollback')
@@ -250,7 +231,7 @@ export function buildProgram(): Command {
250
231
  target: string;
251
232
  syncSkill?: boolean;
252
233
  hostAdapters?: boolean;
253
- externalSkills?: boolean;
234
+ withExternalSkills?: boolean;
254
235
  verify?: boolean;
255
236
  codegraph?: boolean;
256
237
  reclaimRuntime?: boolean;
@@ -409,9 +390,9 @@ export function buildProgram(): Command {
409
390
  .option('--target <target>', `Target host: ${VALID_TARGETS.join('|')}`, 'both')
410
391
  .option('--location <location>', `Adapter-only install location: ${VALID_LOCATIONS.join('|')}`)
411
392
  .option('--no-cli', 'Skip installing the Forge CLI globally')
412
- .option('--no-sync-skill', 'Skip refreshing legacy forge skill aliases under host skill roots')
393
+ .option('--no-sync-skill', 'Skip refreshing Forge host skill aliases')
413
394
  .option('--no-hooks', 'Skip global hook adapter installation during full runtime install')
414
- .option('--no-external-skills', 'Skip Waza, Mermaid, and cross-review (codex-review/claude-review) skill bootstrap')
395
+ .option('--with-external-skills', 'Also install optional third-party Waza/Mermaid skills and Forge cross-review helpers')
415
396
  .option('--no-codegraph', 'Skip CodeGraph CLI/MCP configuration')
416
397
  .option('--brain-root <path>', 'Brain vault root to persist for Forge brain commands')
417
398
  .option('--json', 'Output JSON instead of human-readable text')
@@ -466,7 +447,7 @@ export function buildProgram(): Command {
466
447
  });
467
448
 
468
449
  program
469
- .command('hook')
450
+ .command('hook', { hidden: true })
470
451
  .description('Dispatch a hook event to opt-in repo .ai/hooks/<script>')
471
452
  .argument('<event>', 'Hook event name')
472
453
  .requiredOption('--route <route>', 'Route id (default, edit, bash, always)')
@@ -498,11 +479,10 @@ export function buildProgram(): Command {
498
479
  process.exit(report.summary.fail > 0 ? 1 : 0);
499
480
  });
500
481
 
501
- program.addCommand(buildInitHookCommand());
502
482
  program.addCommand(buildSetupCommand());
503
483
 
504
484
  program
505
- .command('migrate')
485
+ .command('migrate', { hidden: true })
506
486
  .description('Migrate legacy project-level hook adapters to the global CLI (dry-run by default)')
507
487
  .option('--apply', 'Commit changes (default is dry-run)')
508
488
  .option('--json', 'Output JSON plan')
@@ -529,13 +509,12 @@ export function buildProgram(): Command {
529
509
 
530
510
  program.addCommand(buildToolsCommand());
531
511
  program.addCommand(buildPluginCommand());
532
- program.addCommand(buildBrainCommand());
533
- program.addCommand(buildCapabilityContextCommand());
512
+ program.addCommand(buildBrainCommand(), { hidden: true });
513
+ program.addCommand(buildCapabilityContextCommand(), { hidden: true });
534
514
  program.addCommand(buildDocsCommand());
535
515
  program.addCommand(buildMcpCommand());
536
516
  program.addCommand(buildChatgptCommand());
537
- program.addCommand(buildRunCommand());
538
- program.addCommand(buildControllerCommand());
517
+ program.addCommand(buildRunCommand(), { hidden: true });
539
518
  program.addCommand(buildRepositoryCommand());
540
519
  program.addCommand(buildRuntimeCommand());
541
520
  program.addCommand(buildRecoveryCommand());
@@ -28,7 +28,7 @@ export const EXPECTED_FACADE_TOOLS = [...PREFERRED_FACADE_TOOL_NAMES] as const;
28
28
  /**
29
29
  * Interactive development tools in the bounded default schema. The historical
30
30
  * core and advanced labels expose the same bounded default surface; git / issue /
31
- * campaign atomics live in the `full` compatibility profile.
31
+ * legacy atomics live in the `full` compatibility profile.
32
32
  */
33
33
  export const OPTIONAL_INTERACTIVE_DEVELOPMENT_TOOLS = [
34
34
  'repository_safe_patch_apply',
@@ -0,0 +1,17 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { dirname, join } from "path";
3
+
4
+ export type ConsoleAssetName = "app.js" | "app.css";
5
+
6
+ export function resolveConsoleAssetPath(
7
+ asset: ConsoleAssetName,
8
+ executablePath = process.execPath,
9
+ ): string | URL {
10
+ const releaseAssetPath = join(dirname(executablePath), "ui-dist", asset);
11
+ if (existsSync(releaseAssetPath)) return releaseAssetPath;
12
+ return new URL(`./ui-dist/${asset}`, import.meta.url);
13
+ }
14
+
15
+ export function readConsoleAsset(asset: ConsoleAssetName): string {
16
+ return readFileSync(resolveConsoleAssetPath(asset), "utf8");
17
+ }