@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
@@ -1,6 +1,8 @@
1
+ import { spawnSync } from "child_process";
1
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
3
  import { isIP } from "net";
3
- import { dirname, join, relative } from "path";
4
+ import { homedir } from "os";
5
+ import { dirname, join, relative, resolve } from "path";
4
6
  import {
5
7
  ensureMcpControllerHomeBearerToken,
6
8
  ensureMcpControllerHomeOAuthPassphrase,
@@ -18,7 +20,6 @@ import { resolveMcpRepoRoot } from "./repo";
18
20
  import {
19
21
  FORGE_TOOL_SURFACE,
20
22
  DEFAULT_AGENT_TIMEOUT_MS,
21
- defaultLocalAgentRunners,
22
23
  MAX_AGENT_TIMEOUT_MS,
23
24
  } from "../controller/runtime-config";
24
25
  import { ensureControllerHome, ensureRepoPreferredControllerHome } from "../repositories/controller-home";
@@ -35,7 +36,7 @@ export interface McpSetupResult {
35
36
  const REQUIRED_CODEX_TOOLS = [
36
37
  "harness_status",
37
38
  "read_workflow_file",
38
- "latest_handoff",
39
+ "latest_session_context",
39
40
  "latest_checks",
40
41
  "prepare_codex_goal_from_sprint",
41
42
  "write_codex_goal",
@@ -184,265 +185,146 @@ function normalizeChatgptMcpServerName(value: string | undefined): string {
184
185
  export function chatgptGuideMarkdown(
185
186
  endpoint = CHATGPT_MCP_ENDPOINT_PLACEHOLDER,
186
187
  ): string {
187
- return `# forge ChatGPT Controller Setup
188
+ return `# Forge ChatGPT Controller — Low-level Reference
188
189
 
189
- ## Purpose
190
+ > For normal onboarding, start with \`forge setup\` and [Tutorial 2](tutorials/02-connect-chatgpt.md). This file is the lower-level MCP reference and may also be regenerated by repository-scoped compatibility setup.
190
191
 
191
- The \`controller\` profile makes ChatGPT the project control plane. ChatGPT can inspect code and documents, maintain durable Issues and dependency-aware Tasks, apply bounded direct edits, publish Issues to GitHub Projects, dispatch short local Codex/Claude runs or visible GitHub Copilot cloud sessions, and review the resulting state. Repository files remain the source of truth; chat history is not required for recovery.
192
+ ## Product model
192
193
 
193
- ## Prerequisites
194
+ Forge has no internal AI brain. ChatGPT is one external controller option: ChatGPT decides semantic next steps, while Forge owns bounded execution, durable state, authorization, Runtime lifecycle, and evidence. Codex/Claude are optional and are not enabled merely because they are installed.
194
195
 
195
- - A forge adopted repository.
196
- - Bun and the \`forge\` CLI on PATH.
197
- - Codex and/or Claude CLI installed for delegated local execution.
198
- - GitHub CLI \`gh\` authenticated when GitHub Issues, Projects, or Copilot cloud sessions are used.
199
- - ChatGPT workspace access to Developer Mode and custom MCP Connectors.
200
- - A public HTTPS \`/mcp\` endpoint for ChatGPT.
196
+ ## Normal package path
201
197
 
202
- For a shared editable installation, keep the forge checkout at a stable path such as \`~/DevProjects/forge\`, run \`bun install\`, and reinstall the CLI after pulling updates.
203
-
204
- ## One-time setup
205
-
206
- \`\`\`bash
207
- forge mcp setup chatgpt --repo .
208
- forge runtime service install \
209
- --controller-home <absolute-controller-home> \
210
- --repo <absolute-repo-root> \
211
- --host 127.0.0.1 \
212
- --port 8765
213
- \`\`\`
214
-
215
- The \`controller\` profile writes the service-level MCP configuration consumed by the single \`forge-runtime\`. The macOS launchd service starts and restarts that one root process; Gateway, Controller Services, Scheduler, and MCP Transport share the same Runtime lifecycle owner. There is no MCP KeepAlive wrapper or component-level lifecycle command.
216
-
217
- The Runtime endpoint is loopback-only at \`http://127.0.0.1:8765/\`. The local visual controller surface is part of the same Runtime configuration; do not expose it directly.
218
-
219
- Health check:
198
+ A repository is not required for the user-level ChatGPT connection:
220
199
 
221
200
  \`\`\`bash
222
- curl http://127.0.0.1:8765/ready
223
- forge runtime status --controller-home <absolute-controller-home>
224
- forge mcp doctor --repo .
201
+ forge setup configure --controller chatgpt --tunnel auto
202
+ forge setup next
225
203
  \`\`\`
226
204
 
227
- For a fixed Cloudflare domain, verify both local and public discovery without leaking tokens:
205
+ The corresponding low-level commands are:
228
206
 
229
207
  \`\`\`bash
230
- curl http://127.0.0.1:8765/ready
231
- curl https://${CHATGPT_NAMED_TUNNEL_HOST_PLACEHOLDER}/.well-known/oauth-protected-resource/mcp
232
- env | grep -Ei 'proxy|no_proxy'
233
- HTTPS_PROXY= HTTP_PROXY= ALL_PROXY= curl -v https://${CHATGPT_NAMED_TUNNEL_HOST_PLACEHOLDER}/mcp
208
+ forge mcp setup chatgpt --user-level
209
+ forge runtime service install-package
210
+ forge runtime status --json
234
211
  \`\`\`
235
212
 
236
- If a local HTTP proxy interferes with endpoint checks, add the fixed domain, \`*.trycloudflare.com\`, \`*.ts.net\`, and \`100.64.0.0/10\` to \`NO_PROXY\` instead of disabling proxies globally.
237
-
238
- The controller profile stores service-level MCP config under \`controllerHome/mcp/mcp.local.json\`, including allowed local agents, timeout, endpoint, and \`chatgpt.serverName\`. OAuth credentials live in \`controllerHome/mcp/mcp.oauth.json\`; the bearer fallback lives in \`controllerHome/mcp/mcp.tokens.json\`. Existing repo-local \`.forge/mcp.*\` files remain a legacy fallback.
213
+ The Package Runtime does not require a Forge Git checkout, Bun compilation, CodeGraph, or Standalone Recovery. The older \`forge runtime service install --repo ...\` command is an advanced source/immutable-release path.
239
214
 
240
- Repository-specific MCP access rules may be added in \`.forge/mcp.policy.json\`. Repository policy can narrow access, but immutable secret, credential, Git-internal, and build-output denies remain enforced.
215
+ ## Local boundaries
241
216
 
242
- ## Stable endpoint
243
-
244
- Use this Connector URL:
217
+ Forge MCP remains loopback-only, normally at:
245
218
 
246
219
  \`\`\`text
247
- ${endpoint}
220
+ http://127.0.0.1:8765/mcp
248
221
  \`\`\`
249
222
 
250
- Quick tunnels are useful for one-off smoke tests, but their URL may change. For routine use, prefer a fixed Cloudflare domain. Forge never owns the tunnel process: publish the loopback Runtime endpoint through your own stable HTTPS tunnel. With Cloudflare, create and route a named tunnel:
223
+ The local Utility Console uses a separate loopback port (normally 8766). Never expose the Utility Console to the internet.
251
224
 
252
- \`\`\`bash
253
- cloudflared tunnel login
254
- cloudflared tunnel create forge-mcp
255
- cloudflared tunnel route dns forge-mcp ${CHATGPT_NAMED_TUNNEL_HOST_PLACEHOLDER}
256
- cloudflared tunnel run forge-mcp
257
- \`\`\`
225
+ Service-level MCP configuration lives below \`Controller Home/mcp/\`. OAuth credentials and bearer fallback tokens are local secrets and must not be pasted into chat or committed. Repository-specific \`.forge/mcp.policy.json\` can narrow access when a repository is later adopted.
258
226
 
259
- Keep forge on the fixed public origin without owning the tunnel process:
227
+ ## Remote controller connection
260
228
 
261
- \`\`\`bash
262
- forge mcp setup chatgpt --repo . --endpoint https://${CHATGPT_NAMED_TUNNEL_HOST_PLACEHOLDER}/mcp
263
- forge runtime service install --controller-home <absolute-controller-home> --repo <absolute-repo-root> --host 127.0.0.1 --port 8765
264
- \`\`\`
229
+ The setup profile owns the connection choice. Supported paths are:
265
230
 
266
- Regenerate this guide with the stable endpoint:
231
+ 1. **OpenAI Secure MCP Tunnel** — preferred when the OpenAI organization/workspace has tunnel permission; no public inbound Forge endpoint is required. Forge stores only the non-secret \`tunnel_id\`. The runtime API key remains an \`env:\`/file reference owned by the official \`tunnel-client\`.
232
+ 2. **Cloudflare Tunnel** — stable public/internet-reachable HTTPS endpoint.
233
+ 3. **Tailscale Funnel** — HTTPS endpoint for compatible Tailscale environments.
234
+ 4. **Existing HTTPS** — user-managed reverse proxy/tunnel ending in \`/mcp\`.
235
+ 5. **None** — local setup only; remote connectivity can be configured later.
267
236
 
268
- \`\`\`bash
269
- forge mcp setup chatgpt --repo . --endpoint <https-url>/mcp
270
- \`\`\`
271
-
272
- The real endpoint stays in ignored local config; the tracked guide stays placeholder-only. The OAuth discovery endpoint includes \`oauth-protected-resource\` metadata.
273
-
274
- ## Create the ChatGPT Connector
275
-
276
- 1. Open ChatGPT Settings and enable Developer Mode.
277
- 2. Create a custom Connector using the server name from \`controllerHome/mcp/mcp.local.json\` under \`chatgpt.serverName\`.
278
- 3. Paste the public HTTPS URL ending in \`/mcp\`.
279
- 4. Configure Connector authentication as OAuth. ChatGPT must use the \`/mcp\` OAuth URL; do not point ChatGPT at \`/mcp-bearer\`.
280
- 5. Scan tools and authorize with the passphrase from \`controllerHome/mcp/mcp.oauth.json\`.
281
- 6. Keep write confirmations enabled.
282
- 7. Re-scan tools after updating forge tool schemas.
283
-
284
- ### Grok and other OAuth MCP clients
285
-
286
- Current Grok custom connectors support OAuth dynamic client registration + PKCE. Use the same canonical endpoint as ChatGPT:
287
-
288
- \`\`\`text
289
- <https-tunnel-url>/mcp
290
- \`\`\`
291
-
292
- Do not use the legacy \/mcp-grok route for new connectors. Grok's current OAuth callback is \/connectors-oauth-exchange-code\/ on grok.com; forge accepts that callback through dynamic registration or its public-client fallback.
293
-
294
- ### Non-OAuth MCP clients
237
+ For OpenAI Secure MCP Tunnel, setup uses the official supervised runtime surface and considers it ready only when \`tunnel-client runtimes status forge --json\` reports the runtime running, healthy, and ready.
295
238
 
296
- Clients that cannot complete OAuth dynamic client registration + PKCE should use the dedicated bearer endpoint instead of \`/mcp\` or \`/authorize\`:
239
+ For public HTTPS paths, the configured connector URL is:
297
240
 
298
241
  \`\`\`text
299
- <https-tunnel-url>/mcp-bearer
242
+ ${endpoint}
300
243
  \`\`\`
301
244
 
302
- Authenticate with \`Authorization: Bearer <token>\` using the token stored under \`controllerHome/mcp/mcp.tokens.json\` (or \`FORGE_MCP_TOKEN\`). Do not paste the raw token into chat or docs. \`/health\` advertises both \`mcpEndpoint\` and \`bearerEndpoint\`. Incomplete OAuth hits on \`/authorize\` return HTTP 400 and point clients to \`/mcp-bearer\`.
303
-
304
- ## Verify the loaded tool surface
305
-
306
- Default \`--toolset core\` and \`--toolset advanced\` expose the same bounded 20-tool ChatGPT surface: the five \`rh_*\` facades (with code retrieval routed through \`rh_context.search\`) plus repository selection, \`repository_command_execute\`, exact source read, safe patch, \`run_check\`, one typed \`plugin_action_execute\` dispatcher (action schemas via \`rh_context\`), managed process lifecycle, result retrieval, and approval resolution. Use \`--toolset full\` for exhaustive legacy compatibility, which keeps every atomic handler (including Git) registered. Confirm all five \`rh_*\` tools appear after connect. If only legacy planning tools are visible, refresh or recreate the Connector so ChatGPT reloads the MCP tool schema.
307
-
308
- ## Refresh newly added repository tools
309
-
310
- For the current repository only, prefer a bounded local restart:
245
+ Record a stable endpoint with:
311
246
 
312
247
  \`\`\`bash
313
- launchctl kickstart -k gui/$(id -u)/com.moretea.forge.runtime.<controller-home-suffix>
248
+ forge setup configure --controller chatgpt --tunnel existing --endpoint https://forge.example.com/mcp
314
249
  \`\`\`
315
250
 
316
- The launchd label is printed by \`forge runtime service install\` (it is \`com.moretea.forge.runtime.<controller-home-suffix>\`). The single Runtime restart also refreshes repository tools. There is no per-repository rollout or component restart surface.
251
+ Forge synchronizes an explicitly recorded endpoint into the user-level ChatGPT MCP configuration. A configured URL is not proof of live end-to-end connectivity; verify from ChatGPT with a real tool call.
317
252
 
318
- After the restart, rescan or recreate the ChatGPT Connector, then call \`controller_capabilities\` again and verify \`expectedTools\` still includes \`repository_latest_source_diagnose\` and \`repository_bootstrap_local_project\`.
253
+ ## ChatGPT-side setup
319
254
 
320
- ## Daily workflow
255
+ ChatGPT developer-mode/MCP availability is controlled by the current ChatGPT plan and workspace policy. Follow OpenAI's current developer-mode documentation; Forge cannot bypass plan, administrator, RBAC, or tool-action restrictions.
321
256
 
322
- Start a new ChatGPT conversation with:
257
+ - With **Secure MCP Tunnel**, create the ChatGPT app/connector using the Tunnel connection and the tunnel ID.
258
+ - With **HTTPS**, use the stable URL ending in \`/mcp\` and Forge's OAuth flow.
259
+ - Refresh/scan tools after Forge tool definitions change.
323
260
 
324
- \`\`\`text
325
- Use forge as the project controller. Read project_snapshot, current Issues, active Runs, and relevant code before deciding the next action. Keep work in small dependency-aware Tasks. Do not dispatch one large Issue as one agent run.
326
- \`\`\`
327
-
328
- Typical requests:
329
-
330
- \`\`\`text
331
- Analyze this requirement and the current implementation. Create or update an Issue and split it into executable Tasks. Do not execute yet.
332
- \`\`\`
261
+ The bounded default surface intentionally exposes the preferred \`rh_*\` facade plus repository/source/check/process/plugin escape hatches. Do not switch to exhaustive compatibility mode merely to see more tool names.
333
262
 
334
- \`\`\`text
335
- Inspect readiness for this Issue, publish it to GitHub when collaboration is useful, and launch at most two independent Tasks. Review every local diff or GitHub pull request and record verification evidence before accepting it.
336
- \`\`\`
263
+ ## Verify
337
264
 
338
- \`\`\`text
339
- Read the project board and failed Runs. Retry only the smallest failed Task, or re-plan the Issue when the original split is wrong.
340
- \`\`\`
265
+ Local Runtime:
341
266
 
342
- \`\`\`text
343
- This is a small local fix. Open a bounded edit session, modify only the named files, inspect the Git diff, run focused checks, and finalize or rollback the edit.
267
+ \`\`\`bash
268
+ forge runtime status --json
269
+ forge mcp doctor
344
270
  \`\`\`
345
271
 
346
- ## Persistent model
272
+ Then verify from the selected external controller:
347
273
 
348
274
  \`\`\`text
349
- Issue
350
- -> Task T1
351
- -> Run 1
352
- -> Run 2 (retry)
353
- -> Task T2
354
- -> Task T3
355
- \`\`\`
356
-
357
- - Issues and Tasks are stored under \`tasks/issues/\` as JSON plus readable Markdown.
358
- - Agent jobs, logs, edit backups, and worktrees are stored under ignored \`.ai/harness/\` runtime directories.
359
- - A completed isolated agent Run moves its Task to review. ChatGPT must inspect it with \`get_task_diff\`, integrate it with \`integrate_task_run\`, record named-check and criterion evidence through \`verify_task\`, then explicitly accept it or request changes.
360
- - Dependency completion unlocks later Tasks automatically.
361
- - Any new ChatGPT conversation can recover state through \`project_snapshot\` and \`get_project_board\`.
362
-
363
- ## Capability boundaries
364
-
365
- - \`observe\`: inspect repository state, search code, and read bounded file ranges.
366
- - \`manage\`: create Issues, dynamically split Tasks, inspect launch readiness, publish to GitHub Issues/Projects, update status, and maintain project documents.
367
- - \`edit\`: use a bounded edit session with allowed paths, SHA preconditions, change limits, backups, and rollback.
368
- - \`execute\`: dispatch a ready Task to an allowed local agent in an isolated worktree or to a visible GitHub Copilot cloud session.
369
- - Protected operations such as secrets, Git internals, package lockfiles, CI workflow changes, commits, merges, and pushes are not default controller actions.
370
-
371
- Kernel-managed Agent goals and persistent Task Runs are retired. New work uses
372
- \`rh_work\` to create or continue a WorkContract, \`rh_work.controller_claim\` to
373
- establish ownership, and \`rh_work.launcher_start\` to begin an external
374
- SuperController session.
375
-
376
- ## Dev Mode Agent Runner
377
-
378
- Local Agent execution is opt-in. GitHub cloud sessions use authenticated \`gh\` and do not require the local dev runner:
379
-
380
- The runner is enabled through the service-level MCP configuration written by \`forge mcp setup chatgpt\` (\`devMode.agentRunner\` and \`devMode.allowedAgents\`). It executes inside the single Forge Runtime's bounded worker processes; there is no separate \`forge mcp serve\` lifecycle to supervise.
381
-
382
- The runner defaults to 60 minutes per local Task and supports explicit values up to 12 hours. Requested values are validated and persisted unchanged; an invalid value fails instead of silently falling back to 120 seconds.
383
-
384
- The runner:
385
-
386
- - accepts only configured \`codex\` or \`claude\` agents;
387
- - creates one persistent Run per Task;
388
- - normally creates an isolated Git worktree;
389
- - records prompt, process metadata, streaming stdout/stderr, structured events, and result under \`.ai/harness/jobs/\`;
390
- - never exposes arbitrary shell input through MCP;
391
- - does not commit, merge, or push automatically.
392
-
393
- Watch local or GitHub execution from a terminal:
394
-
395
- \`\`\`bash
396
- forge controller runs --repo .
397
- forge controller watch <RUN-ID> --repo . --log
275
+ Use Forge. Call rh_status and report whether the Runtime is ready. Do not modify anything yet.
398
276
  \`\`\`
399
277
 
400
- The \`--log\` view streams local Codex/Claude output while the process is running and polls GitHub cloud-session logs when available.
278
+ A successful controller-to-Forge tool call is the meaningful end-to-end setup milestone.
401
279
 
402
- ## Local Codex MCP
280
+ ## Optional controller entries
403
281
 
404
- Configure Codex to read forge state:
282
+ Codex and Claude are configured only when explicitly selected:
405
283
 
406
284
  \`\`\`bash
407
- forge mcp setup codex --repo . --scope project
285
+ forge mcp setup codex --scope user --profile controller
286
+ forge mcp setup claude --scope user --profile controller
408
287
  \`\`\`
409
288
 
410
- The executor profile remains read-oriented. Controller-dispatched Codex work is scoped by the generated Task prompt and worktree.
289
+ Multiple controller entries may coexist, while one external controller remains the semantic owner at a time.
411
290
 
412
- ## Security
291
+ ## Advanced source/Recovery path
413
292
 
414
- - Keep OAuth passphrases, bearer tokens, tunnel tokens, \`~/.codex/auth.json\`, and other credentials out of chat and Git.
415
- - Keep the MCP server bound to loopback; expose it only through the authenticated tunnel.
416
- - Do not remove immutable hard-deny patterns in order to make a Task pass.
417
- - Review every completed local diff or GitHub pull request and record passing Verification Gate evidence before accepting a Task.
418
- - Use the smallest allowed path set and focused checks for direct edits.
419
-
420
- ## Troubleshooting
421
-
422
- - ChatGPT cannot connect: verify the HTTPS tunnel ends in \`/mcp\` and local \`/ready\` responds.
423
- - Grok cannot connect: recreate it with the canonical \`…/mcp\` URL. The legacy \`…/mcp-grok\` URL is compatibility-only.
424
- - A genuinely non-OAuth client loops on \`/authorize\`: use \`…/mcp-bearer\` with a bearer token from \`controllerHome/mcp/mcp.tokens.json\`.
425
- - ChatGPT auth loops: retry authorization and inspect \`controllerHome/mcp/mcp.oauth.json\` first, then legacy \`.forge/mcp.oauth.json\` only when using fallback; do not paste the passphrase into chat.
426
- - Tool scan misses tools: restart the Forge Runtime service with \`launchctl kickstart -k gui/$(id -u)/com.moretea.forge.runtime.<controller-home-suffix>\`, then rescan or recreate the versioned Connector and verify \`controller_capabilities.expectedTools\` includes \`repository_latest_source_diagnose\` and \`repository_bootstrap_local_project\`.
427
- - Codex cannot see the MCP server: rerun \`forge mcp setup codex --repo . --scope project\`.
428
- - A quick tunnel URL changed: update the Connector URL or switch to a named tunnel.
429
- - A Task is blocked: inspect \`get_task_run\`, shrink or re-plan the Task, then retry that Task rather than redispatching the full Issue.
293
+ Maintainers who intentionally need immutable Git/source releases and independent disaster recovery may use the source Runtime and Standalone Recovery documentation. Those mechanisms are not required by ordinary npm/package users and are deliberately excluded from normal setup.
430
294
  `;
431
295
  }
432
296
 
433
297
  export function runMcpSetupChatgpt(opts: {
434
298
  repo?: string;
299
+ controllerHome?: string;
300
+ userLevel?: boolean;
435
301
  host?: string;
436
302
  port?: string;
437
303
  endpoint?: string;
438
304
  serverName?: string;
305
+ localControllerPort?: string;
306
+ connectorPort?: string;
439
307
  }): McpSetupResult {
440
308
  const repoRoot = resolveMcpRepoRoot(opts.repo ?? ".");
441
- const controllerHome = ensureRepoPreferredControllerHome(repoRoot);
309
+ const controllerHome = opts.userLevel
310
+ ? ensureControllerHome(opts.controllerHome)
311
+ : ensureRepoPreferredControllerHome(repoRoot, opts.controllerHome);
442
312
  const changed: string[] = [];
443
313
  const existingConfig = loadMcpServiceLocalConfig(controllerHome, repoRoot);
444
314
  const host = opts.host ?? existingConfig?.server?.host ?? "127.0.0.1";
445
315
  const port = opts.port ?? String(existingConfig?.server?.port ?? 8765);
316
+ const parsedPort = Number(port);
317
+ if (!Number.isInteger(parsedPort) || parsedPort < 1 || parsedPort > 65_535) throw new Error(`forge mcp setup chatgpt: invalid port ${port}`);
318
+ const localControllerPort = opts.localControllerPort !== undefined
319
+ ? Number(opts.localControllerPort)
320
+ : existingConfig?.localController?.port ?? (parsedPort < 65_535 ? parsedPort + 1 : 8766);
321
+ if (!Number.isInteger(localControllerPort) || localControllerPort < 1 || localControllerPort > 65_535) throw new Error(`forge mcp setup chatgpt: invalid Local Controller port ${opts.localControllerPort}`);
322
+ if (localControllerPort === parsedPort) throw new Error('forge mcp setup chatgpt: Local Controller port must differ from MCP port');
323
+ const connectorPort = opts.connectorPort !== undefined
324
+ ? Number(opts.connectorPort)
325
+ : parsedPort <= 65_533 ? parsedPort + 2 : 8767;
326
+ if (!Number.isInteger(connectorPort) || connectorPort < 1 || connectorPort > 65_535) throw new Error(`forge mcp setup chatgpt: invalid connector port ${opts.connectorPort}`);
327
+ if (connectorPort === parsedPort || connectorPort === localControllerPort) throw new Error('forge mcp setup chatgpt: connector port must differ from Runtime and Local Controller ports');
446
328
  const existingServerName = existingConfig?.chatgpt?.serverName;
447
329
  const migratedServerName =
448
330
  existingServerName && !LEGACY_DEFAULT_SERVER_NAMES.has(existingServerName)
@@ -455,7 +337,7 @@ export function runMcpSetupChatgpt(opts: {
455
337
  opts.endpoint ?? existingConfig?.chatgpt?.endpoint,
456
338
  );
457
339
  const configPath = mcpControllerHomeLocalConfigPath(controllerHome);
458
- const guidePath = join(repoRoot, "docs", "forge-chatgpt-mcp-setup.md");
340
+ const guidePath = opts.userLevel ? undefined : join(repoRoot, "docs", "forge-chatgpt-mcp-setup.md");
459
341
  const token = ensureMcpControllerHomeBearerToken(controllerHome);
460
342
  const oauth = ensureMcpControllerHomeOAuthPassphrase(controllerHome);
461
343
  if (token.changed) changed.push(token.path);
@@ -464,11 +346,11 @@ export function runMcpSetupChatgpt(opts: {
464
346
  const toolset = migratedToolset.toolset;
465
347
  const config = {
466
348
  version: Math.max(existingConfig?.version ?? 1, 2),
467
- repo: repoRoot,
349
+ ...(opts.userLevel ? (existingConfig?.repo ? { repo: existingConfig.repo } : {}) : { repo: repoRoot }),
468
350
  server: {
469
351
  ...existingConfig?.server,
470
352
  host,
471
- port: Number(port),
353
+ port: parsedPort,
472
354
  transport: existingConfig?.server?.transport ?? "http",
473
355
  },
474
356
  auth: existingConfig?.auth ?? {
@@ -480,6 +362,7 @@ export function runMcpSetupChatgpt(opts: {
480
362
  ...existingConfig?.chatgpt,
481
363
  serverName,
482
364
  ...(endpoint ? { endpoint } : {}),
365
+ localEndpoint: `http://127.0.0.1:${connectorPort}/mcp`,
483
366
  },
484
367
  profile: existingConfig?.profile ?? "controller",
485
368
  toolset,
@@ -488,16 +371,22 @@ export function runMcpSetupChatgpt(opts: {
488
371
  ?? accessModeForLegacyToolset(existingConfig?.toolset ?? toolset),
489
372
  accessModeUpdatedAt: existingConfig?.accessModeUpdatedAt,
490
373
  accessModeRevision: existingConfig?.accessModeRevision ?? 0,
491
- localController: existingConfig?.localController ?? {
492
- enabled: true,
493
- host: "127.0.0.1",
494
- port: 8766,
495
- autoOpen: false,
496
- },
374
+ localController: opts.userLevel && !existingConfig?.repo
375
+ ? {
376
+ enabled: false,
377
+ host: existingConfig?.localController?.host ?? "127.0.0.1",
378
+ port: localControllerPort,
379
+ autoOpen: false,
380
+ mode: "disabled",
381
+ }
382
+ : {
383
+ ...(existingConfig?.localController ?? { enabled: true, host: "127.0.0.1", autoOpen: false }),
384
+ port: localControllerPort,
385
+ },
497
386
  devMode: {
498
387
  ...existingConfig?.devMode,
499
- agentRunner: existingConfig?.devMode?.agentRunner ?? true,
500
- allowedAgents: existingConfig?.devMode?.allowedAgents ?? defaultLocalAgentRunners(),
388
+ agentRunner: existingConfig?.devMode?.agentRunner ?? false,
389
+ allowedAgents: existingConfig?.devMode?.allowedAgents ?? [],
501
390
  timeoutMs:
502
391
  !existingConfig?.devMode?.timeoutMs ||
503
392
  existingConfig.devMode.timeoutMs === 120_000
@@ -512,105 +401,117 @@ export function runMcpSetupChatgpt(opts: {
512
401
  `${JSON.stringify(config, null, 2)}\n`,
513
402
  changed,
514
403
  );
515
- writeFileIfChanged(guidePath, chatgptGuideMarkdown(), changed);
516
- ensureGitignoreEntries(
517
- repoRoot,
518
- [
519
- ".forge/mcp.local.json",
520
- ".forge/mcp.tokens.json",
521
- ".forge/mcp.oauth.json",
522
- ".forge/mcp.oauth-tokens.json",
523
- ".forge/mcp.runtime.json",
524
- ".ai/harness/mcp/audit.log",
525
- ".ai/harness/local-jobs/",
526
- ".ai/harness/controller/",
527
- ],
528
- changed,
529
- );
404
+ if (guidePath) {
405
+ writeFileIfChanged(guidePath, chatgptGuideMarkdown(), changed);
406
+ ensureGitignoreEntries(
407
+ repoRoot,
408
+ [
409
+ ".forge/mcp.local.json",
410
+ ".forge/mcp.tokens.json",
411
+ ".forge/mcp.oauth.json",
412
+ ".forge/mcp.oauth-tokens.json",
413
+ ".forge/mcp.runtime.json",
414
+ ".ai/harness/mcp/audit.log",
415
+ ".ai/harness/local-jobs/",
416
+ ".ai/harness/controller/",
417
+ ],
418
+ changed,
419
+ );
420
+ }
530
421
 
531
422
  return {
532
423
  status: "ok",
533
424
  repoRoot,
534
425
  changed,
535
426
  lines: [
536
- `[forge mcp] Repo: ${repoRoot}`,
427
+ opts.userLevel ? `[forge mcp] Scope: user (${controllerHome})` : `[forge mcp] Repo: ${repoRoot}`,
537
428
  "[forge mcp] Profile: controller",
538
429
  `[forge mcp] Toolset: ${config.toolset}`,
539
430
  `[forge mcp] ChatGPT MCP server name: ${serverName}`,
540
- `[forge mcp] Local endpoint: http://${host}:${port}/mcp`,
431
+ `[forge mcp] Internal Runtime endpoint: http://${host}:${port}/mcp`,
432
+ `[forge mcp] ChatGPT local OAuth endpoint: http://127.0.0.1:${connectorPort}/mcp`,
541
433
  `[forge mcp] Local Controller: http://${config.localController.host}:${config.localController.port}/`,
542
- `[forge mcp] Local agent timeout: ${config.devMode.timeoutMs}ms (max ${config.devMode.maxTimeoutMs}ms)`,
434
+ `[forge mcp] Optional external delegation: ${config.devMode.agentRunner ? `enabled (${config.devMode.allowedAgents.join(',') || 'none'})` : 'disabled'}`,
543
435
  endpoint
544
436
  ? `[forge mcp] ChatGPT endpoint: ${endpoint}`
545
437
  : "[forge mcp] ChatGPT endpoint: requires stable HTTPS tunnel",
546
438
  `[forge mcp] Auth: OAuth passphrase (${relative(repoRoot, oauth.path)})`,
547
439
  `[forge mcp] Bearer fallback token: ${relative(repoRoot, token.path)}`,
548
440
  `[forge mcp] Config: ${relative(repoRoot, configPath)}`,
549
- `[forge mcp] Guide: ${relative(repoRoot, guidePath)} (generic; endpoint stays in ignored local config)`,
550
- `[forge mcp] Runtime state: ${relative(repoRoot, mcpControllerHomeRuntimeStatePath(controllerHome))}`,
551
- `Next: forge runtime service install --controller-home ${controllerHome} --repo ${repoRoot} --host ${host} --port ${port}`,
441
+ ...(guidePath ? [`[forge mcp] Guide: ${relative(repoRoot, guidePath)} (generic; endpoint stays in ignored local config)`] : []),
442
+ `[forge mcp] Runtime state: ${mcpControllerHomeRuntimeStatePath(controllerHome)}`,
443
+ opts.userLevel
444
+ ? 'Next: forge setup next'
445
+ : `Next: forge runtime service install-package --controller-home ${controllerHome} --host ${host} --port ${port}`,
552
446
  ],
553
447
  };
554
448
  }
555
449
 
556
- const CODEX_MCP_BLOCK = `[mcp_servers.forge]
557
- command = "forge"
558
- args = [
559
- "mcp",
560
- "serve",
561
- "--repo",
562
- ".",
563
- "--transport",
564
- "stdio",
565
- "--profile",
566
- "executor"
567
- ]
568
- enabled_tools = [
569
- "harness_status",
570
- "read_workflow_file",
571
- "latest_handoff",
572
- "latest_checks",
573
- "prepare_codex_goal_from_sprint",
574
- "write_codex_goal",
575
- "run_workflow_check"
576
- ]
577
- default_tools_approval_mode = "prompt"
578
- `;
450
+ type LocalControllerProfile = 'controller' | 'executor';
451
+
452
+ function localControllerProfile(value: string | undefined, label: string, fallback: LocalControllerProfile): LocalControllerProfile {
453
+ const normalized = (value ?? fallback).trim().toLowerCase();
454
+ if (normalized !== 'controller' && normalized !== 'executor') {
455
+ throw new Error(`${label}: expected controller|executor`);
456
+ }
457
+ return normalized;
458
+ }
459
+
460
+ export function codexMcpBlock(profile: LocalControllerProfile = 'executor'): string {
461
+ const lines = [
462
+ '[mcp_servers.forge]',
463
+ 'command = "forge"',
464
+ 'args = [',
465
+ ' "mcp",',
466
+ ' "serve",',
467
+ ' "--repo",',
468
+ ' ".",',
469
+ ' "--transport",',
470
+ ' "stdio",',
471
+ ' "--profile",',
472
+ ` "${profile}"`,
473
+ ']',
474
+ ];
475
+ if (profile === 'executor') {
476
+ lines.push(
477
+ 'enabled_tools = [',
478
+ ...REQUIRED_CODEX_TOOLS.map((tool) => ` "${tool}",`),
479
+ ']',
480
+ 'default_tools_approval_mode = "prompt"',
481
+ );
482
+ }
483
+ return `${lines.join('\n')}\n`;
484
+ }
579
485
 
580
- export function patchCodexConfigToml(current: string): string {
486
+ export function patchCodexConfigToml(current: string, profile: LocalControllerProfile = 'executor'): string {
487
+ const block = codexMcpBlock(profile);
581
488
  const normalized = current.trimEnd();
582
489
  const blockPattern = /\n?\[mcp_servers\.forge\][\s\S]*?(?=\n\[|$)/;
583
- const prefix = normalized.length > 0 ? `${normalized}\n\n` : "";
584
- if (!blockPattern.test(normalized)) return `${prefix}${CODEX_MCP_BLOCK}`;
585
- return `${normalized.replace(blockPattern, `\n${CODEX_MCP_BLOCK}`.trimEnd())}\n`;
490
+ const prefix = normalized.length > 0 ? `${normalized}\n\n` : '';
491
+ if (!blockPattern.test(normalized)) return `${prefix}${block}`;
492
+ return `${normalized.replace(blockPattern, `\n${block}`.trimEnd())}\n`;
586
493
  }
587
494
 
588
495
  export function runMcpSetupCodex(opts: {
589
496
  repo?: string;
590
497
  scope?: string;
498
+ profile?: string;
591
499
  dryRun?: boolean;
592
500
  }): McpSetupResult {
593
- if ((opts.scope ?? "project") !== "project") {
594
- throw new Error(
595
- "forge mcp setup codex currently supports --scope project only",
596
- );
501
+ const scope = (opts.scope ?? 'project').trim().toLowerCase();
502
+ if (scope !== 'project' && scope !== 'user') {
503
+ throw new Error('forge mcp setup codex --scope: expected project|user');
597
504
  }
598
- const repoRoot = resolveMcpRepoRoot(opts.repo ?? ".");
599
- const configPath = join(repoRoot, ".codex", "config.toml");
505
+ const profile = localControllerProfile(opts.profile, 'forge mcp setup codex --profile', 'executor');
506
+ const repoRoot = resolveMcpRepoRoot(opts.repo ?? '.');
507
+ const configPath = scope === 'user' ? join(homedir(), '.codex', 'config.toml') : join(repoRoot, '.codex', 'config.toml');
600
508
  const changed: string[] = [];
601
- const current = existsSync(configPath)
602
- ? readFileSync(configPath, "utf-8")
603
- : "";
604
- const next = patchCodexConfigToml(current);
509
+ const current = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';
510
+ const next = patchCodexConfigToml(current, profile);
605
511
  if (opts.dryRun === true) {
606
512
  return {
607
- status: "ok",
608
- repoRoot,
609
- changed: [],
610
- lines: [
611
- `[forge mcp] Dry run: would patch ${relative(repoRoot, configPath)}`,
612
- next,
613
- ],
513
+ status: 'ok', repoRoot, changed: [],
514
+ lines: [`[forge mcp] Dry run: would patch ${scope === 'user' ? configPath : relative(repoRoot, configPath)}`, next],
614
515
  };
615
516
  }
616
517
  if (existsSync(configPath) && current !== next) {
@@ -619,13 +520,56 @@ export function runMcpSetupCodex(opts: {
619
520
  }
620
521
  writeFileIfChanged(configPath, next, changed);
621
522
  return {
622
- status: "ok",
623
- repoRoot,
624
- changed,
523
+ status: 'ok', repoRoot, changed,
524
+ lines: [
525
+ `[forge mcp] Codex config: ${scope === 'user' ? configPath : relative(repoRoot, configPath)}`,
526
+ '[forge mcp] Server: forge',
527
+ '[forge mcp] Transport: stdio',
528
+ `[forge mcp] Role: external ${profile === 'controller' ? 'primary controller' : 'executor/delegate'}`,
529
+ ],
530
+ };
531
+ }
532
+
533
+ export function claudeMcpSetupCommand(repoRoot: string, profile: LocalControllerProfile = 'controller', scope = 'user'): string[] {
534
+ return [
535
+ 'claude', 'mcp', 'add', 'forge', '--scope', scope, '--',
536
+ 'forge', 'mcp', 'serve', '--repo', repoRoot, '--transport', 'stdio', '--profile', profile,
537
+ ];
538
+ }
539
+
540
+ export function runMcpSetupClaude(opts: {
541
+ repo?: string;
542
+ scope?: string;
543
+ profile?: string;
544
+ dryRun?: boolean;
545
+ }): McpSetupResult {
546
+ const repoRoot = resolveMcpRepoRoot(opts.repo ?? '.');
547
+ const scope = (opts.scope ?? 'user').trim().toLowerCase();
548
+ if (!['local', 'project', 'user'].includes(scope)) {
549
+ throw new Error('forge mcp setup claude --scope: expected local|project|user');
550
+ }
551
+ const profile = localControllerProfile(opts.profile, 'forge mcp setup claude --profile', 'controller');
552
+ const command = claudeMcpSetupCommand(scope === 'user' ? '.' : repoRoot, profile, scope);
553
+ if (opts.dryRun === true) {
554
+ return {
555
+ status: 'ok', repoRoot, changed: [],
556
+ lines: [`[forge mcp] Dry run: ${command.join(' ')}`],
557
+ };
558
+ }
559
+ const result = spawnSync(command[0], command.slice(1), { cwd: repoRoot, encoding: 'utf8', timeout: 30_000 });
560
+ if (result.error && (result.error as NodeJS.ErrnoException).code === 'ENOENT') {
561
+ throw new Error('Claude Code CLI is not installed. Install/authenticate Claude only if you selected it as a Forge controller.');
562
+ }
563
+ if (result.status !== 0) {
564
+ throw new Error(`Claude MCP configuration failed (${result.status ?? 'unknown'}): ${(result.stderr || result.stdout || result.error?.message || '').trim()}`);
565
+ }
566
+ return {
567
+ status: 'ok', repoRoot, changed: [],
625
568
  lines: [
626
- `[forge mcp] Codex config: ${relative(repoRoot, configPath)}`,
627
- "[forge mcp] Server: forge",
628
- "[forge mcp] Transport: stdio",
569
+ '[forge mcp] Claude Code MCP server configured through the Claude CLI.',
570
+ `[forge mcp] Scope: ${scope}`,
571
+ `[forge mcp] Role: external ${profile === 'controller' ? 'primary controller' : 'executor/delegate'}`,
572
+ '[forge mcp] Verify: claude mcp get forge',
629
573
  ],
630
574
  };
631
575
  }
@@ -709,7 +653,7 @@ export function runMcpDoctor(opts: {
709
653
  },
710
654
  devMode: {
711
655
  agentRunner: localConfig?.devMode?.agentRunner === true,
712
- allowedAgents: localConfig?.devMode?.allowedAgents ?? defaultLocalAgentRunners(),
656
+ allowedAgents: localConfig?.devMode?.allowedAgents ?? [],
713
657
  timeoutMs: localConfig?.devMode?.timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS,
714
658
  maxTimeoutMs:
715
659
  localConfig?.devMode?.maxTimeoutMs ?? MAX_AGENT_TIMEOUT_MS,