@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,29 @@
1
+ import type { McpSessionContext } from "./types.js";
2
+ type McpToolResult = {
3
+ isError?: boolean;
4
+ content: {
5
+ type: "text";
6
+ text: string;
7
+ }[];
8
+ };
9
+ /** Wrap any data in a standard MCP success response. */
10
+ export declare function mcpSuccess(data: unknown): McpToolResult;
11
+ /** Wrap any data in a standard MCP error response. */
12
+ export declare function mcpError(data: unknown): McpToolResult;
13
+ /** Return a "socket not connected" error for a given action. */
14
+ export declare function mcpNotConnectedError(action: string): McpToolResult;
15
+ /** Return a network error from an unknown caught error. */
16
+ export declare function mcpNetworkError(error: unknown, retryHint?: string): McpToolResult;
17
+ /** Return a "task not found" error. */
18
+ export declare function mcpTaskNotFoundError(taskId: string): McpToolResult;
19
+ /**
20
+ * Execute a socket emit call with standard connection check and error handling.
21
+ * Covers the most common MCP tool pattern: check connection → emit → return response.
22
+ */
23
+ export declare function executeSocketEmit(context: McpSessionContext, action: string, emitFn: () => Promise<unknown>): Promise<McpToolResult>;
24
+ /**
25
+ * Handle a REST client error, returning task-not-found for 404 or network error otherwise.
26
+ */
27
+ export declare function handleRestError(error: unknown, taskId: string): McpToolResult;
28
+ export {};
29
+ //# sourceMappingURL=mcp-response.d.ts.map
@@ -0,0 +1,67 @@
1
+ import { MCP_ERROR_CODE } from "./types.js";
2
+ import { RestClientError } from "../lib/rest-client.js";
3
+ /** Wrap any data in a standard MCP success response. */
4
+ export function mcpSuccess(data) {
5
+ return {
6
+ content: [{ type: "text", text: JSON.stringify(data) }],
7
+ };
8
+ }
9
+ /** Wrap any data in a standard MCP error response. */
10
+ export function mcpError(data) {
11
+ return {
12
+ isError: true,
13
+ content: [{ type: "text", text: JSON.stringify(data) }],
14
+ };
15
+ }
16
+ /** Return a "socket not connected" error for a given action. */
17
+ export function mcpNotConnectedError(action) {
18
+ return mcpError({
19
+ error: MCP_ERROR_CODE.NETWORK_ERROR,
20
+ message: `Socket not connected. Cannot ${action}.`,
21
+ retryHint: "Cloud may be temporarily unavailable. Retry in a few seconds.",
22
+ });
23
+ }
24
+ /** Return a network error from an unknown caught error. */
25
+ export function mcpNetworkError(error, retryHint = "Cloud may be temporarily unavailable. Retry in a few seconds.") {
26
+ return mcpError({
27
+ error: MCP_ERROR_CODE.NETWORK_ERROR,
28
+ message: error instanceof Error ? error.message : String(error),
29
+ retryHint,
30
+ });
31
+ }
32
+ /** Return a "task not found" error. */
33
+ export function mcpTaskNotFoundError(taskId) {
34
+ return mcpError({
35
+ error: MCP_ERROR_CODE.TASK_NOT_FOUND,
36
+ message: `Task "${taskId}" not found`,
37
+ });
38
+ }
39
+ /**
40
+ * Execute a socket emit call with standard connection check and error handling.
41
+ * Covers the most common MCP tool pattern: check connection → emit → return response.
42
+ */
43
+ export async function executeSocketEmit(context, action, emitFn) {
44
+ if (!context.socketClient.socket.connected) {
45
+ return mcpNotConnectedError(action);
46
+ }
47
+ try {
48
+ const response = (await emitFn());
49
+ if (response.error) {
50
+ return mcpError(response);
51
+ }
52
+ return mcpSuccess(response);
53
+ }
54
+ catch (error) {
55
+ return mcpNetworkError(error);
56
+ }
57
+ }
58
+ /**
59
+ * Handle a REST client error, returning task-not-found for 404 or network error otherwise.
60
+ */
61
+ export function handleRestError(error, taskId) {
62
+ if (error instanceof RestClientError && error.status === 404) {
63
+ return mcpTaskNotFoundError(taskId);
64
+ }
65
+ return mcpNetworkError(error);
66
+ }
67
+ //# sourceMappingURL=mcp-response.js.map
@@ -0,0 +1,19 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import type { McpSessionContext } from "./types.js";
4
+ /**
5
+ * Create and configure an MCP server instance for a single agent session.
6
+ * Registers all ForgeAI tools and resources.
7
+ */
8
+ export declare function createMcpServer(context: McpSessionContext): McpServer;
9
+ /**
10
+ * Create a stdio transport for the MCP server.
11
+ * Uses provided streams or defaults to process.stdin/stdout.
12
+ */
13
+ export declare function createStdioTransport(stdin?: NodeJS.ReadableStream, stdout?: NodeJS.WritableStream): StdioServerTransport;
14
+ /**
15
+ * Start an MCP server with stdio transport.
16
+ * Convenience function for daemon integration.
17
+ */
18
+ export declare function startMcpServer(context: McpSessionContext, stdin?: NodeJS.ReadableStream, stdout?: NodeJS.WritableStream): Promise<McpServer>;
19
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -0,0 +1,76 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { registerForgeGetTask } from "./tools/forge-get-task.js";
4
+ import { registerForgeUpdateStatus } from "./tools/forge-update-status.js";
5
+ import { registerForgeAskHuman } from "./tools/forge-ask-human.js";
6
+ import { registerForgeRequeue } from "./tools/forge-requeue.js";
7
+ import { registerForgeColumnDone } from "./tools/forge-column-done.js";
8
+ import { registerForgeCompleteStep } from "./tools/forge-complete-step.js";
9
+ import { registerForgeCreateTaskGroup } from "./tools/forge-create-task-group.js";
10
+ import { registerForgeAddGroupDependency } from "./tools/forge-add-group-dependency.js";
11
+ import { registerForgeCreateTask } from "./tools/forge-create-task.js";
12
+ import { registerForgeAddTaskDependency } from "./tools/forge-add-task-dependency.js";
13
+ import { registerForgeUpdateTask } from "./tools/forge-update-task.js";
14
+ import { registerForgeDeleteTask } from "./tools/forge-delete-task.js";
15
+ import { registerForgeUpdateTaskGroup } from "./tools/forge-update-task-group.js";
16
+ import { registerForgeDeleteTaskGroup } from "./tools/forge-delete-task-group.js";
17
+ import { registerForgeRemoveTaskDependency } from "./tools/forge-remove-task-dependency.js";
18
+ import { registerForgeRemoveGroupDependency } from "./tools/forge-remove-group-dependency.js";
19
+ import { registerForgeMergeBack } from "./tools/forge-merge-back.js";
20
+ import { registerForgeValidateMerge } from "./tools/forge-validate-merge.js";
21
+ import { registerForgeReleaseMerge } from "./tools/forge-release-merge.js";
22
+ import { registerForgeReleaseTag } from "./tools/forge-release-tag.js";
23
+ import { registerForgeReleaseNotes } from "./tools/forge-release-notes.js";
24
+ import { registerTaskCurrentResource } from "./resources/task-current.js";
25
+ import { registerProjectConventionsResource } from "./resources/project-conventions.js";
26
+ /**
27
+ * Create and configure an MCP server instance for a single agent session.
28
+ * Registers all ForgeAI tools and resources.
29
+ */
30
+ export function createMcpServer(context) {
31
+ const server = new McpServer({ name: "forgeai", version: "1.0.0" }, { capabilities: { tools: {}, resources: {} } });
32
+ // Register tools
33
+ registerForgeGetTask(server, context);
34
+ registerForgeUpdateStatus(server, context);
35
+ registerForgeAskHuman(server, context);
36
+ registerForgeRequeue(server, context);
37
+ registerForgeColumnDone(server, context);
38
+ registerForgeCompleteStep(server, context);
39
+ registerForgeCreateTaskGroup(server, context);
40
+ registerForgeAddGroupDependency(server, context);
41
+ registerForgeCreateTask(server, context);
42
+ registerForgeAddTaskDependency(server, context);
43
+ registerForgeUpdateTask(server, context);
44
+ registerForgeDeleteTask(server, context);
45
+ registerForgeUpdateTaskGroup(server, context);
46
+ registerForgeDeleteTaskGroup(server, context);
47
+ registerForgeRemoveTaskDependency(server, context);
48
+ registerForgeRemoveGroupDependency(server, context);
49
+ registerForgeMergeBack(server, context);
50
+ registerForgeValidateMerge(server, context);
51
+ registerForgeReleaseMerge(server, context);
52
+ registerForgeReleaseTag(server, context);
53
+ registerForgeReleaseNotes(server, context);
54
+ // Register resources
55
+ registerTaskCurrentResource(server, context);
56
+ registerProjectConventionsResource(server, context);
57
+ return server;
58
+ }
59
+ /**
60
+ * Create a stdio transport for the MCP server.
61
+ * Uses provided streams or defaults to process.stdin/stdout.
62
+ */
63
+ export function createStdioTransport(stdin, stdout) {
64
+ return new StdioServerTransport(stdin, stdout);
65
+ }
66
+ /**
67
+ * Start an MCP server with stdio transport.
68
+ * Convenience function for daemon integration.
69
+ */
70
+ export async function startMcpServer(context, stdin, stdout) {
71
+ const server = createMcpServer(context);
72
+ const transport = createStdioTransport(stdin, stdout);
73
+ await server.connect(transport);
74
+ return server;
75
+ }
76
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerProjectConventionsResource(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=project-conventions.d.ts.map
@@ -0,0 +1,50 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ export function registerProjectConventionsResource(server, context) {
4
+ server.registerResource("Project Conventions", "forge://project/conventions", { mimeType: "text/plain" }, async (uri) => {
5
+ if (!context.projectRoot) {
6
+ return {
7
+ contents: [
8
+ {
9
+ uri: uri.href,
10
+ mimeType: "text/plain",
11
+ text: "No project root configured. Cannot read conventions.",
12
+ },
13
+ ],
14
+ };
15
+ }
16
+ const sections = [];
17
+ // Read CLAUDE.md
18
+ try {
19
+ const claudeMd = await readFile(join(context.projectRoot, "CLAUDE.md"), "utf-8");
20
+ sections.push("# CLAUDE.md\n\n" + claudeMd);
21
+ }
22
+ catch {
23
+ sections.push("# CLAUDE.md\n\nNo CLAUDE.md found in project root.");
24
+ }
25
+ // Read architecture docs if they exist
26
+ const archPaths = [
27
+ "docs/architecture.md",
28
+ "_bmad-output/planning-artifacts/architecture.md",
29
+ ];
30
+ for (const archPath of archPaths) {
31
+ try {
32
+ const content = await readFile(join(context.projectRoot, archPath), "utf-8");
33
+ sections.push(`\n\n# ${archPath}\n\n${content}`);
34
+ }
35
+ catch {
36
+ // File doesn't exist, skip
37
+ }
38
+ }
39
+ return {
40
+ contents: [
41
+ {
42
+ uri: uri.href,
43
+ mimeType: "text/plain",
44
+ text: sections.join("\n"),
45
+ },
46
+ ],
47
+ };
48
+ });
49
+ }
50
+ //# sourceMappingURL=project-conventions.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerTaskCurrentResource(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=task-current.d.ts.map
@@ -0,0 +1,51 @@
1
+ export function registerTaskCurrentResource(server, context) {
2
+ let fetchPromise = null;
3
+ server.registerResource("Current Task", "forge://task/current", { mimeType: "application/json" }, async (uri) => {
4
+ if (!context.currentTask) {
5
+ // Deduplicate concurrent fetches — only one REST call at a time
6
+ fetchPromise ??= context.restClient
7
+ .getTask(context.taskId)
8
+ .then((task) => {
9
+ context.currentTask = {
10
+ id: task.id,
11
+ title: task.title,
12
+ description: task.description ?? "",
13
+ status: task.kanbanSubStatus ?? "UNKNOWN",
14
+ kanbanColumn: task.kanbanColumn,
15
+ assigneeId: task.assigneeId,
16
+ cliDeviceId: task.cliDeviceId,
17
+ };
18
+ })
19
+ .catch((error) => {
20
+ console.warn(`[MCP] Failed to fetch current task ${context.taskId}:`, error);
21
+ })
22
+ .finally(() => {
23
+ fetchPromise = null;
24
+ });
25
+ await fetchPromise;
26
+ }
27
+ if (!context.currentTask) {
28
+ return {
29
+ contents: [
30
+ {
31
+ uri: uri.href,
32
+ mimeType: "application/json",
33
+ text: JSON.stringify({
34
+ error: "Failed to fetch current task",
35
+ }),
36
+ },
37
+ ],
38
+ };
39
+ }
40
+ return {
41
+ contents: [
42
+ {
43
+ uri: uri.href,
44
+ mimeType: "application/json",
45
+ text: JSON.stringify(context.currentTask),
46
+ },
47
+ ],
48
+ };
49
+ });
50
+ }
51
+ //# sourceMappingURL=task-current.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerForgeAddGroupDependency(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=forge-add-group-dependency.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { ForgeAddGroupDependencyInputSchema } from "@getforgeai/protocol";
2
+ import { executeSocketEmit } from "../mcp-response.js";
3
+ const shape = ForgeAddGroupDependencyInputSchema.shape;
4
+ export function registerForgeAddGroupDependency(server, context) {
5
+ server.registerTool("forge_add_group_dependency", {
6
+ description: "Add a dependency between two task groups. The blocked group cannot start until the blocking group is done. References groups by name within the current project.",
7
+ inputSchema: {
8
+ blockingGroupName: shape.blockingGroupName.describe("Name of the group that must complete first"),
9
+ blockedGroupName: shape.blockedGroupName.describe("Name of the group that is blocked"),
10
+ },
11
+ }, async (params) => {
12
+ return executeSocketEmit(context, "add group dependency", () => context.socketClient.emitTaskGroupAddDependency({
13
+ projectId: context.projectId,
14
+ workflowId: context.workflowId,
15
+ blockingGroupName: params.blockingGroupName,
16
+ blockedGroupName: params.blockedGroupName,
17
+ }));
18
+ });
19
+ }
20
+ //# sourceMappingURL=forge-add-group-dependency.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerForgeAddTaskDependency(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=forge-add-task-dependency.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { ForgeAddTaskDependencyInputSchema } from "@getforgeai/protocol";
2
+ import { executeSocketEmit } from "../mcp-response.js";
3
+ const shape = ForgeAddTaskDependencyInputSchema.shape;
4
+ export function registerForgeAddTaskDependency(server, context) {
5
+ server.registerTool("forge_add_task_dependency", {
6
+ description: "Add a dependency between two tasks. The blocked task cannot start until the blocking task is done. References tasks by title within the current project (most recently created match).",
7
+ inputSchema: {
8
+ blockingTaskTitle: shape.blockingTaskTitle.describe("Title of the task that must complete first"),
9
+ blockedTaskTitle: shape.blockedTaskTitle.describe("Title of the task that is blocked"),
10
+ },
11
+ }, async (params) => {
12
+ return executeSocketEmit(context, "add task dependency", () => context.socketClient.emitTaskAddDependency({
13
+ projectId: context.projectId,
14
+ workflowId: context.workflowId,
15
+ blockingTaskTitle: params.blockingTaskTitle,
16
+ blockedTaskTitle: params.blockedTaskTitle,
17
+ }));
18
+ });
19
+ }
20
+ //# sourceMappingURL=forge-add-task-dependency.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerForgeAskHuman(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=forge-ask-human.d.ts.map
@@ -0,0 +1,80 @@
1
+ import { z } from "zod";
2
+ import { mcpSuccess, mcpError, mcpNetworkError, handleRestError, } from "../mcp-response.js";
3
+ export function registerForgeAskHuman(server, context) {
4
+ server.registerTool("forge_ask_human", {
5
+ description: "Ask a human a question. Creates a blocking question on the task, sets kanbanSubStatus to AWAITING_HUMAN, and returns a waiting signal. The agent should checkpoint and stop after calling this.",
6
+ inputSchema: {
7
+ taskId: z.string().min(1).describe("Task ID to attach the question to"),
8
+ question: z.string().min(1).describe("Question text for the human"),
9
+ context: z
10
+ .string()
11
+ .optional()
12
+ .describe("Optional context to help the human answer"),
13
+ },
14
+ }, async ({ taskId, question, context: questionContext }) => {
15
+ // Check current task status
16
+ let currentTask;
17
+ try {
18
+ currentTask = await context.restClient.getTask(taskId);
19
+ }
20
+ catch (error) {
21
+ return handleRestError(error, taskId);
22
+ }
23
+ // Validate kanbanSubStatus allows transition to AWAITING_HUMAN
24
+ // Only ACTIVE tasks can ask a human (per state machine)
25
+ if (currentTask.kanbanSubStatus === "AWAITING_HUMAN") {
26
+ return mcpError({
27
+ error: "INVALID_STATUS",
28
+ message: "Task is already in AWAITING_HUMAN state. Wait for the human to respond before asking another question.",
29
+ });
30
+ }
31
+ if (currentTask.kanbanSubStatus !== "ACTIVE") {
32
+ return mcpError({
33
+ error: "INVALID_STATUS",
34
+ message: `Cannot ask human from kanbanSubStatus "${currentTask.kanbanSubStatus}". Task must be ACTIVE.`,
35
+ });
36
+ }
37
+ // 1. Persist AWAITING_HUMAN + question to DB via REST FIRST.
38
+ // This MUST complete before returning so that the DB has the correct
39
+ // status before the agent exits and the fallback release checks it.
40
+ // The REST route also broadcasts socket events for real-time UI.
41
+ try {
42
+ await context.restClient.updateTaskSubStatus({
43
+ taskId,
44
+ kanbanSubStatus: "AWAITING_HUMAN",
45
+ agentQuestion: question,
46
+ });
47
+ }
48
+ catch (error) {
49
+ return mcpNetworkError(error, "Failed to persist AWAITING_HUMAN status. Retry in a few seconds.");
50
+ }
51
+ // Mark task for container preservation (--continue on resume)
52
+ const { markAskHuman } = await import("../../orchestrator/agent-spawner.js");
53
+ markAskHuman(taskId);
54
+ // 2. Emit socket events for real-time UI updates (fire-and-forget)
55
+ try {
56
+ if (context.socketClient.socket.connected) {
57
+ context.socketClient.emitTaskAskHuman({
58
+ taskId,
59
+ question,
60
+ context: questionContext,
61
+ projectId: context.projectId,
62
+ });
63
+ context.socketClient.emitTaskUpdate({
64
+ taskId,
65
+ kanbanSubStatus: "AWAITING_HUMAN",
66
+ projectId: context.projectId,
67
+ });
68
+ }
69
+ }
70
+ catch {
71
+ // Socket.io broadcast failed — DB is already updated, UI will catch up
72
+ }
73
+ return mcpSuccess({
74
+ taskId,
75
+ status: "waiting",
76
+ message: "Question submitted. Task kanbanSubStatus changed to AWAITING_HUMAN. Checkpoint your work and stop — you will be resumed when the human responds.",
77
+ });
78
+ });
79
+ }
80
+ //# sourceMappingURL=forge-ask-human.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerForgeColumnDone(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=forge-column-done.d.ts.map
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ import { ensureChangesCommittedAndPushed } from "../../lib/branch-manager.js";
3
+ import { mcpSuccess, mcpError, handleRestError } from "../mcp-response.js";
4
+ export function registerForgeColumnDone(server, context) {
5
+ server.registerTool("forge_column_done", {
6
+ description: "Signal that you have completed your work for the current column. " +
7
+ "The task advances to the next column in the pipeline and will be " +
8
+ "re-dispatched to an available agent. If the task is in the final " +
9
+ "column, it is marked as done without advancing.",
10
+ inputSchema: {
11
+ taskId: z.string().min(1).describe("Task ID to advance"),
12
+ },
13
+ }, async ({ taskId }) => {
14
+ try {
15
+ // Auto-commit/push guard: ensure all changes are saved before advancing.
16
+ // Only runs when projectRoot is set (local worktree mode).
17
+ // In VM mode (projectRoot is null), the agent manages its own git commits.
18
+ let guardStatus = "";
19
+ if (context.projectRoot) {
20
+ const guardResult = await ensureChangesCommittedAndPushed(context.projectRoot, "chore: auto-commit before column advance");
21
+ if (guardResult.error) {
22
+ return mcpError({
23
+ error: "GUARD_FAILED",
24
+ message: `Cannot advance column: failed to commit/push changes. ${guardResult.error}`,
25
+ retryHint: "Fix the git issue (commit and push your changes manually) then retry forge_column_done.",
26
+ });
27
+ }
28
+ guardStatus = [
29
+ guardResult.committed ? "Changes were auto-committed." : "",
30
+ guardResult.pushed ? "Changes were auto-pushed." : "",
31
+ ]
32
+ .filter(Boolean)
33
+ .join(" ");
34
+ }
35
+ const result = await context.restClient.advanceTask(taskId);
36
+ return mcpSuccess({
37
+ ...result,
38
+ ...(guardStatus ? { guardStatus } : {}),
39
+ });
40
+ }
41
+ catch (error) {
42
+ return handleRestError(error, taskId);
43
+ }
44
+ });
45
+ }
46
+ //# sourceMappingURL=forge-column-done.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerForgeCompleteStep(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=forge-complete-step.d.ts.map
@@ -0,0 +1,139 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { z } from "zod";
4
+ import { ForgeCompleteStepDeliverableSchema } from "@getforgeai/protocol";
5
+ import { MCP_ERROR_CODE } from "../types.js";
6
+ import { ensureChangesCommittedAndPushed } from "../../lib/branch-manager.js";
7
+ const MAX_FILE_CONTENT_BYTES = 100 * 1024; // 100 KB
8
+ export function registerForgeCompleteStep(server, context) {
9
+ server.registerTool("forge_complete_step", {
10
+ description: "Signal that you have completed the current workflow step and provide the list of deliverables (files produced). Each deliverable has a filePath (relative to repo root) and a label (human-readable description). The user will review the deliverables before advancing to the next step.",
11
+ inputSchema: {
12
+ deliverables: z
13
+ .array(ForgeCompleteStepDeliverableSchema)
14
+ .min(1)
15
+ .describe("List of deliverables produced for this step"),
16
+ },
17
+ }, async ({ deliverables }) => {
18
+ const { workflowId, stepId } = context;
19
+ if (!workflowId || !stepId) {
20
+ return {
21
+ isError: true,
22
+ content: [
23
+ {
24
+ type: "text",
25
+ text: JSON.stringify({
26
+ error: MCP_ERROR_CODE.VALIDATION_ERROR,
27
+ message: "No workflow context available. This tool can only be used during a workflow step execution.",
28
+ }),
29
+ },
30
+ ],
31
+ };
32
+ }
33
+ try {
34
+ // Read file contents for each deliverable (best-effort)
35
+ const enrichedDeliverables = await Promise.all(deliverables.map(async (d) => {
36
+ try {
37
+ let raw;
38
+ if (context.projectRoot) {
39
+ // Local mode — read directly from filesystem
40
+ const absPath = resolve(context.projectRoot, d.filePath);
41
+ raw = await readFile(absPath, "utf-8");
42
+ }
43
+ else {
44
+ // Docker mode — read via docker exec in the container
45
+ const { getVmManager } = await import("../../vm/vm-manager-facade.js");
46
+ const sm = getVmManager();
47
+ const session = sm.getSessionByTaskId(context.taskId);
48
+ if (!session)
49
+ return d;
50
+ const { execInContainer } = await import("../../vm/docker-executor.js");
51
+ const result = await execInContainer(session.containerId, ["cat", `/session/workspace/${d.filePath}`], { timeout: 10_000 });
52
+ if (result.exitCode !== 0)
53
+ return d;
54
+ raw = result.stdout;
55
+ }
56
+ const content = raw.length > MAX_FILE_CONTENT_BYTES
57
+ ? raw.slice(0, MAX_FILE_CONTENT_BYTES) +
58
+ "\n\n--- truncated ---"
59
+ : raw;
60
+ return { ...d, content };
61
+ }
62
+ catch {
63
+ return d; // File unreadable — send without content
64
+ }
65
+ }));
66
+ // Auto-commit/push guard: only in local mode (projectRoot set).
67
+ // In VM mode, the agent manages its own git commits.
68
+ if (context.projectRoot) {
69
+ const guardResult = await ensureChangesCommittedAndPushed(context.projectRoot, `step: ${deliverables.map((d) => d.label).join(", ")}`);
70
+ if (guardResult.error) {
71
+ return {
72
+ isError: true,
73
+ content: [
74
+ {
75
+ type: "text",
76
+ text: JSON.stringify({
77
+ error: MCP_ERROR_CODE.NETWORK_ERROR,
78
+ message: `Cannot signal step completion: failed to commit/push changes. ${guardResult.error}`,
79
+ retryHint: "Fix the git issue (commit and push your changes manually) then retry forge_complete_step.",
80
+ }),
81
+ },
82
+ ],
83
+ };
84
+ }
85
+ }
86
+ if (!context.socketClient.socket.connected) {
87
+ return {
88
+ isError: true,
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: JSON.stringify({
93
+ error: MCP_ERROR_CODE.NETWORK_ERROR,
94
+ message: "Socket not connected. Cannot signal step completion.",
95
+ retryHint: "Cloud may be temporarily unavailable. Retry in a few seconds.",
96
+ }),
97
+ },
98
+ ],
99
+ };
100
+ }
101
+ context.socketClient.emitStepComplete({
102
+ workflowId,
103
+ stepId,
104
+ projectId: context.projectId,
105
+ deliverables: enrichedDeliverables,
106
+ });
107
+ const guardStatus = "";
108
+ return {
109
+ content: [
110
+ {
111
+ type: "text",
112
+ text: JSON.stringify({
113
+ workflowId,
114
+ stepId,
115
+ deliverableCount: deliverables.length,
116
+ message: `Step completion signaled with ${deliverables.length} deliverable(s). The user will review them before advancing to the next step.${guardStatus ? " " + guardStatus : ""}`,
117
+ }),
118
+ },
119
+ ],
120
+ };
121
+ }
122
+ catch (error) {
123
+ return {
124
+ isError: true,
125
+ content: [
126
+ {
127
+ type: "text",
128
+ text: JSON.stringify({
129
+ error: MCP_ERROR_CODE.NETWORK_ERROR,
130
+ message: error instanceof Error ? error.message : String(error),
131
+ retryHint: "Retry in a few seconds.",
132
+ }),
133
+ },
134
+ ],
135
+ };
136
+ }
137
+ });
138
+ }
139
+ //# sourceMappingURL=forge-complete-step.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpSessionContext } from "../types.js";
3
+ export declare function registerForgeCreateTaskGroup(server: McpServer, context: McpSessionContext): void;
4
+ //# sourceMappingURL=forge-create-task-group.d.ts.map