@getforgeai/cli 0.1.0-beta.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 (221) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +80 -0
  3. package/dist/bin/forgeai.d.ts +3 -0
  4. package/dist/bin/forgeai.js +8 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.js +180 -0
  7. package/dist/commands/auth-login.d.ts +4 -0
  8. package/dist/commands/auth-login.js +202 -0
  9. package/dist/commands/auth-logout.d.ts +2 -0
  10. package/dist/commands/auth-logout.js +41 -0
  11. package/dist/commands/auth-status.d.ts +4 -0
  12. package/dist/commands/auth-status.js +81 -0
  13. package/dist/commands/cli-list.d.ts +4 -0
  14. package/dist/commands/cli-list.js +40 -0
  15. package/dist/commands/cli-unlink.d.ts +4 -0
  16. package/dist/commands/cli-unlink.js +45 -0
  17. package/dist/commands/config-set.d.ts +5 -0
  18. package/dist/commands/config-set.js +63 -0
  19. package/dist/commands/connect.d.ts +9 -0
  20. package/dist/commands/connect.js +165 -0
  21. package/dist/commands/disconnect.d.ts +5 -0
  22. package/dist/commands/disconnect.js +126 -0
  23. package/dist/commands/journal-clear.d.ts +2 -0
  24. package/dist/commands/journal-clear.js +16 -0
  25. package/dist/commands/journal-list.d.ts +2 -0
  26. package/dist/commands/journal-list.js +20 -0
  27. package/dist/commands/journal-status.d.ts +2 -0
  28. package/dist/commands/journal-status.js +18 -0
  29. package/dist/commands/mcp-serve.d.ts +13 -0
  30. package/dist/commands/mcp-serve.js +74 -0
  31. package/dist/commands/org-list.d.ts +4 -0
  32. package/dist/commands/org-list.js +56 -0
  33. package/dist/commands/org-unlink.d.ts +4 -0
  34. package/dist/commands/org-unlink.js +63 -0
  35. package/dist/commands/reconnect.d.ts +4 -0
  36. package/dist/commands/reconnect.js +46 -0
  37. package/dist/commands/release-open.d.ts +2 -0
  38. package/dist/commands/release-open.js +43 -0
  39. package/dist/commands/session-respond.d.ts +2 -0
  40. package/dist/commands/session-respond.js +29 -0
  41. package/dist/commands/task-open.d.ts +2 -0
  42. package/dist/commands/task-open.js +42 -0
  43. package/dist/commands/vm.d.ts +21 -0
  44. package/dist/commands/vm.js +122 -0
  45. package/dist/commands/workflow-open.d.ts +2 -0
  46. package/dist/commands/workflow-open.js +43 -0
  47. package/dist/config/config-manager.d.ts +31 -0
  48. package/dist/config/config-manager.js +70 -0
  49. package/dist/config/token-manager.d.ts +11 -0
  50. package/dist/config/token-manager.js +87 -0
  51. package/dist/index.d.ts +11 -0
  52. package/dist/index.js +22 -0
  53. package/dist/lib/auth-client.d.ts +1670 -0
  54. package/dist/lib/auth-client.js +10 -0
  55. package/dist/lib/branch-manager.d.ts +79 -0
  56. package/dist/lib/branch-manager.js +620 -0
  57. package/dist/lib/clone-and-checkout.d.ts +6 -0
  58. package/dist/lib/clone-and-checkout.js +41 -0
  59. package/dist/lib/connection-manager.d.ts +58 -0
  60. package/dist/lib/connection-manager.js +436 -0
  61. package/dist/lib/daemon-entry.d.ts +2 -0
  62. package/dist/lib/daemon-entry.js +210 -0
  63. package/dist/lib/daemon.d.ts +38 -0
  64. package/dist/lib/daemon.js +148 -0
  65. package/dist/lib/event-journal.d.ts +24 -0
  66. package/dist/lib/event-journal.js +76 -0
  67. package/dist/lib/ipc-client.d.ts +10 -0
  68. package/dist/lib/ipc-client.js +70 -0
  69. package/dist/lib/ipc-server.d.ts +36 -0
  70. package/dist/lib/ipc-server.js +70 -0
  71. package/dist/lib/node-version-guard.d.ts +8 -0
  72. package/dist/lib/node-version-guard.js +60 -0
  73. package/dist/lib/open-directory.d.ts +7 -0
  74. package/dist/lib/open-directory.js +41 -0
  75. package/dist/lib/repo-manager.d.ts +29 -0
  76. package/dist/lib/repo-manager.js +103 -0
  77. package/dist/lib/resilient-emitter.d.ts +53 -0
  78. package/dist/lib/resilient-emitter.js +160 -0
  79. package/dist/lib/rest-client.d.ts +164 -0
  80. package/dist/lib/rest-client.js +139 -0
  81. package/dist/lib/skill-manager.d.ts +23 -0
  82. package/dist/lib/skill-manager.js +167 -0
  83. package/dist/lib/socket-client.d.ts +68 -0
  84. package/dist/lib/socket-client.js +160 -0
  85. package/dist/mcp/mcp-probe.d.ts +11 -0
  86. package/dist/mcp/mcp-probe.js +106 -0
  87. package/dist/mcp/mcp-response.d.ts +29 -0
  88. package/dist/mcp/mcp-response.js +67 -0
  89. package/dist/mcp/mcp-server.d.ts +19 -0
  90. package/dist/mcp/mcp-server.js +76 -0
  91. package/dist/mcp/resources/project-conventions.d.ts +4 -0
  92. package/dist/mcp/resources/project-conventions.js +50 -0
  93. package/dist/mcp/resources/task-current.d.ts +4 -0
  94. package/dist/mcp/resources/task-current.js +51 -0
  95. package/dist/mcp/tools/forge-add-group-dependency.d.ts +4 -0
  96. package/dist/mcp/tools/forge-add-group-dependency.js +20 -0
  97. package/dist/mcp/tools/forge-add-task-dependency.d.ts +4 -0
  98. package/dist/mcp/tools/forge-add-task-dependency.js +20 -0
  99. package/dist/mcp/tools/forge-ask-human.d.ts +4 -0
  100. package/dist/mcp/tools/forge-ask-human.js +80 -0
  101. package/dist/mcp/tools/forge-column-done.d.ts +4 -0
  102. package/dist/mcp/tools/forge-column-done.js +46 -0
  103. package/dist/mcp/tools/forge-complete-step.d.ts +4 -0
  104. package/dist/mcp/tools/forge-complete-step.js +139 -0
  105. package/dist/mcp/tools/forge-create-task-group.d.ts +4 -0
  106. package/dist/mcp/tools/forge-create-task-group.js +22 -0
  107. package/dist/mcp/tools/forge-create-task.d.ts +4 -0
  108. package/dist/mcp/tools/forge-create-task.js +28 -0
  109. package/dist/mcp/tools/forge-delete-task-group.d.ts +4 -0
  110. package/dist/mcp/tools/forge-delete-task-group.js +17 -0
  111. package/dist/mcp/tools/forge-delete-task.d.ts +4 -0
  112. package/dist/mcp/tools/forge-delete-task.js +17 -0
  113. package/dist/mcp/tools/forge-get-task.d.ts +4 -0
  114. package/dist/mcp/tools/forge-get-task.js +17 -0
  115. package/dist/mcp/tools/forge-merge-back.d.ts +4 -0
  116. package/dist/mcp/tools/forge-merge-back.js +179 -0
  117. package/dist/mcp/tools/forge-release-merge.d.ts +4 -0
  118. package/dist/mcp/tools/forge-release-merge.js +159 -0
  119. package/dist/mcp/tools/forge-release-notes.d.ts +4 -0
  120. package/dist/mcp/tools/forge-release-notes.js +51 -0
  121. package/dist/mcp/tools/forge-release-tag.d.ts +4 -0
  122. package/dist/mcp/tools/forge-release-tag.js +125 -0
  123. package/dist/mcp/tools/forge-remove-group-dependency.d.ts +4 -0
  124. package/dist/mcp/tools/forge-remove-group-dependency.js +19 -0
  125. package/dist/mcp/tools/forge-remove-task-dependency.d.ts +4 -0
  126. package/dist/mcp/tools/forge-remove-task-dependency.js +19 -0
  127. package/dist/mcp/tools/forge-requeue.d.ts +4 -0
  128. package/dist/mcp/tools/forge-requeue.js +39 -0
  129. package/dist/mcp/tools/forge-submit-pr.d.ts +4 -0
  130. package/dist/mcp/tools/forge-submit-pr.js +68 -0
  131. package/dist/mcp/tools/forge-update-status.d.ts +4 -0
  132. package/dist/mcp/tools/forge-update-status.js +77 -0
  133. package/dist/mcp/tools/forge-update-task-group.d.ts +4 -0
  134. package/dist/mcp/tools/forge-update-task-group.js +21 -0
  135. package/dist/mcp/tools/forge-update-task.d.ts +4 -0
  136. package/dist/mcp/tools/forge-update-task.js +25 -0
  137. package/dist/mcp/tools/forge-validate-merge.d.ts +4 -0
  138. package/dist/mcp/tools/forge-validate-merge.js +194 -0
  139. package/dist/mcp/types.d.ts +39 -0
  140. package/dist/mcp/types.js +8 -0
  141. package/dist/methodology/artifact-manager.d.ts +14 -0
  142. package/dist/methodology/artifact-manager.js +55 -0
  143. package/dist/orchestrator/agent-registry.d.ts +53 -0
  144. package/dist/orchestrator/agent-registry.js +96 -0
  145. package/dist/orchestrator/agent-spawner.d.ts +112 -0
  146. package/dist/orchestrator/agent-spawner.js +1518 -0
  147. package/dist/orchestrator/connectors/claude-code-connector.d.ts +3 -0
  148. package/dist/orchestrator/connectors/claude-code-connector.js +60 -0
  149. package/dist/orchestrator/connectors/connector.d.ts +95 -0
  150. package/dist/orchestrator/connectors/connector.js +25 -0
  151. package/dist/orchestrator/connectors/docker-connector.d.ts +14 -0
  152. package/dist/orchestrator/connectors/docker-connector.js +337 -0
  153. package/dist/orchestrator/connectors/native-vm-connector.d.ts +11 -0
  154. package/dist/orchestrator/connectors/native-vm-connector.js +162 -0
  155. package/dist/orchestrator/connectors/opencode-connector.d.ts +3 -0
  156. package/dist/orchestrator/connectors/opencode-connector.js +39 -0
  157. package/dist/orchestrator/connectors/vm-connector.d.ts +3 -0
  158. package/dist/orchestrator/connectors/vm-connector.js +139 -0
  159. package/dist/orchestrator/dispatch-queue.d.ts +43 -0
  160. package/dist/orchestrator/dispatch-queue.js +93 -0
  161. package/dist/orchestrator/resume-handler.d.ts +34 -0
  162. package/dist/orchestrator/resume-handler.js +159 -0
  163. package/dist/orchestrator/stream-batcher.d.ts +16 -0
  164. package/dist/orchestrator/stream-batcher.js +50 -0
  165. package/dist/orchestrator/stream-json-extractor.d.ts +97 -0
  166. package/dist/orchestrator/stream-json-extractor.js +579 -0
  167. package/dist/vm/claude-token-manager.d.ts +36 -0
  168. package/dist/vm/claude-token-manager.js +223 -0
  169. package/dist/vm/docker-executor.d.ts +131 -0
  170. package/dist/vm/docker-executor.js +360 -0
  171. package/dist/vm/docker-process-proxy.d.ts +31 -0
  172. package/dist/vm/docker-process-proxy.js +105 -0
  173. package/dist/vm/forge-vm-agent.d.ts +11 -0
  174. package/dist/vm/forge-vm-agent.js +159 -0
  175. package/dist/vm/git-credentials.d.ts +43 -0
  176. package/dist/vm/git-credentials.js +53 -0
  177. package/dist/vm/image-manager.d.ts +7 -0
  178. package/dist/vm/image-manager.js +61 -0
  179. package/dist/vm/log.d.ts +9 -0
  180. package/dist/vm/log.js +18 -0
  181. package/dist/vm/mcp-bridge.d.ts +63 -0
  182. package/dist/vm/mcp-bridge.js +243 -0
  183. package/dist/vm/mcp-stub.d.ts +15 -0
  184. package/dist/vm/mcp-stub.js +67 -0
  185. package/dist/vm/native-addon.d.ts +69 -0
  186. package/dist/vm/native-addon.js +68 -0
  187. package/dist/vm/provider-resolver.d.ts +7 -0
  188. package/dist/vm/provider-resolver.js +27 -0
  189. package/dist/vm/providers/lima.d.ts +17 -0
  190. package/dist/vm/providers/lima.js +191 -0
  191. package/dist/vm/providers/qemu.d.ts +28 -0
  192. package/dist/vm/providers/qemu.js +260 -0
  193. package/dist/vm/providers/vfkit.d.ts +40 -0
  194. package/dist/vm/providers/vfkit.js +371 -0
  195. package/dist/vm/providers/wsl2.d.ts +27 -0
  196. package/dist/vm/providers/wsl2.js +226 -0
  197. package/dist/vm/session-manager.d.ts +130 -0
  198. package/dist/vm/session-manager.js +494 -0
  199. package/dist/vm/session-snapshot.d.ts +31 -0
  200. package/dist/vm/session-snapshot.js +180 -0
  201. package/dist/vm/types.d.ts +73 -0
  202. package/dist/vm/types.js +2 -0
  203. package/dist/vm/vm-helper-client.d.ts +81 -0
  204. package/dist/vm/vm-helper-client.js +335 -0
  205. package/dist/vm/vm-helper.d.ts +11 -0
  206. package/dist/vm/vm-helper.js +156 -0
  207. package/dist/vm/vm-ipc-protocol.d.ts +84 -0
  208. package/dist/vm/vm-ipc-protocol.js +9 -0
  209. package/dist/vm/vm-manager-facade.d.ts +23 -0
  210. package/dist/vm/vm-manager-facade.js +43 -0
  211. package/dist/vm/vm-manager.d.ts +74 -0
  212. package/dist/vm/vm-manager.js +515 -0
  213. package/dist/vm/vm-process-proxy.d.ts +34 -0
  214. package/dist/vm/vm-process-proxy.js +64 -0
  215. package/dist/vm/vsock-bridge.d.ts +44 -0
  216. package/dist/vm/vsock-bridge.js +140 -0
  217. package/dist/vm/vsock-protocol.d.ts +98 -0
  218. package/dist/vm/vsock-protocol.js +57 -0
  219. package/package.json +65 -0
  220. package/rootfs/Dockerfile +50 -0
  221. package/rootfs/forge-mcp-relay.mjs +158 -0
