@moretea-labs/forge 1.5.0-rc.1 → 1.5.0

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 (259) hide show
  1. package/AGENTS.md +9 -11
  2. package/CHANGELOG.md +19 -0
  3. package/CLAUDE.md +8 -10
  4. package/DESIGN.md +278 -0
  5. package/README.md +70 -90
  6. package/README.zh-CN.md +50 -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/templates/helpers/archive-workflow.sh +0 -1
  28. package/assets/templates/helpers/capture-plan.sh +3 -3
  29. package/assets/templates/helpers/check-task-workflow.sh +2 -2
  30. package/assets/templates/helpers/codex-handoff-resume.sh +5 -5
  31. package/assets/templates/helpers/ensure-task-workflow.sh +37 -35
  32. package/assets/templates/helpers/new-plan.sh +1 -1
  33. package/assets/templates/helpers/plan-to-todo.sh +2 -2
  34. package/assets/templates/helpers/prepare-codex-handoff.sh +2 -2
  35. package/assets/templates/helpers/prepare-handoff.sh +9 -9
  36. package/assets/templates/helpers/refresh-current-status.sh +2 -2
  37. package/assets/templates/helpers/ship-worktrees.sh +1 -1
  38. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  39. package/assets/templates/helpers/verify-sprint.sh +7 -7
  40. package/assets/templates/helpers/workstream-sync.sh +1 -1
  41. package/assets/templates/plan.template.md +1 -1
  42. package/assets/workflow-contract.v1.json +6 -3
  43. package/docs/architecture/current/README.md +10 -9
  44. package/docs/architecture/current/approved-target-architecture.zh-CN.md +2 -2
  45. package/docs/architecture/current/architecture-invariants.md +2 -2
  46. package/docs/architecture/current/automation-and-schedule-engine.md +15 -15
  47. package/docs/architecture/current/codegraph-context-provider.md +2 -2
  48. package/docs/architecture/current/control-plane-authority-inventory.md +12 -0
  49. package/docs/architecture/current/dispatch-and-agent-strategy.md +11 -11
  50. package/docs/architecture/current/entity-model.md +0 -27
  51. package/docs/architecture/current/failure-recovery.md +0 -24
  52. package/docs/architecture/current/governance.md +1 -1
  53. package/docs/architecture/current/implementation-status.md +1 -1
  54. package/docs/architecture/current/legacy-governance.md +58 -0
  55. package/docs/architecture/current/multi-repository-execution.md +1 -1
  56. package/docs/architecture/current/peer-supercontroller-runtime.md +1 -1
  57. package/docs/architecture/current/personal-assistant-plugin-baseline.md +4 -5
  58. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -10
  59. package/docs/architecture/current/target-requirements-traceability.md +2 -2
  60. package/docs/architecture/current/thin-harness-governance-policy.md +1 -1
  61. package/docs/architecture/current/thin-harness-v1.md +9 -11
  62. package/docs/architecture/domains/runtime-harness.md +1 -1
  63. package/docs/forge-autonomous-goal-loop.md +3 -1
  64. package/docs/forge-chatgpt-mcp-setup.md +51 -184
  65. package/docs/images/forge-banner-cn.svg +26 -1
  66. package/docs/images/forge-banner.svg +26 -1
  67. package/docs/operations/features.md +1 -1
  68. package/docs/operations/homebrew.md +3 -3
  69. package/docs/operations/mcp-tool-exposure.md +5 -5
  70. package/docs/operations/platform-support.md +45 -37
  71. package/docs/operations/platform-support.zh-CN.md +40 -38
  72. package/docs/operations/releasing.md +3 -3
  73. package/docs/operations/releasing.zh-CN.md +3 -3
  74. package/docs/operations/troubleshooting.md +11 -6
  75. package/docs/operations/troubleshooting.zh-CN.md +11 -6
  76. package/docs/public-usage-guide.md +28 -21
  77. package/docs/public-usage-guide.zh-CN.md +28 -21
  78. package/docs/tutorials/01-install-and-start.md +56 -35
  79. package/docs/tutorials/01-install-and-start.zh-CN.md +56 -35
  80. package/docs/tutorials/02-connect-chatgpt.md +112 -46
  81. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +113 -45
  82. package/docs/wiki/Architecture.md +1 -1
  83. package/docs/wiki/Installation.md +2 -2
  84. package/docs/wiki/Quick-Start.md +1 -1
  85. package/docs/wiki/Work-Lifecycle.md +1 -1
  86. package/install.ps1 +4 -5
  87. package/install.sh +4 -6
  88. package/package.json +15 -5
  89. package/scripts/archive-workflow.sh +0 -1
  90. package/scripts/benchmark-thin-harness-mcp-e2e.ts +27 -14
  91. package/scripts/build-controller-ui.ts +28 -0
  92. package/scripts/capture-plan.sh +4 -4
  93. package/scripts/check-agent-tooling.sh +12 -1
  94. package/scripts/check-platform-support.mjs +2 -2
  95. package/scripts/check-public-docs.sh +16 -5
  96. package/scripts/check-release-readiness.sh +5 -11
  97. package/scripts/check-runtime-architecture.mjs +14 -22
  98. package/scripts/check-tarball-install-smoke.sh +3 -2
  99. package/scripts/check-task-workflow.sh +2 -2
  100. package/scripts/codex-handoff-resume.sh +5 -5
  101. package/scripts/create-project-dirs.sh +3 -3
  102. package/scripts/ensure-task-workflow.sh +45 -35
  103. package/scripts/init-project.sh +4 -4
  104. package/scripts/install-desktop-operator-registration.ts +2 -2
  105. package/scripts/lib/project-init-lib.sh +46 -44
  106. package/scripts/migrate-project-template.sh +7 -4
  107. package/scripts/new-plan.sh +1 -1
  108. package/scripts/plan-to-todo.sh +2 -2
  109. package/scripts/prepare-codex-handoff.sh +2 -2
  110. package/scripts/prepare-handoff.sh +9 -9
  111. package/scripts/refresh-current-status.sh +2 -2
  112. package/scripts/setup-plugins.sh +3 -3
  113. package/scripts/ship-worktrees.sh +1 -1
  114. package/scripts/sprint-backlog.sh +5 -5
  115. package/scripts/sync-codex-installed-copies.sh +2 -3
  116. package/scripts/verify-sprint.sh +7 -7
  117. package/scripts/workstream-sync.sh +1 -1
  118. package/src/cli/chatgpt-browser/bridge-extension.ts +1 -1
  119. package/src/cli/chatgpt-browser/bridge-provider.ts +29 -0
  120. package/src/cli/chatgpt-browser/file-policy.ts +3 -1
  121. package/src/cli/commands/chatgpt.ts +51 -0
  122. package/src/cli/commands/doctor.ts +1 -1
  123. package/src/cli/commands/init-hook.ts +211 -70
  124. package/src/cli/commands/init.ts +5 -6
  125. package/src/cli/commands/mcp.ts +29 -5
  126. package/src/cli/commands/runtime.ts +60 -8
  127. package/src/cli/commands/setup-profile.ts +441 -0
  128. package/src/cli/controller/check-runner.ts +20 -3
  129. package/src/cli/controller/codex-continuation.ts +4 -7
  130. package/src/cli/controller/context-pack.ts +118 -14
  131. package/src/cli/controller/operational-plan.ts +4 -4
  132. package/src/cli/controller/task-ledger.ts +10 -10
  133. package/src/cli/controller/work-mode.ts +6 -13
  134. package/src/cli/index.ts +14 -35
  135. package/src/cli/local-bridge/connector-freshness.ts +1 -1
  136. package/src/cli/local-bridge/console-automations.ts +160 -0
  137. package/src/cli/local-bridge/dashboard.ts +8 -1303
  138. package/src/cli/local-bridge/facade-api.ts +16 -7
  139. package/src/cli/local-bridge/frontend/src/App.tsx +10 -0
  140. package/src/cli/local-bridge/frontend/src/components/Button.tsx +1 -0
  141. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +3 -0
  142. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +1 -0
  143. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +1 -0
  144. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +1 -0
  145. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +3 -0
  146. package/src/cli/local-bridge/frontend/src/design/icons.tsx +11 -0
  147. package/src/cli/local-bridge/frontend/src/design/tokens.css +8 -0
  148. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +3 -0
  149. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +4 -0
  150. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +3 -0
  151. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +2 -0
  152. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +2 -0
  153. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +2 -0
  154. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +3 -0
  155. package/src/cli/local-bridge/frontend/src/lib/api.ts +23 -0
  156. package/src/cli/local-bridge/frontend/src/lib/format.ts +4 -0
  157. package/src/cli/local-bridge/frontend/src/lib/routes.ts +7 -0
  158. package/src/cli/local-bridge/frontend/src/main.tsx +1 -0
  159. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +1 -0
  160. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +2 -0
  161. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +3 -0
  162. package/src/cli/local-bridge/frontend/src/styles.css +12 -0
  163. package/src/cli/local-bridge/frontend/src/types.ts +14 -0
  164. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +1 -0
  165. package/src/cli/local-bridge/frontend/vite.config.ts +27 -0
  166. package/src/cli/local-bridge/server.ts +44 -0
  167. package/src/cli/local-bridge/ui-dist/app.css +1 -0
  168. package/src/cli/local-bridge/ui-dist/app.js +11094 -0
  169. package/src/cli/mcp/instructions.ts +3 -3
  170. package/src/cli/mcp/legacy-tool-service.ts +27 -23
  171. package/src/cli/mcp/multi-repository.ts +2 -0
  172. package/src/cli/mcp/policy.ts +7 -5
  173. package/src/cli/mcp/repository-tools.ts +53 -14
  174. package/src/cli/mcp/server.ts +5 -10
  175. package/src/cli/mcp/setup.ts +211 -275
  176. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  177. package/src/cli/mcp/toolset-names.ts +1 -1
  178. package/src/cli/mcp/toolset.ts +0 -1
  179. package/src/cli/mcp/transports/http.ts +24 -4
  180. package/src/cli/repositories/branch-name-policy.ts +0 -7
  181. package/src/cli/repositories/controller-home.ts +110 -3
  182. package/src/cli/repositories/safe-patch.ts +1 -1
  183. package/src/cli/repositories/selected-path-actions.ts +26 -24
  184. package/src/core/adoption/gitignore-plan.ts +2 -3
  185. package/src/core/adoption/plan.ts +4 -1
  186. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +556 -0
  187. package/src/runtime/control-plane/execution/work-handle-store.ts +128 -1
  188. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +277 -0
  189. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +169 -0
  190. package/src/runtime/control-plane/facade/capability-registry.ts +0 -10
  191. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +44 -25
  192. package/src/runtime/control-plane/facade/suggested-actions.ts +7 -1
  193. package/src/runtime/control-plane/facade/types.ts +0 -1
  194. package/src/runtime/control-plane/global-scheduler/scheduler.ts +30 -8
  195. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +114 -0
  196. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +175 -0
  197. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +161 -0
  198. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +64 -0
  199. package/src/runtime/control-plane/launcher/thin-launcher.ts +117 -24
  200. package/src/runtime/control-plane/routing/route-policy.ts +14 -14
  201. package/src/runtime/control-plane/runtime-cleanup.ts +0 -1
  202. package/src/runtime/evidence/event-ledger.ts +1 -1
  203. package/src/runtime/{workflow/campaigns/workspace.ts → execution/managed-workspace.ts} +41 -43
  204. package/src/runtime/execution/process-runtime/check-facade.ts +6 -0
  205. package/src/runtime/execution/process-runtime/check-result.ts +45 -0
  206. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -16
  207. package/src/runtime/execution/process-runtime/command-facade.ts +4 -2
  208. package/src/runtime/execution/process-runtime/gc.ts +1 -1
  209. package/src/runtime/execution/process-runtime/index.ts +3 -0
  210. package/src/runtime/execution/process-runtime/interactive-admission.ts +14 -5
  211. package/src/runtime/execution/process-runtime/persisted-check.ts +285 -0
  212. package/src/runtime/execution/process-runtime/resource-claims.ts +30 -0
  213. package/src/runtime/execution/process-runtime/runtime.ts +18 -1
  214. package/src/runtime/execution/process-runtime/store.ts +1 -19
  215. package/src/runtime/execution/process-runtime/types.ts +4 -0
  216. package/src/runtime/execution/thin-harness/execution-router.ts +0 -1
  217. package/src/runtime/execution/thin-harness/fast-executor.ts +1 -1
  218. package/src/runtime/execution/thin-harness/lightweight-lanes.ts +1 -2
  219. package/src/runtime/execution/thin-harness/types.ts +0 -1
  220. package/src/runtime/execution/workers/executor.ts +1 -1
  221. package/src/runtime/gateway/mcp/execution-tools.ts +1 -1
  222. package/src/runtime/gateway/mcp/persisted-check-process.ts +3 -255
  223. package/src/runtime/gateway/mcp/process-tools.ts +16 -3
  224. package/src/runtime/gateway/mcp/resource-policy.ts +0 -9
  225. package/src/runtime/gateway/mcp/router.ts +117 -182
  226. package/src/runtime/gateway/mcp/runtime-tools.ts +331 -409
  227. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +1 -219
  228. package/src/runtime/plugins/browser-adapter.ts +5 -0
  229. package/src/runtime/plugins/desktop-operator-registration.ts +1 -1
  230. package/src/runtime/plugins/store.ts +14 -6
  231. package/src/runtime/plugins/types.ts +1 -1
  232. package/src/runtime/projections/materialized-view.ts +1 -23
  233. package/src/runtime/recovery/maintenance-executor.ts +9 -0
  234. package/src/runtime/resources/ownership.ts +0 -1
  235. package/src/runtime/root/gateway-adapter.ts +2 -0
  236. package/src/runtime/root/mcp-transport.ts +10 -2
  237. package/src/runtime/root/package-runtime-release.ts +161 -0
  238. package/src/runtime/root/package-runtime-service.ts +172 -0
  239. package/src/runtime/root/runtime.ts +2 -1
  240. package/src/runtime/root/scheduler.ts +5 -0
  241. package/src/runtime/root/service.ts +18 -5
  242. package/src/runtime/standalone-recovery/core.ts +65 -16
  243. package/src/runtime/workflow/findings/store.ts +145 -106
  244. package/src/runtime/workflow/findings/types.ts +22 -0
  245. package/src/runtime/workflow/schedules/engine.ts +132 -2
  246. package/src/runtime/workflow/schedules/pending-activation.ts +139 -0
  247. package/src/runtime/workflow/schedules/work-continuation.ts +218 -0
  248. package/docs/CHANGELOG.md +0 -978
  249. package/docs/README.zh-CN.md +0 -58
  250. package/scripts/smoke-chatgpt-supervised-automation.ts +0 -208
  251. package/src/cli/commands/controller.ts +0 -680
  252. package/src/runtime/workflow/campaigns/cleanup.ts +0 -263
  253. package/src/runtime/workflow/campaigns/engine.ts +0 -112
  254. package/src/runtime/workflow/campaigns/normalize.ts +0 -100
  255. package/src/runtime/workflow/campaigns/review.ts +0 -315
  256. package/src/runtime/workflow/campaigns/store.ts +0 -526
  257. package/src/runtime/workflow/campaigns/supervisor.ts +0 -112
  258. package/src/runtime/workflow/campaigns/types.ts +0 -290
  259. /package/src/runtime/{workflow/campaigns → execution}/workspace-agent.ts +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `forge init-hook` -- read-only Agent bootstrap checklist.
2
+ * Readiness model behind `forge setup check` and resumable `forge setup`.
3
3
  *
4
4
  * This command intentionally does not install hooks, write user-owned markdown,
5
5
  * or mutate repo-local runtime files. It gathers the existing readiness probes
@@ -16,9 +16,22 @@ import { fileURLToPath } from 'url';
16
16
  import { Command } from 'commander';
17
17
  import { runDoctor, type CheckStatus, type DoctorReport } from './doctor';
18
18
  import { runStatus, type StatusReport } from './status';
19
- import type { InstallTargetSpec } from './install';
20
-
21
- export type InitHookTarget = InstallTargetSpec;
19
+ import { runMcpSetupChatgpt } from '../mcp/setup';
20
+ import {
21
+ configureSetupProfile,
22
+ detectSetupPlatform,
23
+ formatSetupProfile,
24
+ readSetupProfile,
25
+ resolveControllerGuidance,
26
+ resolveRuntimeGuidance,
27
+ resolveTunnelGuidance,
28
+ setupHostTarget,
29
+ type SetupHostTarget,
30
+ type SetupPlatformSnapshot,
31
+ type SetupProfile,
32
+ } from './setup-profile';
33
+
34
+ export type InitHookTarget = SetupHostTarget;
22
35
  export type InitHookStatus = 'ok' | 'attention' | 'blocked';