@@ -0,0 +1,5 @@
1
+ export declare function disconnectAction(options: {
2
+ server?: string;
3
+ org?: string;
4
+ }): Promise<void>;
5
+ //# sourceMappingURL=disconnect.d.ts.map
@@ -0,0 +1,126 @@
1
+ import chalk from "chalk";
2
+ import { getConfig, getServerUrl } from "../config/config-manager.js";
3
+ import { loadToken } from "../config/token-manager.js";
4
+ import { createRestClient } from "../lib/rest-client.js";
5
+ import { isDaemonRunning } from "../lib/daemon.js";
6
+ import { sendIpcCommand, DaemonNotRunningError } from "../lib/ipc-client.js";
7
+ export async function disconnectAction(options) {
8
+ // Check if daemon is running — if so, delegate via IPC
9
+ const daemonPid = isDaemonRunning();
10
+ if (daemonPid) {
11
+ return disconnectViaDaemon(options);
12
+ }
13
+ // No daemon running — fallback to REST-only behavior
14
+ return disconnectViaRest(options);
15
+ }
16
+ /**
17
+ * Print disconnect response orgs or generic message.
18
+ */
19
+ function printDisconnectResponse(response) {
20
+ if ("orgs" in response && response.orgs) {
21
+ for (const org of response.orgs) {
22
+ console.log(chalk.green(`Disconnected from "${org.slug}" (daemon stays alive)`));
23
+ }
24
+ }
25
+ else {
26
+ console.log(chalk.green("Disconnected (daemon stays alive)"));
27
+ }
28
+ console.log(chalk.cyan("Run `forge reconnect` to reconnect."));
29
+ }
30
+ /**
31
+ * Disconnect via IPC when daemon is running (AC #8).
32
+ */
33
+ async function disconnectViaDaemon(options) {
34
+ try {
35
+ const response = await sendIpcCommand({
36
+ cmd: "disconnect",
37
+ org: options.org,
38
+ });
39
+ if (response.ok) {
40
+ printDisconnectResponse(response);
41
+ }
42
+ else {
43
+ console.error(chalk.red(response.error));
44
+ }
45
+ }
46
+ catch (error) {
47
+ if (error instanceof DaemonNotRunningError) {
48
+ // Race condition: daemon stopped between check and IPC call
49
+ console.log(chalk.yellow("Daemon stopped. No active connections to disconnect."));
50
+ }
51
+ else {
52
+ console.error(chalk.red(`Disconnect failed: ${error instanceof Error ? error.message : String(error)}`));
53
+ }
54
+ }
55
+ }
56
+ /**
57
+ * Resolve target organizations for disconnection.
58
+ */
59
+ function resolveTargetOrgs(config, orgFlag) {
60
+ if (config.linkedOrgs && config.linkedOrgs.length > 0) {
61
+ if (orgFlag) {
62
+ // --org flag: disconnect from specific org only
63
+ const match = config.linkedOrgs.find((o) => o.orgSlug === orgFlag);
64
+ if (!match) {
65
+ console.error(chalk.red(`Organization "${orgFlag}" not found in linked orgs. Available: ${config.linkedOrgs.map((o) => o.orgSlug).join(", ")}`));
66
+ return null;
67
+ }
68
+ return [match];
69
+ }
70
+ // No flag: disconnect from ALL linked orgs
71
+ return config.linkedOrgs;
72
+ }
73
+ if (config.defaultOrgId) {
74
+ return [
75
+ {
76
+ orgId: config.defaultOrgId,
77
+ orgSlug: config.defaultOrg ?? "default",
78
+ },
79
+ ];
80
+ }
81
+ return null;
82
+ }
83
+ /**
84
+ * REST-only disconnect fallback when no daemon is running (AC #9).
85
+ * Preserves the original behavior from story 4-0c.
86
+ */
87
+ async function disconnectViaRest(options) {
88
+ const token = loadToken();
89
+ if (!token) {
90
+ console.error(chalk.red("Not authenticated. Run `forge auth login` first."));
91
+ return;
92
+ }
93
+ const config = getConfig();
94
+ const serverUrl = options.server ?? getServerUrl();
95
+ if (!config.cliId) {
96
+ console.error(chalk.red("No CLI ID found. Run `forge connect` first to register this CLI."));
97
+ return;
98
+ }
99
+ // Determine which orgs to disconnect from
100
+ const targetOrgs = resolveTargetOrgs(config, options.org);
101
+ if (!targetOrgs) {
102
+ console.error(chalk.red("No organization configured. Run `forge auth login` to select one."));
103
+ return;
104
+ }
105
+ let successCount = 0;
106
+ let failCount = 0;
107
+ for (const org of targetOrgs) {
108
+ const restClient = createRestClient(serverUrl, token, org.orgId);
109
+ try {
110
+ await restClient.disconnectCli({ cliId: config.cliId });
111
+ console.log(chalk.green(`Disconnected from "${org.orgSlug}" (CLI remains registered)`));
112
+ successCount++;
113
+ }
114
+ catch (error) {
115
+ console.error(chalk.red(`Failed to disconnect from "${org.orgSlug}": ${error instanceof Error ? error.message : String(error)}`));
116
+ failCount++;
117
+ }
118
+ }
119
+ if (targetOrgs.length > 1) {
120
+ console.log(chalk.cyan(`\nDisconnect summary: ${successCount} succeeded, ${failCount} failed`));
121
+ }
122
+ if (successCount > 0) {
123
+ console.log(chalk.cyan("Run `forge connect` to reconnect."));
124
+ }
125
+ }
126
+ //# sourceMappingURL=disconnect.js.map
@@ -0,0 +1,2 @@
1
+ export declare function journalClearAction(projectId?: string): void;
2
+ //# sourceMappingURL=journal-clear.d.ts.map
@@ -0,0 +1,16 @@
1
+ import chalk from "chalk";
2
+ import { EventJournal } from "../lib/event-journal.js";
3
+ export function journalClearAction(projectId = "default") {
4
+ const id = projectId;
5
+ const journal = new EventJournal(id);
6
+ const cursor = journal.getCursor();
7
+ if (!cursor) {
8
+ console.log(chalk.gray(`No journal entries to clear for project "${id}"`));
9
+ return;
10
+ }
11
+ const count = journal.readAll().length;
12
+ console.log(chalk.yellow(`Warning: clearing ${count} journal entries (events not yet synced to Cloud will be lost)`));
13
+ journal.clear(cursor);
14
+ console.log(chalk.green(`Cleared ${count} journal entries for project "${id}"`));
15
+ }
16
+ //# sourceMappingURL=journal-clear.js.map
@@ -0,0 +1,2 @@
1
+ export declare function journalListAction(projectId?: string): void;
2
+ //# sourceMappingURL=journal-list.d.ts.map
@@ -0,0 +1,20 @@
1
+ import chalk from "chalk";
2
+ import { EventJournal } from "../lib/event-journal.js";
3
+ export function journalListAction(projectId = "default") {
4
+ // Default to orgId-scoped journal when no projectId specified (matches connect.ts)
5
+ const id = projectId;
6
+ const journal = new EventJournal(id);
7
+ const entries = journal.readAll();
8
+ if (entries.length === 0) {
9
+ console.log(chalk.gray(`No journal entries for project "${id}"`));
10
+ return;
11
+ }
12
+ const pending = entries.filter((e) => !e.synced).length;
13
+ const lastEntry = entries.at(-1);
14
+ console.log(chalk.bold(`Journal: ${id}`));
15
+ console.log(` Total events: ${entries.length}`);
16
+ console.log(` Pending sync: ${pending}`);
17
+ console.log(` Last event: ${lastEntry.timestamp}`);
18
+ console.log(` Last event type: ${lastEntry.type}`);
19
+ }
20
+ //# sourceMappingURL=journal-list.js.map
@@ -0,0 +1,2 @@
1
+ export declare function journalStatusAction(): void;
2
+ //# sourceMappingURL=journal-status.d.ts.map
@@ -0,0 +1,18 @@
1
+ import chalk from "chalk";
2
+ import { getConfig } from "../config/config-manager.js";
3
+ import { EventJournal } from "../lib/event-journal.js";
4
+ export function journalStatusAction() {
5
+ const config = getConfig();
6
+ // Use orgId-scoped journal (matches connect.ts behavior)
7
+ const journal = new EventJournal(config.defaultOrgId ?? "default");
8
+ const pendingCount = journal.pendingCount();
9
+ const cursor = journal.getCursor();
10
+ const lastTimestamp = journal.lastEventTimestamp();
11
+ console.log(chalk.bold("Sync Status"));
12
+ console.log(` Last sync: ${config.lastSyncTimestamp ?? chalk.gray("never")}`);
13
+ console.log(` Sync cursor: ${config.lastSyncCursor ?? chalk.gray("none")}`);
14
+ console.log(` Pending events: ${pendingCount}`);
15
+ console.log(` Journal cursor: ${cursor || chalk.gray("empty")}`);
16
+ console.log(` Last event at: ${lastTimestamp ?? chalk.gray("no events")}`);
17
+ }
18
+ //# sourceMappingURL=journal-status.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Handler for `forge mcp` command.
3
+ * Creates an MCP server with stdio transport, bridging the agent to Cloud.
4
+ * Each invocation is per-task — spawned by the agent process via MCP config.
5
+ */
6
+ export declare function mcpServeAction(options: {
7
+ taskId: string;
8
+ projectId: string;
9
+ orgId: string;
10
+ workflowId?: string;
11
+ stepId?: string;
12
+ }): Promise<void>;
13
+ //# sourceMappingURL=mcp-serve.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { getServerUrl } from "../config/config-manager.js";
2
+ import { loadToken } from "../config/token-manager.js";
3
+ import { createRestClient } from "../lib/rest-client.js";
4
+ import { createSocketClient } from "../lib/socket-client.js";
5
+ import { startMcpServer } from "../mcp/mcp-server.js";
6
+ /**
7
+ * Handler for `forge mcp` command.
8
+ * Creates an MCP server with stdio transport, bridging the agent to Cloud.
9
+ * Each invocation is per-task — spawned by the agent process via MCP config.
10
+ */
11
+ export async function mcpServeAction(options) {
12
+ const { taskId, projectId, orgId, workflowId, stepId } = options;
13
+ let isShuttingDown = false;
14
+ // Global error handlers — log to stderr so failures are diagnosable.
15
+ // Without these, unhandled errors crash silently and the agent sees no MCP tools.
16
+ process.on("uncaughtException", (err) => {
17
+ if (isShuttingDown)
18
+ return;
19
+ isShuttingDown = true;
20
+ console.error(`[mcp] Uncaught exception: ${err.message}`);
21
+ console.error(err.stack);
22
+ process.exit(1);
23
+ });
24
+ process.on("unhandledRejection", (reason) => {
25
+ if (isShuttingDown)
26
+ return;
27
+ isShuttingDown = true;
28
+ console.error(`[mcp] Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
29
+ if (reason instanceof Error && reason.stack)
30
+ console.error(reason.stack);
31
+ process.exit(1);
32
+ });
33
+ const token = loadToken();
34
+ if (!token) {
35
+ console.error("[mcp] Not authenticated. Cannot start MCP bridge.");
36
+ process.exit(1);
37
+ }
38
+ const serverUrl = getServerUrl();
39
+ const restClient = createRestClient(serverUrl, token, orgId);
40
+ const socketClient = createSocketClient(serverUrl, token, orgId);
41
+ const shutdown = () => {
42
+ if (isShuttingDown)
43
+ return;
44
+ isShuttingDown = true;
45
+ socketClient.disconnect();
46
+ process.exit(0);
47
+ };
48
+ // Handle stdin close (agent disconnected) → clean shutdown
49
+ process.stdin.on("end", shutdown);
50
+ try {
51
+ await startMcpServer({
52
+ taskId,
53
+ projectId,
54
+ orgId,
55
+ restClient,
56
+ socketClient,
57
+ currentTask: null,
58
+ projectRoot: null,
59
+ workflowId,
60
+ stepId,
61
+ }, process.stdin, process.stdout);
62
+ }
63
+ catch (err) {
64
+ console.error(`[mcp] Failed to start MCP server: ${err instanceof Error ? err.message : String(err)}`);
65
+ if (err instanceof Error && err.stack)
66
+ console.error(err.stack);
67
+ socketClient.disconnect();
68
+ process.exit(1);
69
+ }
70
+ // Register signal handlers only after server started successfully
71
+ process.on("SIGTERM", shutdown);
72
+ process.on("SIGINT", shutdown);
73
+ }
74
+ //# sourceMappingURL=mcp-serve.js.map
@@ -0,0 +1,4 @@
1
+ export declare function orgListAction(options: {
2
+ server?: string;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=org-list.d.ts.map
@@ -0,0 +1,56 @@
1
+ import chalk from "chalk";
2
+ import { getConfig, getServerUrl } from "../config/config-manager.js";
3
+ import { loadToken } from "../config/token-manager.js";
4
+ import { createRestClient } from "../lib/rest-client.js";
5
+ async function getOrgConnectionStatus(serverUrl, token, orgId, cliId) {
6
+ try {
7
+ const restClient = createRestClient(serverUrl, token, orgId);
8
+ const state = await restClient.getProjectState();
9
+ const myCli = state.clis.find((c) => c.cliId === cliId);
10
+ if (!myCli)
11
+ return "not registered";
12
+ return myCli.status === "CONNECTED" ? "connected" : "disconnected";
13
+ }
14
+ catch {
15
+ return "unreachable";
16
+ }
17
+ }
18
+ export async function orgListAction(options) {
19
+ const token = loadToken();
20
+ if (!token) {
21
+ console.error(chalk.red("Not authenticated. Run `forge auth login` first."));
22
+ return;
23
+ }
24
+ const config = getConfig();
25
+ const serverUrl = options.server ?? getServerUrl();
26
+ if (!config.linkedOrgs || config.linkedOrgs.length === 0) {
27
+ console.log(chalk.yellow("No linked organizations. Run `forge auth login` to link."));
28
+ return;
29
+ }
30
+ console.log(chalk.bold(`\nLinked Organizations (${config.linkedOrgs.length}):\n`));
31
+ // Parallelize status queries for all orgs
32
+ const statuses = config.cliId
33
+ ? await Promise.all(config.linkedOrgs.map((org) => getOrgConnectionStatus(serverUrl, token, org.orgId, config.cliId)))
34
+ : config.linkedOrgs.map(() => "unknown");
35
+ for (const [i, org] of config.linkedOrgs.entries()) {
36
+ const status = statuses[i] ?? "unknown";
37
+ const isDefault = org.orgId === config.defaultOrgId;
38
+ const marker = isDefault ? chalk.green(" (default)") : "";
39
+ let statusColor;
40
+ if (status === "connected") {
41
+ statusColor = chalk.green;
42
+ }
43
+ else if (status === "unreachable") {
44
+ statusColor = chalk.red;
45
+ }
46
+ else {
47
+ statusColor = chalk.yellow;
48
+ }
49
+ const displayName = org.orgName ?? org.orgSlug;
50
+ console.log(` ${chalk.bold(displayName)}${marker}`);
51
+ console.log(` Slug: ${org.orgSlug}`);
52
+ console.log(` Status: ${statusColor(status)}`);
53
+ console.log();
54
+ }
55
+ }
56
+ //# sourceMappingURL=org-list.js.map
@@ -0,0 +1,4 @@
1
+ export declare function orgUnlinkAction(slug: string, options: {
2
+ server?: string;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=org-unlink.d.ts.map
@@ -0,0 +1,63 @@
1
+ import { createInterface } from "node:readline";
2
+ import chalk from "chalk";
3
+ import { getConfig, getServerUrl, removeLinkedOrg, } from "../config/config-manager.js";
4
+ import { loadToken } from "../config/token-manager.js";
5
+ import { createRestClient } from "../lib/rest-client.js";
6
+ function confirm(question) {
7
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
8
+ return new Promise((resolve) => {
9
+ rl.question(question, (answer) => {
10
+ rl.close();
11
+ resolve(answer.toLowerCase() === "y");
12
+ });
13
+ });
14
+ }
15
+ export async function orgUnlinkAction(slug, options) {
16
+ const token = loadToken();
17
+ if (!token) {
18
+ console.error(chalk.red("Not authenticated. Run `forge auth login` first."));
19
+ return;
20
+ }
21
+ const config = getConfig();
22
+ const serverUrl = options.server ?? getServerUrl();
23
+ if (!config.linkedOrgs || config.linkedOrgs.length === 0) {
24
+ console.error(chalk.red("No linked organizations. Run `forge auth login` first."));
25
+ return;
26
+ }
27
+ const match = config.linkedOrgs.find((o) => o.orgSlug === slug);
28
+ if (!match) {
29
+ console.error(chalk.red(`Organization "${slug}" not found in linked orgs. Available: ${config.linkedOrgs.map((o) => o.orgSlug).join(", ")}`));
30
+ return;
31
+ }
32
+ if (!config.cliId) {
33
+ console.error(chalk.red("No CLI ID found. Run `forge connect` first to register this CLI."));
34
+ return;
35
+ }
36
+ // Warn if this is the last linked org
37
+ const isLastOrg = config.linkedOrgs.length === 1;
38
+ if (isLastOrg) {
39
+ console.warn(chalk.yellow("This is your last linked org. Run `forge auth logout` to fully disconnect."));
40
+ }
41
+ const confirmed = await confirm(`Are you sure you want to unlink CLI from "${slug}"? This will remove the CLI device registration. (y/N) `);
42
+ if (!confirmed) {
43
+ console.log("Cancelled.");
44
+ return;
45
+ }
46
+ const restClient = createRestClient(serverUrl, token, match.orgId);
47
+ // Disconnect WebSocket first (if connected), then unlink
48
+ try {
49
+ await restClient.disconnectCli({ cliId: config.cliId });
50
+ }
51
+ catch {
52
+ // Ignore disconnect errors — CLI may not be connected
53
+ }
54
+ try {
55
+ await restClient.unlinkCli({ cliId: config.cliId });
56
+ removeLinkedOrg(slug);
57
+ console.log(chalk.green(`CLI unlinked from "${slug}"`));
58
+ }
59
+ catch (error) {
60
+ console.error(chalk.red(`Failed to unlink from "${slug}": ${error instanceof Error ? error.message : String(error)}`));
61
+ }
62
+ }
63
+ //# sourceMappingURL=org-unlink.js.map
@@ -0,0 +1,4 @@
1
+ export declare function reconnectAction(options: {
2
+ org?: string;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=reconnect.d.ts.map
@@ -0,0 +1,46 @@
1
+ import chalk from "chalk";
2
+ import { sendIpcCommand, DaemonNotRunningError } from "../lib/ipc-client.js";
3
+ /**
4
+ * Print reconnect status message.
5
+ */
6
+ function printReconnectStatus(response) {
7
+ if ("status" in response) {
8
+ if (response.status === "already_connected") {
9
+ console.log(chalk.green("Already connected."));
10
+ }
11
+ else {
12
+ console.log(chalk.green("Reconnecting..."));
13
+ }
14
+ }
15
+ if ("orgs" in response && response.orgs) {
16
+ for (const org of response.orgs) {
17
+ const status = org.connected
18
+ ? chalk.green("connected")
19
+ : chalk.yellow("reconnecting");
20
+ console.log(` ${org.slug}: ${status}`);
21
+ }
22
+ }
23
+ }
24
+ export async function reconnectAction(options) {
25
+ try {
26
+ const response = await sendIpcCommand({
27
+ cmd: "reconnect",
28
+ org: options.org,
29
+ });
30
+ if (response.ok) {
31
+ printReconnectStatus(response);
32
+ }
33
+ else {
34
+ console.error(chalk.red(response.error));
35
+ }
36
+ }
37
+ catch (error) {
38
+ if (error instanceof DaemonNotRunningError) {
39
+ console.error(chalk.red("No daemon running. Run `forge connect` first."));
40
+ }
41
+ else {
42
+ console.error(chalk.red(`Reconnect failed: ${error instanceof Error ? error.message : String(error)}`));
43
+ }
44
+ }
45
+ }
46
+ //# sourceMappingURL=reconnect.js.map
@@ -0,0 +1,2 @@
1
+ export declare function releaseOpenAction(releaseId: string): Promise<void>;
2
+ //# sourceMappingURL=release-open.d.ts.map
@@ -0,0 +1,43 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import chalk from "chalk";
4
+ import { getConfig, getServerUrl } from "../config/config-manager.js";
5
+ import { loadToken } from "../config/token-manager.js";
6
+ import { createRestClient } from "../lib/rest-client.js";
7
+ import { cloneAndCheckout } from "../lib/clone-and-checkout.js";
8
+ import { openDirectory } from "../lib/open-directory.js";
9
+ export async function releaseOpenAction(releaseId) {
10
+ const token = loadToken();
11
+ if (!token) {
12
+ console.error(chalk.red('Not authenticated. Run "forge auth login" first.'));
13
+ process.exitCode = 1;
14
+ return;
15
+ }
16
+ const config = getConfig();
17
+ const orgId = config.defaultOrgId;
18
+ if (!orgId) {
19
+ console.error(chalk.red('No organization selected. Run "forge connect" first.'));
20
+ process.exitCode = 1;
21
+ return;
22
+ }
23
+ const serverUrl = getServerUrl();
24
+ const client = createRestClient(serverUrl, token, orgId);
25
+ const release = await client.getRelease(releaseId);
26
+ const branch = release.releaseBranch ?? release.targetBranch;
27
+ if (!branch) {
28
+ console.error(chalk.red("Release has no branch assigned yet."));
29
+ process.exitCode = 1;
30
+ return;
31
+ }
32
+ if (!release.project.repoUrl) {
33
+ console.error(chalk.red("Project has no repository URL configured."));
34
+ process.exitCode = 1;
35
+ return;
36
+ }
37
+ const branchSlug = branch.replaceAll("/", "--");
38
+ const targetDir = join(homedir(), ".forgeai", "open", release.project.slug, branchSlug);
39
+ await cloneAndCheckout(release.project.repoUrl, branch, targetDir);
40
+ openDirectory(targetDir, config.opener ?? "terminal");
41
+ console.log(chalk.green(`Opened release "${release.version}" at ${targetDir}`));
42
+ }
43
+ //# sourceMappingURL=release-open.js.map
@@ -0,0 +1,2 @@
1
+ export declare function sessionRespondAction(taskId: string, response: string): Promise<void>;
2
+ //# sourceMappingURL=session-respond.d.ts.map
@@ -0,0 +1,29 @@
1
+ import chalk from "chalk";
2
+ import { getConfig, getServerUrl } from "../config/config-manager.js";
3
+ import { loadToken } from "../config/token-manager.js";
4
+ import { createRestClient } from "../lib/rest-client.js";
5
+ export async function sessionRespondAction(taskId, response) {
6
+ const token = loadToken();
7
+ if (!token) {
8
+ console.error(chalk.red("Not authenticated. Run `forge auth login` first."));
9
+ process.exitCode = 1;
10
+ return;
11
+ }
12
+ const config = getConfig();
13
+ const serverUrl = getServerUrl();
14
+ if (!config.defaultOrgId) {
15
+ console.error(chalk.red("No organization configured. Run `forge auth login` to select one."));
16
+ process.exitCode = 1;
17
+ return;
18
+ }
19
+ const restClient = createRestClient(serverUrl, token, config.defaultOrgId);
20
+ try {
21
+ await restClient.respondToAgent(taskId, response);
22
+ console.log(chalk.green("Response sent — agent will resume shortly."));
23
+ }
24
+ catch (error) {
25
+ console.error(chalk.red(`Failed to send response: ${error instanceof Error ? error.message : String(error)}`));
26
+ process.exitCode = 1;
27
+ }
28
+ }
29
+ //# sourceMappingURL=session-respond.js.map
@@ -0,0 +1,2 @@
1
+ export declare function taskOpenAction(taskId: string): Promise<void>;
2
+ //# sourceMappingURL=task-open.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import chalk from "chalk";
4
+ import { getConfig, getServerUrl } from "../config/config-manager.js";
5
+ import { loadToken } from "../config/token-manager.js";
6
+ import { createRestClient } from "../lib/rest-client.js";
7
+ import { cloneAndCheckout } from "../lib/clone-and-checkout.js";
8
+ import { openDirectory } from "../lib/open-directory.js";
9
+ export async function taskOpenAction(taskId) {
10
+ const token = loadToken();
11
+ if (!token) {
12
+ console.error(chalk.red('Not authenticated. Run "forge auth login" first.'));
13
+ process.exitCode = 1;
14
+ return;
15
+ }
16
+ const config = getConfig();
17
+ const orgId = config.defaultOrgId;
18
+ if (!orgId) {
19
+ console.error(chalk.red('No organization selected. Run "forge connect" first.'));
20
+ process.exitCode = 1;
21
+ return;
22
+ }
23
+ const serverUrl = getServerUrl();
24
+ const client = createRestClient(serverUrl, token, orgId);
25
+ const task = await client.getTask(taskId);
26
+ if (!task.taskBranch) {
27
+ console.error(chalk.red("Task has no branch assigned yet."));
28
+ process.exitCode = 1;
29
+ return;
30
+ }
31
+ if (!task.project?.repoUrl) {
32
+ console.error(chalk.red("Project has no repository URL configured."));
33
+ process.exitCode = 1;
34
+ return;
35
+ }
36
+ const branchSlug = task.taskBranch.replaceAll("/", "--");
37
+ const targetDir = join(homedir(), ".forgeai", "open", task.project.slug, branchSlug);
38
+ await cloneAndCheckout(task.project.repoUrl, task.taskBranch, targetDir);
39
+ openDirectory(targetDir, config.opener ?? "terminal");
40
+ console.log(chalk.green(`Opened task "${task.title}" at ${targetDir}`));
41
+ }
42
+ //# sourceMappingURL=task-open.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * forge vm init — Check Docker availability and pull/build agent image.
3
+ */
4
+ export declare function vmInitAction(): Promise<void>;
5
+ /**
6
+ * forge vm status — List active Docker container sessions.
7
+ */
8
+ export declare function vmStatusAction(): Promise<void>;
9
+ /**
10
+ * forge vm cleanup — Remove stopped session containers.
11
+ */
12
+ export declare function vmCleanupAction(): Promise<void>;
13
+ /**
14
+ * forge vm stop-all — Stop all containers and shut down.
15
+ */
16
+ export declare function vmStopAllAction(): Promise<void>;
17
+ /**
18
+ * forge auth setup-claude — Obtain a long-lived Claude Code OAuth token.
19
+ */
20
+ export declare function setupClaudeTokenAction(): Promise<void>;
21
+ //# sourceMappingURL=vm.d.ts.map