23
36
  export type InitHookCheckStatus = CheckStatus | 'needs_agent';
24
37
  export type InitHookCheckSource =
@@ -108,7 +121,7 @@ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
108
121
  const REPO_ROOT = resolve(SCRIPT_DIR, '..', '..', '..');
109
122
  const UPDATE_CHECK_ENV = 'FORGE_CHECK_UPDATES';
110
123
  const GLOBAL_RULES_BEGIN = '<!-- BEGIN: forge global-working-rules -->';
111
- const VALID_TARGETS: readonly InitHookTarget[] = ['codex', 'claude', 'both'];
124
+ const VALID_TARGETS: readonly InitHookTarget[] = ['none', 'codex', 'claude', 'both'];
112
125
 
113
126
  function withProcessEnv<T>(env: NodeJS.ProcessEnv | undefined, fn: () => T): T {
114
127
  if (!env) return fn();
@@ -129,6 +142,7 @@ function withProcessEnv<T>(env: NodeJS.ProcessEnv | undefined, fn: () => T): T {
129
142
  }
130
143
 
131
144
  function selectedTargets(target: InitHookTarget): Array<'codex' | 'claude'> {
145
+ if (target === 'none') return [];
132
146
  if (target === 'both') return ['codex', 'claude'];
133
147
  return [target];
134
148
  }
@@ -230,6 +244,19 @@ function parseActionCommand(detail: string): string | undefined {
230
244
  return run?.[1]?.trim();
231
245
  }
232
246
 
247
+ function doctorEntryRelevant(id: string, target: InitHookTarget): boolean {
248
+ if (id === 'cli-on-path' || id === 'cli-version' || id === 'cli-update' || id === 'security-config') return true;
249
+ if (target === 'none') return false;
250
+ if (id === 'repo-hook-scripts' || id === 'codegraph-readiness' || id === 'codegraph-index') return true;
251
+ if (target === 'codex' || target === 'both') {
252
+ if (id === 'codex-adapter' || id === 'codex-trust-state' || id === 'codex-codegraph-mcp') return true;
253
+ }
254
+ if (target === 'claude' || target === 'both') {
255
+ if (id === 'claude-adapter' || id === 'claude-codegraph-mcp') return true;
256
+ }
257
+ return false;
258
+ }
259
+
233
260
  function doctorChecks(
234
261
  report: DoctorReport,
235
262
  target: InitHookTarget,
@@ -238,6 +265,7 @@ function doctorChecks(
238
265
  ): InitHookCheck[] {
239
266
  const checks: InitHookCheck[] = [];
240
267
  for (const entry of report.checks) {
268
+ if (!doctorEntryRelevant(entry.id, target)) continue;
241
269
  const source: InitHookCheckSource = entry.id === 'security-config' ? 'security' : 'doctor';
242
270
  let checkStatus: InitHookCheckStatus = entry.status;
243
271
 
@@ -425,7 +453,7 @@ function commandForToolGap(toolName: string, tool: ToolingTool, target: InitHook
425
453
  return normalizeToolCommand(tool.install_command, target);
426
454
  }
427
455
  if (toolName === 'codex_automation_profile') {
428
- return 'forge init --target codex --no-cli --no-hooks --no-codegraph';
456
+ return 'forge install --target codex --no-cli --no-hooks --no-codegraph';
429
457
  }
430
458
  return normalizeToolCommand(
431
459
  tool.sync_command ?? tool.ensure_command ?? tool.mcp_install_command ?? tool.install_command ?? tool.upgrade_command,
@@ -579,24 +607,26 @@ function overallStatus(summary: InitHookReport['summary']): InitHookStatus {
579
607
  export function runInitHook(opts: InitHookOptions = {}): InitHookReport {
580
608
  const cwd = resolve(opts.cwd ?? process.cwd());
581
609
  const sourceRoot = resolve(opts.sourceRoot ?? REPO_ROOT);
582
- const target = opts.target ?? 'both';
610
+ const target = opts.target ?? 'none';
583
611
  const checkUpdates = opts.checkUpdates === true;
584
612
  const actions: InitHookAction[] = [];
585
613
 
586
614
  const statusReport = opts.statusReport ?? withProcessEnv(opts.env, () => runStatus(cwd));
587
615
  const doctorEnv = { ...(opts.env ?? {}), [UPDATE_CHECK_ENV]: checkUpdates ? '1' : undefined };
588
616
  const doctorReport = opts.doctorReport ?? withProcessEnv(doctorEnv, () => runDoctor(cwd));
589
- const toolingProbe = opts.toolingReport
590
- ? { report: opts.toolingReport, error: undefined }
591
- : collectToolingReport(sourceRoot, cwd, target, checkUpdates, opts.env);
617
+ const toolingProbe = target === 'none'
618
+ ? { report: undefined, error: undefined }
619
+ : opts.toolingReport
620
+ ? { report: opts.toolingReport, error: undefined }
621
+ : collectToolingReport(sourceRoot, cwd, target, checkUpdates, opts.env);
592
622
 
593
623
  const checks: InitHookCheck[] = [
594
- ...statusChecks(statusReport, target, checkUpdates, actions),
624
+ ...(target === 'none' ? [] : statusChecks(statusReport, target, checkUpdates, actions)),
595
625
  ...doctorChecks(doctorReport, target, checkUpdates, actions),
596
- ...globalRulesChecks(target, opts.env, checkUpdates, actions),
597
- ...runtimeCapabilityChecks(toolingProbe.report, target, checkUpdates, actions),
598
- ...toolingChecks(toolingProbe.report, target, checkUpdates, actions, toolingProbe.error),
599
- ...legacyChecks(cwd, target, checkUpdates, actions),
626
+ ...(target === 'none' ? [] : globalRulesChecks(target, opts.env, checkUpdates, actions)),
627
+ ...(target === 'none' ? [] : runtimeCapabilityChecks(toolingProbe.report, target, checkUpdates, actions)),
628
+ ...(target === 'none' ? [] : toolingChecks(toolingProbe.report, target, checkUpdates, actions, toolingProbe.error)),
629
+ ...(target === 'none' ? [] : legacyChecks(cwd, target, checkUpdates, actions)),
600
630
  ];
601
631
 
602
632
  const summary = summarize(checks);
@@ -617,7 +647,7 @@ export function formatInitHook(report: InitHookReport, asJson = false): string {
617
647
  lines.push(`forge setup check: ${report.status}`);
618
648
  lines.push(`target=${report.target}; check-updates=${report.checkUpdates ? 'on' : 'off'}`);
619
649
  lines.push(
620
- `summary: ${report.summary.ok} ok, ${report.summary.warn} warn, ${report.summary.fail} fail, ${report.summary.na} n/a, ${report.summary.needs_agent} needs-agent`,
650
+ `summary: ${report.summary.ok} ok, ${report.summary.warn} warn, ${report.summary.fail} fail, ${report.summary.na} n/a, ${report.summary.needs_agent} needs-action`,
621
651
  );
622
652
  lines.push('');
623
653
  lines.push('Checks:');
@@ -625,7 +655,7 @@ export function formatInitHook(report: InitHookReport, asJson = false): string {
625
655
  lines.push(` [${check.status}] ${check.id}: ${check.detail}`);
626
656
  }
627
657
  lines.push('');
628
- lines.push('Agent actions:');
658
+ lines.push('Actions:');
629
659
  if (report.agent_actions.length === 0) {
630
660
  lines.push(' none');
631
661
  } else {
@@ -640,30 +670,6 @@ export function formatInitHook(report: InitHookReport, asJson = false): string {
640
670
  return lines.join('\n');
641
671
  }
642
672
 
643
- export function buildInitHookCommand(): Command {
644
- const command = new Command('init-hook');
645
- command
646
- .description('Run a read-only Agent bootstrap checklist for user-level Forge readiness')
647
- .option('--target <target>', `Host target to inspect: ${VALID_TARGETS.join('|')}`, 'both')
648
- .option('--check-updates', 'Include network-backed version update advisories')
649
- .option('--json', 'Output JSON instead of human-readable text')
650
- .action((rawOpts: { target: string; checkUpdates?: boolean; json?: boolean }) => {
651
- if (!VALID_TARGETS.includes(rawOpts.target as InitHookTarget)) {
652
- console.error(
653
- `forge init-hook: invalid --target "${rawOpts.target}" (expected: ${VALID_TARGETS.join(', ')})`,
654
- );
655
- process.exit(2);
656
- }
657
- const report = runInitHook({
658
- target: rawOpts.target as InitHookTarget,
659
- checkUpdates: rawOpts.checkUpdates === true,
660
- });
661
- console.log(formatInitHook(report, rawOpts.json === true));
662
- process.exit(report.status === 'blocked' ? 1 : 0);
663
- });
664
- return command;
665
- }
666
-
667
673
  export type SetupSessionStatus = 'open' | 'blocked' | 'ready' | 'closed';
668
674
 
669
675
  export interface SetupSession {
@@ -672,6 +678,8 @@ export interface SetupSession {
672
678
  status: SetupSessionStatus;
673
679
  target: InitHookTarget;
674
680
  checkUpdates: boolean;
681
+ profile?: SetupProfile;
682
+ platform?: SetupPlatformSnapshot;
675
683
  createdAt: string;
676
684
  updatedAt: string;
677
685
  closedAt?: string;
@@ -681,6 +689,10 @@ export interface SetupSession {
681
689
 
682
690
  export interface SetupSessionOptions extends InitHookOptions {
683
691
  setupRoot?: string;
692
+ profile?: SetupProfile;
693
+ platform?: SetupPlatformSnapshot;
694
+ controllerHome?: string;
695
+ accountHome?: string;
684
696
  report?: InitHookReport;
685
697
  now?: () => Date;
686
698
  uuid?: () => string;
@@ -714,28 +726,85 @@ export function readSetupSession(options: Pick<SetupSessionOptions, 'setupRoot'
714
726
  return readSetupSessionAt(setupSessionPath(options));
715
727
  }
716
728
 
717
- function setupStatus(report: InitHookReport): SetupSessionStatus {
729
+ function setupStatus(report: InitHookReport, nextAction?: InitHookAction): SetupSessionStatus {
718
730
  if (report.status === 'blocked') return 'blocked';
719
- if (report.status === 'ok' && report.agent_actions.length === 0) return 'ready';
731
+ if (report.status === 'ok' && report.agent_actions.length === 0 && !nextAction) return 'ready';
720
732
  return 'open';
721
733
  }
722
734
 
735
+ function profileNextAction(profile: SetupProfile | undefined, platform: SetupPlatformSnapshot, options: { controllerHome?: string; accountHome?: string } = {}): InitHookAction | undefined {
736
+ if (!profile) {
737
+ return {
738
+ id: 'controller.select',
739
+ status: 'needs_agent',
740
+ reason: 'Choose the external controller that will act as Forge’s brain. ChatGPT is recommended; Codex, Claude, or another MCP client can be primary instead.',
741
+ requires_agent: true,
742
+ risk: 'Writes only the local Forge setup profile. It does not install or authenticate third-party software.',
743
+ command: 'forge setup configure --controller chatgpt',
744
+ verification: 'forge setup next',
745
+ };
746
+ }
747
+ const controller = resolveControllerGuidance(profile, { controllerHome: options.controllerHome, home: options.accountHome });
748
+ if (controller && !controller.ready) {
749
+ return {
750
+ id: `controller.${controller.controller}.configure`,
751
+ status: 'needs_agent',
752
+ reason: `${controller.title}: ${controller.detail}`,
753
+ requires_agent: true,
754
+ risk: 'Configures only the explicitly selected external controller entry. Unselected Codex/Claude providers remain untouched.',
755
+ ...(controller.command ? { command: controller.command } : {}),
756
+ verification: 'forge setup next',
757
+ };
758
+ }
759
+ const runtime = resolveRuntimeGuidance(profile, { controllerHome: options.controllerHome });
760
+ if (runtime && !runtime.ready) {
761
+ return {
762
+ id: 'runtime.package.install',
763
+ status: 'needs_agent',
764
+ reason: `${runtime.title}: ${runtime.detail}`,
765
+ requires_agent: true,
766
+ risk: 'Installs or diagnoses the user-level Forge Runtime only. Standalone Recovery and source-release activation are not part of normal setup.',
767
+ ...(runtime.command ? { command: runtime.command } : {}),
768
+ verification: 'forge setup next',
769
+ };
770
+ }
771
+ const tunnel = resolveTunnelGuidance(profile, platform, { controllerHome: options.controllerHome });
772
+ if (!tunnel.ready) {
773
+ return {
774
+ id: `tunnel.${tunnel.provider}.configure`,
775
+ status: 'needs_agent',
776
+ reason: `${tunnel.title}: ${tunnel.detail}`,
777
+ requires_agent: true,
778
+ risk: 'Remote connectivity is provider-owned. Forge keeps its MCP listener on loopback; credentials, authentication, and account changes remain explicit.',
779
+ ...(tunnel.command ? { command: tunnel.command } : {}),
780
+ verification: 'forge setup next',
781
+ };
782
+ }
783
+ return undefined;
784
+ }
785
+
723
786
  export function openSetupSession(options: SetupSessionOptions = {}): SetupSession {
724
787
  const path = setupSessionPath(options);
725
788
  const previous = readSetupSessionAt(path);
726
- const report = options.report ?? runInitHook(options);
789
+ const profile = options.profile ?? readSetupProfile(options);
790
+ const platform = options.platform ?? detectSetupPlatform({ env: options.env });
791
+ const target = options.target ?? setupHostTarget(profile);
792
+ const report = options.report ?? runInitHook({ ...options, target });
793
+ const nextAction = profileNextAction(profile, platform, { controllerHome: options.controllerHome, accountHome: options.accountHome }) ?? report.agent_actions[0];
727
794
  const now = (options.now ?? (() => new Date()))().toISOString();
728
795
  const reuse = previous && previous.status !== 'closed';
729
796
  return writeSetupSessionAt(path, {
730
797
  schemaVersion: 1,
731
798
  sessionId: reuse ? previous.sessionId : (options.uuid ?? randomUUID)(),
732
- status: setupStatus(report),
799
+ status: setupStatus(report, nextAction),
733
800
  target: report.target,
734
801
  checkUpdates: report.checkUpdates,
802
+ ...(profile ? { profile } : {}),
803
+ platform,
735
804
  createdAt: reuse ? previous.createdAt : now,
736
805
  updatedAt: now,
737
806
  lastReport: report,
738
- ...(report.agent_actions[0] ? { nextAction: report.agent_actions[0] } : {}),
807
+ ...(nextAction ? { nextAction } : {}),
739
808
  });
740
809
  }
741
810
 
@@ -756,8 +825,9 @@ export function formatSetupSession(session: SetupSession, asJson = false): strin
756
825
  if (asJson) return JSON.stringify(session, null, 2);
757
826
  const lines = [
758
827
  `forge setup: ${session.status}`,
759
- `session=${session.sessionId}; target=${session.target}; updated=${session.updatedAt}`,
760
- `summary: ${session.lastReport.summary.ok} ok, ${session.lastReport.summary.warn} warn, ${session.lastReport.summary.fail} fail, ${session.lastReport.summary.needs_agent} needs-agent`,
828
+ `session=${session.sessionId}; controller=${session.profile?.primaryController ?? 'not-selected'}; host-tooling=${session.target}; updated=${session.updatedAt}`,
829
+ `summary: ${session.lastReport.summary.ok} ok, ${session.lastReport.summary.warn} warn, ${session.lastReport.summary.fail} fail, ${session.lastReport.summary.needs_agent} needs-action`,
830
+ ...(session.profile ? ['', formatSetupProfile(session.profile, session.platform)] : []),
761
831
  ];
762
832
  if (session.nextAction) {
763
833
  lines.push('', `Next: ${session.nextAction.reason}`);
@@ -771,17 +841,50 @@ export function formatSetupSession(session: SetupSession, asJson = false): strin
771
841
  return lines.join('\n');
772
842
  }
773
843
 
774
- function setupTarget(value: string, surface: string): InitHookTarget {
844
+ function setupTarget(value: string | undefined, surface: string): InitHookTarget | undefined {
845
+ if (value === undefined) return undefined;
775
846
  if (!VALID_TARGETS.includes(value as InitHookTarget)) {
776
847
  throw new Error(`${surface}: invalid --target "${value}" (expected: ${VALID_TARGETS.join(', ')})`);
777
848
  }
778
849
  return value as InitHookTarget;
779
850
  }
780
851
 
781
- function setupCycle(rawOpts: { target: string; checkUpdates?: boolean; json?: boolean }, surface: string): void {
852
+ interface SetupCycleRawOptions {
853
+ target?: string;
854
+ controller?: string;
855
+ addController?: string[];
856
+ tunnel?: string;
857
+ endpoint?: string;
858
+ tunnelId?: string;
859
+ checkUpdates?: boolean;
860
+ json?: boolean;
861
+ }
862
+
863
+ function collectOption(value: string, previous: string[] = []): string[] {
864
+ return [...previous, value];
865
+ }
866
+
867
+ function syncExplicitRemoteControllerEndpoint(profile: SetupProfile | undefined, endpoint: string | undefined): void {
868
+ if (!profile || !endpoint || !profile.controllers.some((controller) => controller === 'chatgpt' || controller === 'mcp')) return;
869
+ runMcpSetupChatgpt({ userLevel: true, endpoint });
870
+ }
871
+
872
+ function setupCycle(rawOpts: SetupCycleRawOptions, surface: string): void {
782
873
  try {
874
+ let profile = readSetupProfile();
875
+ if (rawOpts.controller || rawOpts.addController?.length || rawOpts.tunnel || rawOpts.endpoint || rawOpts.tunnelId) {
876
+ profile = configureSetupProfile({
877
+ controller: rawOpts.controller,
878
+ addControllers: rawOpts.addController,
879
+ tunnel: rawOpts.tunnel,
880
+ endpoint: rawOpts.endpoint,
881
+ tunnelId: rawOpts.tunnelId,
882
+ });
883
+ syncExplicitRemoteControllerEndpoint(profile, rawOpts.endpoint);
884
+ }
783
885
  const session = openSetupSession({
784
- target: setupTarget(rawOpts.target, surface),
886
+ profile,
887
+ target: setupTarget(rawOpts.target, surface) ?? setupHostTarget(profile),
785
888
  checkUpdates: rawOpts.checkUpdates === true,
786
889
  });
787
890
  console.log(formatSetupSession(session, rawOpts.json === true));
@@ -792,33 +895,69 @@ function setupCycle(rawOpts: { target: string; checkUpdates?: boolean; json?: bo
792
895
  }
793
896
  }
794
897
 
898
+ function addSetupSelectionOptions(command: Command): Command {
899
+ return command
900
+ .option('--controller <controller>', 'Primary external controller: chatgpt|codex|claude|mcp')
901
+ .option('--add-controller <controller>', 'Also configure another external controller entry (repeatable)', collectOption, [])
902
+ .option('--tunnel <provider>', 'Remote connection: auto|openai|cloudflare|tailscale|existing|none')
903
+ .option('--tunnel-id <id>', 'OpenAI Secure MCP Tunnel id (non-secret tunnel_...)')
904
+ .option('--endpoint <url>', 'Existing/stable public HTTPS URL ending in /mcp')
905
+ .option('--target <target>', `Deprecated host-tooling override: ${VALID_TARGETS.join('|')}`)
906
+ .option('--check-updates', 'Include network-backed version update advisories')
907
+ .option('--json', 'Output JSON instead of human-readable text');
908
+ }
909
+
795
910
  export function buildSetupCommand(): Command {
796
911
  const command = new Command('setup');
797
912
  command
798
- .description('Open or continue one resumable Forge setup session')
799
- .option('--target <target>', `Host target to configure: ${VALID_TARGETS.join('|')}`, 'both')
913
+ .description('Configure Forge around one external controller brain and optional additional controller entries')
800
914
  .option('--check-updates', 'Include network-backed version update advisories')
801
915
  .option('--json', 'Output JSON instead of human-readable text')
802
- .action((rawOpts: { target: string; checkUpdates?: boolean; json?: boolean }) => setupCycle(rawOpts, 'forge setup'));
916
+ .action((rawOpts: SetupCycleRawOptions) => setupCycle(rawOpts, 'forge setup'));
803
917
 
804
918
  for (const name of ['open', 'next'] as const) {
805
- command
919
+ addSetupSelectionOptions(command
806
920
  .command(name)
807
- .description(name === 'open' ? 'Start or resume the verified setup session' : 'Re-check configuration and advance to the next action')
808
- .option('--target <target>', `Host target to configure: ${VALID_TARGETS.join('|')}`, 'both')
809
- .option('--check-updates', 'Include network-backed version update advisories')
810
- .option('--json', 'Output JSON instead of human-readable text')
811
- .action((rawOpts: { target: string; checkUpdates?: boolean; json?: boolean }) => setupCycle(rawOpts, `forge setup ${name}`));
921
+ .description(name === 'open' ? 'Start or resume the guided Forge setup session' : 'Re-check configuration and advance to the next action'))
922
+ .action((rawOpts: SetupCycleRawOptions) => setupCycle(rawOpts, `forge setup ${name}`));
812
923
  }
813
924
 
925
+ command
926
+ .command('configure')
927
+ .description('Choose the primary external controller, optional additional controllers, and public access provider')
928
+ .requiredOption('--controller <controller>', 'Primary external controller: chatgpt|codex|claude|mcp')
929
+ .option('--add-controller <controller>', 'Also configure another external controller entry (repeatable)', collectOption, [])
930
+ .option('--tunnel <provider>', 'Remote connection: auto|openai|cloudflare|tailscale|existing|none')
931
+ .option('--tunnel-id <id>', 'OpenAI Secure MCP Tunnel id (non-secret tunnel_...)')
932
+ .option('--endpoint <url>', 'Existing/stable public HTTPS URL ending in /mcp')
933
+ .option('--json', 'Output JSON instead of human-readable text')
934
+ .action((rawOpts: { controller: string; addController?: string[]; tunnel?: string; tunnelId?: string; endpoint?: string; json?: boolean }) => {
935
+ try {
936
+ const profile = configureSetupProfile({
937
+ controller: rawOpts.controller,
938
+ addControllers: rawOpts.addController,
939
+ tunnel: rawOpts.tunnel,
940
+ endpoint: rawOpts.endpoint,
941
+ tunnelId: rawOpts.tunnelId,
942
+ });
943
+ syncExplicitRemoteControllerEndpoint(profile, rawOpts.endpoint);
944
+ console.log(rawOpts.json ? JSON.stringify(profile, null, 2) : `${formatSetupProfile(profile)}\n\nNext: forge setup next`);
945
+ } catch (error) {
946
+ console.error(error instanceof Error ? error.message : String(error));
947
+ process.exitCode = 2;
948
+ }
949
+ });
950
+
814
951
  command
815
952
  .command('status')
816
- .description('Read the persisted setup session without changing host configuration')
953
+ .description('Read the persisted setup session and external-controller profile')
817
954
  .option('--json', 'Output JSON instead of human-readable text')
818
955
  .action((rawOpts: { json?: boolean }) => {
819
956
  const session = readSetupSession();
957
+ const profile = readSetupProfile();
820
958
  if (!session) {
821
- console.log(rawOpts.json ? JSON.stringify({ status: 'not_started' }) : 'forge setup: not started\nRun: forge setup open');
959
+ if (rawOpts.json) console.log(JSON.stringify({ status: 'not_started', profile }, null, 2));
960
+ else console.log(profile ? `forge setup: not started\n${formatSetupProfile(profile)}\nRun: forge setup open` : 'forge setup: not started\nRun: forge setup');
822
961
  return;
823
962
  }
824
963
  console.log(formatSetupSession(session, rawOpts.json === true));
@@ -826,15 +965,16 @@ export function buildSetupCommand(): Command {
826
965
 
827
966
  command
828
967
  .command('close')
829
- .description('Close setup after all checks pass')
830
- .option('--target <target>', `Host target to verify: ${VALID_TARGETS.join('|')}`, 'both')
968
+ .description('Close setup after the selected external controller path is ready')
831
969
  .option('--check-updates', 'Include network-backed version update advisories')
832
970
  .option('--force', 'Close even when checks still need attention')
833
971
  .option('--json', 'Output JSON instead of human-readable text')
834
- .action((rawOpts: { target: string; checkUpdates?: boolean; force?: boolean; json?: boolean }) => {
972
+ .action((rawOpts: { checkUpdates?: boolean; force?: boolean; json?: boolean }) => {
835
973
  try {
974
+ const profile = readSetupProfile();
836
975
  const session = closeSetupSession({
837
- target: setupTarget(rawOpts.target, 'forge setup close'),
976
+ profile,
977
+ target: setupHostTarget(profile),
838
978
  checkUpdates: rawOpts.checkUpdates === true,
839
979
  force: rawOpts.force === true,
840
980
  });
@@ -848,14 +988,15 @@ export function buildSetupCommand(): Command {
848
988
 
849
989
  command
850
990
  .command('check')
851
- .description('Run the complete read-only setup checklist without opening a session')
852
- .option('--target <target>', `Host target to inspect: ${VALID_TARGETS.join('|')}`, 'both')
991
+ .description('Run the read-only readiness checklist for the configured external controller set')
992
+ .option('--target <target>', `Host-tooling override: ${VALID_TARGETS.join('|')}`)
853
993
  .option('--check-updates', 'Include network-backed version update advisories')
854
994
  .option('--json', 'Output JSON instead of human-readable text')
855
- .action((rawOpts: { target: string; checkUpdates?: boolean; json?: boolean }) => {
995
+ .action((rawOpts: { target?: string; checkUpdates?: boolean; json?: boolean }) => {
856
996
  try {
997
+ const profile = readSetupProfile();
857
998
  const report = runInitHook({
858
- target: setupTarget(rawOpts.target, 'forge setup check'),
999
+ target: setupTarget(rawOpts.target, 'forge setup check') ?? setupHostTarget(profile),
859
1000
  checkUpdates: rawOpts.checkUpdates === true,
860
1001
  });
861
1002
  console.log(formatInitHook(report, rawOpts.json === true));
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Existing-repository Forge bootstrap/update implementation.
3
3
  *
4
- * This backs the public `forge adopt` command and the legacy
5
- * `forge-init` skill facade: default the target repo to cwd,
4
+ * This backs the public `forge adopt` command and reusable bootstrap primitives: default the target repo to cwd,
6
5
  * install/refresh the machine runtime pieces, apply the repo-local workflow
7
6
  * migration, then verify the installed harness.
8
7
  */
@@ -382,7 +381,7 @@ export function runInit(opts: InitCommandOptions = {}): InitCommandResult {
382
381
  const verify = opts.verify !== false;
383
382
  const syncSkill = opts.syncSkill !== false;
384
383
  const hostAdapters = opts.hostAdapters !== false;
385
- const externalSkills = opts.externalSkills !== false;
384
+ const externalSkills = opts.externalSkills === true;
386
385
  const codegraph = opts.codegraph !== false;
387
386
  const configureCgMcp = opts.configureCodegraphMcp === true;
388
387
  const syncCodegraph = opts.syncCodegraph === true;
@@ -576,15 +575,15 @@ export function runInit(opts: InitCommandOptions = {}): InitCommandResult {
576
575
  steps.push(
577
576
  withStepName(
578
577
  handoff,
579
- "refresh handoff packet",
578
+ "refresh session continuation",
580
579
  "scripts/prepare-codex-handoff.sh --reason forge-adopt-verify",
581
580
  ),
582
581
  );
583
582
  } else {
584
583
  steps.push({
585
- step: "refresh handoff packet",
584
+ step: "refresh session continuation",
586
585
  status: "skipped",
587
- detail: "scripts/prepare-codex-handoff.sh missing",
586
+ detail: "session continuation helper missing (legacy filename scripts/prepare-codex-handoff.sh)",
588
587
  });
589
588
  }
590
589
  }
@@ -12,6 +12,7 @@ import {
12
12
  runMcpPrintGuide,
13
13
  runMcpSetupChatgpt,
14
14
  runMcpSetupCodex,
15
+ runMcpSetupClaude,
15
16
  } from '../mcp/setup';
16
17
 
17
18
  export interface McpServeOptions {
@@ -42,15 +43,19 @@ interface McpAccessOptions {
42
43
 
43
44
  interface McpSetupChatgptOptions {
44
45
  repo?: string;
46
+ controllerHome?: string;
47
+ userLevel?: boolean;
45
48
  host?: string;
46
49
  port?: string;
47
50
  endpoint?: string;
48
51
  serverName?: string;
52
+ localControllerPort?: string;
49
53
  }
50
54
 
51
- interface McpSetupCodexOptions {
55
+ interface McpSetupLocalControllerOptions {
52
56
  repo?: string;
53
57
  scope?: string;
58
+ profile?: string;
54
59
  dryRun?: boolean;
55
60
  }
56
61
 
@@ -221,14 +226,17 @@ export function buildMcpCommand(): Command {
221
226
  });
222
227
  });
223
228
 
224
- const setup = new Command('setup').description('Generate MCP setup files for ChatGPT or Codex');
229
+ const setup = new Command('setup').description('Configure an external controller connection to Forge');
225
230
 
226
231
  setup
227
232
  .command('chatgpt')
228
233
  .description('Generate ChatGPT Connector local config and manual setup guide')
229
234
  .option('--repo <path>', 'Repository root to configure', '.')
235
+ .option('--controller-home <path>', 'User Controller Home (used with --user-level)')
236
+ .option('--user-level', 'Configure the user-level Controller without writing repository guide/gitignore files')
230
237
  .option('--host <host>', 'Local MCP HTTP bind host', '127.0.0.1')
231
238
  .option('--port <port>', 'Local MCP HTTP bind port', '8765')
239
+ .option('--local-controller-port <port>', 'Local-only Utility Console port; defaults to MCP port + 1')
232
240
  .option('--endpoint <url>', 'Stable public HTTPS /mcp endpoint to store in ignored local config')
233
241
  .option('--server-name <name>', 'ChatGPT Connector/MCP server name to record in ignored local config')
234
242
  .action((rawOpts: McpSetupChatgptOptions) => {
@@ -240,17 +248,33 @@ export function buildMcpCommand(): Command {
240
248
 
241
249
  setup
242
250
  .command('codex')
243
- .description('Patch Codex MCP config for forge')
251
+ .description('Configure Codex as an explicit Forge controller or executor')
244
252
  .option('--repo <path>', 'Repository root to configure', '.')
245
253
  .option('--scope <scope>', 'Config scope: project|user', 'project')
254
+ .option('--profile <profile>', 'Forge MCP role: controller|executor', 'executor')
246
255
  .option('--dry-run', 'Print planned changes without writing files')
247
- .action((rawOpts: McpSetupCodexOptions) => {
256
+ .action((rawOpts: McpSetupLocalControllerOptions) => {
248
257
  void runMcpAction(() => {
249
258
  const result = runMcpSetupCodex(rawOpts);
250
259
  console.log(result.lines.join('\n'));
251
260
  });
252
261
  });
253
262
 
263
+
264
+ setup
265
+ .command('claude')
266
+ .description('Configure Claude Code as an explicit Forge controller or executor through the Claude CLI')
267
+ .option('--repo <path>', 'Repository root to expose initially', '.')
268
+ .option('--scope <scope>', 'Claude MCP scope: local|project|user', 'user')
269
+ .option('--profile <profile>', 'Forge MCP role: controller|executor', 'controller')
270
+ .option('--dry-run', 'Print the Claude MCP command without changing Claude config')
271
+ .action((rawOpts: McpSetupLocalControllerOptions) => {
272
+ void runMcpAction(() => {
273
+ const result = runMcpSetupClaude(rawOpts);
274
+ console.log(result.lines.join('\n'));
275
+ });
276
+ });
277
+
254
278
  mcp.addCommand(setup);
255
279
 
256
280
  const access = new Command('access').description('Read or change repository Request / Full Access permission levels');
@@ -284,7 +308,7 @@ export function buildMcpCommand(): Command {
284
308
 
285
309
  mcp
286
310
  .command('prepare-goal')
287
- .description('Prepare .ai/harness/handoff/codex-goal.md and print a host-native /goal prompt from a PRD and checklist Sprint')
311
+ .description('Prepare .ai/harness/controller/packets/codex-goal.md and print a host-native /goal prompt from a PRD and checklist Sprint')
288
312
  .option('--repo <path>', 'Repository root to configure', '.')
289
313
  .requiredOption('--prd <path>', 'PRD path to read')
290
314
  .requiredOption('--sprint <path>', 'Checklist Sprint path to execute')