@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.
- package/LICENSE +201 -0
- package/README.md +80 -0
- package/dist/bin/forgeai.d.ts +3 -0
- package/dist/bin/forgeai.js +8 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +180 -0
- package/dist/commands/auth-login.d.ts +4 -0
- package/dist/commands/auth-login.js +202 -0
- package/dist/commands/auth-logout.d.ts +2 -0
- package/dist/commands/auth-logout.js +41 -0
- package/dist/commands/auth-status.d.ts +4 -0
- package/dist/commands/auth-status.js +81 -0
- package/dist/commands/cli-list.d.ts +4 -0
- package/dist/commands/cli-list.js +40 -0
- package/dist/commands/cli-unlink.d.ts +4 -0
- package/dist/commands/cli-unlink.js +45 -0
- package/dist/commands/config-set.d.ts +5 -0
- package/dist/commands/config-set.js +63 -0
- package/dist/commands/connect.d.ts +9 -0
- package/dist/commands/connect.js +165 -0
- package/dist/commands/disconnect.d.ts +5 -0
- package/dist/commands/disconnect.js +126 -0
- package/dist/commands/journal-clear.d.ts +2 -0
- package/dist/commands/journal-clear.js +16 -0
- package/dist/commands/journal-list.d.ts +2 -0
- package/dist/commands/journal-list.js +20 -0
- package/dist/commands/journal-status.d.ts +2 -0
- package/dist/commands/journal-status.js +18 -0
- package/dist/commands/mcp-serve.d.ts +13 -0
- package/dist/commands/mcp-serve.js +74 -0
- package/dist/commands/org-list.d.ts +4 -0
- package/dist/commands/org-list.js +56 -0
- package/dist/commands/org-unlink.d.ts +4 -0
- package/dist/commands/org-unlink.js +63 -0
- package/dist/commands/reconnect.d.ts +4 -0
- package/dist/commands/reconnect.js +46 -0
- package/dist/commands/release-open.d.ts +2 -0
- package/dist/commands/release-open.js +43 -0
- package/dist/commands/session-respond.d.ts +2 -0
- package/dist/commands/session-respond.js +29 -0
- package/dist/commands/task-open.d.ts +2 -0
- package/dist/commands/task-open.js +42 -0
- package/dist/commands/vm.d.ts +21 -0
- package/dist/commands/vm.js +122 -0
- package/dist/commands/workflow-open.d.ts +2 -0
- package/dist/commands/workflow-open.js +43 -0
- package/dist/config/config-manager.d.ts +31 -0
- package/dist/config/config-manager.js +70 -0
- package/dist/config/token-manager.d.ts +11 -0
- package/dist/config/token-manager.js +87 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +22 -0
- package/dist/lib/auth-client.d.ts +1670 -0
- package/dist/lib/auth-client.js +10 -0
- package/dist/lib/branch-manager.d.ts +79 -0
- package/dist/lib/branch-manager.js +620 -0
- package/dist/lib/clone-and-checkout.d.ts +6 -0
- package/dist/lib/clone-and-checkout.js +41 -0
- package/dist/lib/connection-manager.d.ts +58 -0
- package/dist/lib/connection-manager.js +436 -0
- package/dist/lib/daemon-entry.d.ts +2 -0
- package/dist/lib/daemon-entry.js +210 -0
- package/dist/lib/daemon.d.ts +38 -0
- package/dist/lib/daemon.js +148 -0
- package/dist/lib/event-journal.d.ts +24 -0
- package/dist/lib/event-journal.js +76 -0
- package/dist/lib/ipc-client.d.ts +10 -0
- package/dist/lib/ipc-client.js +70 -0
- package/dist/lib/ipc-server.d.ts +36 -0
- package/dist/lib/ipc-server.js +70 -0
- package/dist/lib/node-version-guard.d.ts +8 -0
- package/dist/lib/node-version-guard.js +60 -0
- package/dist/lib/open-directory.d.ts +7 -0
- package/dist/lib/open-directory.js +41 -0
- package/dist/lib/repo-manager.d.ts +29 -0
- package/dist/lib/repo-manager.js +103 -0
- package/dist/lib/resilient-emitter.d.ts +53 -0
- package/dist/lib/resilient-emitter.js +160 -0
- package/dist/lib/rest-client.d.ts +164 -0
- package/dist/lib/rest-client.js +139 -0
- package/dist/lib/skill-manager.d.ts +23 -0
- package/dist/lib/skill-manager.js +167 -0
- package/dist/lib/socket-client.d.ts +68 -0
- package/dist/lib/socket-client.js +160 -0
- package/dist/mcp/mcp-probe.d.ts +11 -0
- package/dist/mcp/mcp-probe.js +106 -0
- package/dist/mcp/mcp-response.d.ts +29 -0
- package/dist/mcp/mcp-response.js +67 -0
- package/dist/mcp/mcp-server.d.ts +19 -0
- package/dist/mcp/mcp-server.js +76 -0
- package/dist/mcp/resources/project-conventions.d.ts +4 -0
- package/dist/mcp/resources/project-conventions.js +50 -0
- package/dist/mcp/resources/task-current.d.ts +4 -0
- package/dist/mcp/resources/task-current.js +51 -0
- package/dist/mcp/tools/forge-add-group-dependency.d.ts +4 -0
- package/dist/mcp/tools/forge-add-group-dependency.js +20 -0
- package/dist/mcp/tools/forge-add-task-dependency.d.ts +4 -0
- package/dist/mcp/tools/forge-add-task-dependency.js +20 -0
- package/dist/mcp/tools/forge-ask-human.d.ts +4 -0
- package/dist/mcp/tools/forge-ask-human.js +80 -0
- package/dist/mcp/tools/forge-column-done.d.ts +4 -0
- package/dist/mcp/tools/forge-column-done.js +46 -0
- package/dist/mcp/tools/forge-complete-step.d.ts +4 -0
- package/dist/mcp/tools/forge-complete-step.js +139 -0
- package/dist/mcp/tools/forge-create-task-group.d.ts +4 -0
- package/dist/mcp/tools/forge-create-task-group.js +22 -0
- package/dist/mcp/tools/forge-create-task.d.ts +4 -0
- package/dist/mcp/tools/forge-create-task.js +28 -0
- package/dist/mcp/tools/forge-delete-task-group.d.ts +4 -0
- package/dist/mcp/tools/forge-delete-task-group.js +17 -0
- package/dist/mcp/tools/forge-delete-task.d.ts +4 -0
- package/dist/mcp/tools/forge-delete-task.js +17 -0
- package/dist/mcp/tools/forge-get-task.d.ts +4 -0
- package/dist/mcp/tools/forge-get-task.js +17 -0
- package/dist/mcp/tools/forge-merge-back.d.ts +4 -0
- package/dist/mcp/tools/forge-merge-back.js +179 -0
- package/dist/mcp/tools/forge-release-merge.d.ts +4 -0
- package/dist/mcp/tools/forge-release-merge.js +159 -0
- package/dist/mcp/tools/forge-release-notes.d.ts +4 -0
- package/dist/mcp/tools/forge-release-notes.js +51 -0
- package/dist/mcp/tools/forge-release-tag.d.ts +4 -0
- package/dist/mcp/tools/forge-release-tag.js +125 -0
- package/dist/mcp/tools/forge-remove-group-dependency.d.ts +4 -0
- package/dist/mcp/tools/forge-remove-group-dependency.js +19 -0
- package/dist/mcp/tools/forge-remove-task-dependency.d.ts +4 -0
- package/dist/mcp/tools/forge-remove-task-dependency.js +19 -0
- package/dist/mcp/tools/forge-requeue.d.ts +4 -0
- package/dist/mcp/tools/forge-requeue.js +39 -0
- package/dist/mcp/tools/forge-submit-pr.d.ts +4 -0
- package/dist/mcp/tools/forge-submit-pr.js +68 -0
- package/dist/mcp/tools/forge-update-status.d.ts +4 -0
- package/dist/mcp/tools/forge-update-status.js +77 -0
- package/dist/mcp/tools/forge-update-task-group.d.ts +4 -0
- package/dist/mcp/tools/forge-update-task-group.js +21 -0
- package/dist/mcp/tools/forge-update-task.d.ts +4 -0
- package/dist/mcp/tools/forge-update-task.js +25 -0
- package/dist/mcp/tools/forge-validate-merge.d.ts +4 -0
- package/dist/mcp/tools/forge-validate-merge.js +194 -0
- package/dist/mcp/types.d.ts +39 -0
- package/dist/mcp/types.js +8 -0
- package/dist/methodology/artifact-manager.d.ts +14 -0
- package/dist/methodology/artifact-manager.js +55 -0
- package/dist/orchestrator/agent-registry.d.ts +53 -0
- package/dist/orchestrator/agent-registry.js +96 -0
- package/dist/orchestrator/agent-spawner.d.ts +112 -0
- package/dist/orchestrator/agent-spawner.js +1518 -0
- package/dist/orchestrator/connectors/claude-code-connector.d.ts +3 -0
- package/dist/orchestrator/connectors/claude-code-connector.js +60 -0
- package/dist/orchestrator/connectors/connector.d.ts +95 -0
- package/dist/orchestrator/connectors/connector.js +25 -0
- package/dist/orchestrator/connectors/docker-connector.d.ts +14 -0
- package/dist/orchestrator/connectors/docker-connector.js +337 -0
- package/dist/orchestrator/connectors/native-vm-connector.d.ts +11 -0
- package/dist/orchestrator/connectors/native-vm-connector.js +162 -0
- package/dist/orchestrator/connectors/opencode-connector.d.ts +3 -0
- package/dist/orchestrator/connectors/opencode-connector.js +39 -0
- package/dist/orchestrator/connectors/vm-connector.d.ts +3 -0
- package/dist/orchestrator/connectors/vm-connector.js +139 -0
- package/dist/orchestrator/dispatch-queue.d.ts +43 -0
- package/dist/orchestrator/dispatch-queue.js +93 -0
- package/dist/orchestrator/resume-handler.d.ts +34 -0
- package/dist/orchestrator/resume-handler.js +159 -0
- package/dist/orchestrator/stream-batcher.d.ts +16 -0
- package/dist/orchestrator/stream-batcher.js +50 -0
- package/dist/orchestrator/stream-json-extractor.d.ts +97 -0
- package/dist/orchestrator/stream-json-extractor.js +579 -0
- package/dist/vm/claude-token-manager.d.ts +36 -0
- package/dist/vm/claude-token-manager.js +223 -0
- package/dist/vm/docker-executor.d.ts +131 -0
- package/dist/vm/docker-executor.js +360 -0
- package/dist/vm/docker-process-proxy.d.ts +31 -0
- package/dist/vm/docker-process-proxy.js +105 -0
- package/dist/vm/forge-vm-agent.d.ts +11 -0
- package/dist/vm/forge-vm-agent.js +159 -0
- package/dist/vm/git-credentials.d.ts +43 -0
- package/dist/vm/git-credentials.js +53 -0
- package/dist/vm/image-manager.d.ts +7 -0
- package/dist/vm/image-manager.js +61 -0
- package/dist/vm/log.d.ts +9 -0
- package/dist/vm/log.js +18 -0
- package/dist/vm/mcp-bridge.d.ts +63 -0
- package/dist/vm/mcp-bridge.js +243 -0
- package/dist/vm/mcp-stub.d.ts +15 -0
- package/dist/vm/mcp-stub.js +67 -0
- package/dist/vm/native-addon.d.ts +69 -0
- package/dist/vm/native-addon.js +68 -0
- package/dist/vm/provider-resolver.d.ts +7 -0
- package/dist/vm/provider-resolver.js +27 -0
- package/dist/vm/providers/lima.d.ts +17 -0
- package/dist/vm/providers/lima.js +191 -0
- package/dist/vm/providers/qemu.d.ts +28 -0
- package/dist/vm/providers/qemu.js +260 -0
- package/dist/vm/providers/vfkit.d.ts +40 -0
- package/dist/vm/providers/vfkit.js +371 -0
- package/dist/vm/providers/wsl2.d.ts +27 -0
- package/dist/vm/providers/wsl2.js +226 -0
- package/dist/vm/session-manager.d.ts +130 -0
- package/dist/vm/session-manager.js +494 -0
- package/dist/vm/session-snapshot.d.ts +31 -0
- package/dist/vm/session-snapshot.js +180 -0
- package/dist/vm/types.d.ts +73 -0
- package/dist/vm/types.js +2 -0
- package/dist/vm/vm-helper-client.d.ts +81 -0
- package/dist/vm/vm-helper-client.js +335 -0
- package/dist/vm/vm-helper.d.ts +11 -0
- package/dist/vm/vm-helper.js +156 -0
- package/dist/vm/vm-ipc-protocol.d.ts +84 -0
- package/dist/vm/vm-ipc-protocol.js +9 -0
- package/dist/vm/vm-manager-facade.d.ts +23 -0
- package/dist/vm/vm-manager-facade.js +43 -0
- package/dist/vm/vm-manager.d.ts +74 -0
- package/dist/vm/vm-manager.js +515 -0
- package/dist/vm/vm-process-proxy.d.ts +34 -0
- package/dist/vm/vm-process-proxy.js +64 -0
- package/dist/vm/vsock-bridge.d.ts +44 -0
- package/dist/vm/vsock-bridge.js +140 -0
- package/dist/vm/vsock-protocol.d.ts +98 -0
- package/dist/vm/vsock-protocol.js +57 -0
- package/package.json +65 -0
- package/rootfs/Dockerfile +50 -0
- package/rootfs/forge-mcp-relay.mjs +158 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ForgeCreateTaskGroupInputSchema } from "@getforgeai/protocol";
|
|
2
|
+
import { executeSocketEmit } from "../mcp-response.js";
|
|
3
|
+
const shape = ForgeCreateTaskGroupInputSchema.shape;
|
|
4
|
+
export function registerForgeCreateTaskGroup(server, context) {
|
|
5
|
+
server.registerTool("forge_create_task_group", {
|
|
6
|
+
description: "Create a task group in the current project. Returns the created group's id and name. Use this to organize tasks into logical groups before creating tasks.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
name: shape.name.describe("Group name"),
|
|
9
|
+
description: shape.description.describe("Group description"),
|
|
10
|
+
order: shape.order.describe("Display order (auto-assigned if omitted)"),
|
|
11
|
+
},
|
|
12
|
+
}, async (params) => {
|
|
13
|
+
return executeSocketEmit(context, "create task group", () => context.socketClient.emitTaskGroupCreate({
|
|
14
|
+
projectId: context.projectId,
|
|
15
|
+
workflowId: context.workflowId,
|
|
16
|
+
name: params.name,
|
|
17
|
+
description: params.description,
|
|
18
|
+
order: params.order,
|
|
19
|
+
}));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=forge-create-task-group.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 registerForgeCreateTask(server: McpServer, context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-create-task.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ForgeCreateTaskInputSchema } from "@getforgeai/protocol";
|
|
2
|
+
import { executeSocketEmit } from "../mcp-response.js";
|
|
3
|
+
const shape = ForgeCreateTaskInputSchema.shape;
|
|
4
|
+
export function registerForgeCreateTask(server, context) {
|
|
5
|
+
server.registerTool("forge_create_task", {
|
|
6
|
+
description: "Create a task in the current project. Optionally assign it to a task group by name. Returns the created task's id and title.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
title: shape.title.describe("Task title"),
|
|
9
|
+
description: shape.description.describe("Task description"),
|
|
10
|
+
priority: shape.priority.describe("Task priority"),
|
|
11
|
+
storyPoints: shape.storyPoints.describe("Story points estimate"),
|
|
12
|
+
groupName: shape.groupName.describe("Task group name to assign this task to"),
|
|
13
|
+
order: shape.order.describe("Display order (auto-assigned if omitted)"),
|
|
14
|
+
},
|
|
15
|
+
}, async (params) => {
|
|
16
|
+
return executeSocketEmit(context, "create task", () => context.socketClient.emitTaskCreate({
|
|
17
|
+
projectId: context.projectId,
|
|
18
|
+
workflowId: context.workflowId,
|
|
19
|
+
title: params.title,
|
|
20
|
+
description: params.description,
|
|
21
|
+
priority: params.priority,
|
|
22
|
+
storyPoints: params.storyPoints,
|
|
23
|
+
groupName: params.groupName,
|
|
24
|
+
order: params.order,
|
|
25
|
+
}));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=forge-create-task.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 registerForgeDeleteTaskGroup(server: McpServer, context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-delete-task-group.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ForgeDeleteTaskGroupInputSchema } from "@getforgeai/protocol";
|
|
2
|
+
import { executeSocketEmit } from "../mcp-response.js";
|
|
3
|
+
const shape = ForgeDeleteTaskGroupInputSchema.shape;
|
|
4
|
+
export function registerForgeDeleteTaskGroup(server, context) {
|
|
5
|
+
server.registerTool("forge_delete_task_group", {
|
|
6
|
+
description: "Delete a task group by name (most recently created match). Tasks in the group are orphaned (moved to ungrouped), not deleted.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
name: shape.name.describe("Group name to find and delete"),
|
|
9
|
+
},
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
return executeSocketEmit(context, "delete task group", () => context.socketClient.emitMcpTaskGroupDelete({
|
|
12
|
+
projectId: context.projectId,
|
|
13
|
+
name: params.name,
|
|
14
|
+
}));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=forge-delete-task-group.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 registerForgeDeleteTask(server: McpServer, context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-delete-task.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ForgeDeleteTaskInputSchema } from "@getforgeai/protocol";
|
|
2
|
+
import { executeSocketEmit } from "../mcp-response.js";
|
|
3
|
+
const shape = ForgeDeleteTaskInputSchema.shape;
|
|
4
|
+
export function registerForgeDeleteTask(server, context) {
|
|
5
|
+
server.registerTool("forge_delete_task", {
|
|
6
|
+
description: "Delete a task by name (most recently created match). This permanently removes the task and its dependencies.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
name: shape.name.describe("Task title to find and delete"),
|
|
9
|
+
},
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
return executeSocketEmit(context, "delete task", () => context.socketClient.emitMcpTaskDelete({
|
|
12
|
+
projectId: context.projectId,
|
|
13
|
+
name: params.name,
|
|
14
|
+
}));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=forge-delete-task.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 registerForgeGetTask(server: McpServer, context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-get-task.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { mcpSuccess, handleRestError } from "../mcp-response.js";
|
|
3
|
+
export function registerForgeGetTask(server, context) {
|
|
4
|
+
server.registerTool("forge_get_task", {
|
|
5
|
+
description: "Fetch a task from ForgeAI Cloud with full context (description, acceptance criteria, checkpoints, handoff context)",
|
|
6
|
+
inputSchema: { taskId: z.string().min(1).describe("Task ID to fetch") },
|
|
7
|
+
}, async ({ taskId }) => {
|
|
8
|
+
try {
|
|
9
|
+
const task = await context.restClient.getTask(taskId);
|
|
10
|
+
return mcpSuccess(task);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return handleRestError(error, taskId);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=forge-get-task.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 registerForgeMergeBack(server: McpServer, _context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-merge-back.d.ts.map
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { mcpSuccess, mcpError } from "../mcp-response.js";
|
|
5
|
+
import { MCP_ERROR_CODE } from "../types.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
/** Maximum output bytes to include in conflict details. */
|
|
8
|
+
const MAX_CONFLICT_OUTPUT = 10 * 1024; // 10 KB
|
|
9
|
+
function truncate(text, maxBytes) {
|
|
10
|
+
if (text.length <= maxBytes)
|
|
11
|
+
return text;
|
|
12
|
+
return text.slice(0, maxBytes) + "\n\n--- truncated ---";
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Run a git command in the given repo path and return stdout.
|
|
16
|
+
* Throws on non-zero exit.
|
|
17
|
+
*/
|
|
18
|
+
async function git(repoPath, args) {
|
|
19
|
+
const { stdout } = await execFileAsync("git", args, { cwd: repoPath });
|
|
20
|
+
return stdout.trim();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get the list of conflicted files from `git diff --name-only --diff-filter=U`.
|
|
24
|
+
*/
|
|
25
|
+
async function getConflictedFiles(repoPath) {
|
|
26
|
+
try {
|
|
27
|
+
const output = await git(repoPath, [
|
|
28
|
+
"diff",
|
|
29
|
+
"--name-only",
|
|
30
|
+
"--diff-filter=U",
|
|
31
|
+
]);
|
|
32
|
+
return output
|
|
33
|
+
.split("\n")
|
|
34
|
+
.map((f) => f.trim())
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get conflict markers content for a specific file (limited output).
|
|
43
|
+
*/
|
|
44
|
+
async function getConflictContent(repoPath, filePath) {
|
|
45
|
+
try {
|
|
46
|
+
const { stdout } = await execFileAsync("git", ["diff", filePath], {
|
|
47
|
+
cwd: repoPath,
|
|
48
|
+
});
|
|
49
|
+
return truncate(stdout, MAX_CONFLICT_OUTPUT);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return "(could not read conflict diff)";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function registerForgeMergeBack(server, _context) {
|
|
56
|
+
server.registerTool("forge_merge_back", {
|
|
57
|
+
description: "Merge a child branch into a parent branch. Fetches latest remote state, checks out the parent branch, and performs `git merge`. Returns conflict details if the merge fails so the agent can resolve or call forge_ask_human.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
childBranch: z
|
|
60
|
+
.string()
|
|
61
|
+
.min(1)
|
|
62
|
+
.describe("The child branch to merge (source)"),
|
|
63
|
+
parentBranch: z
|
|
64
|
+
.string()
|
|
65
|
+
.min(1)
|
|
66
|
+
.describe("The parent branch to merge into (target)"),
|
|
67
|
+
repoPath: z
|
|
68
|
+
.string()
|
|
69
|
+
.min(1)
|
|
70
|
+
.describe("Absolute path to the git repository"),
|
|
71
|
+
},
|
|
72
|
+
}, async ({ childBranch, parentBranch, repoPath }) => {
|
|
73
|
+
try {
|
|
74
|
+
// 1. Fetch latest state for both branches
|
|
75
|
+
try {
|
|
76
|
+
await git(repoPath, ["fetch", "origin", parentBranch]);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Remote may not be available — use local state
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
await git(repoPath, ["fetch", "origin", childBranch]);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Remote may not be available — use local state
|
|
86
|
+
}
|
|
87
|
+
// 2. Checkout parent branch
|
|
88
|
+
try {
|
|
89
|
+
await git(repoPath, ["checkout", parentBranch]);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
93
|
+
return mcpError({
|
|
94
|
+
error: MCP_ERROR_CODE.VALIDATION_ERROR,
|
|
95
|
+
message: `Failed to checkout parent branch "${parentBranch}": ${message}`,
|
|
96
|
+
hint: "Ensure the parent branch exists and the working tree is clean.",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// 3. Pull latest parent (fast-forward if possible)
|
|
100
|
+
try {
|
|
101
|
+
await git(repoPath, ["pull", "--ff-only", "origin", parentBranch]);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// May fail if no remote tracking or no new commits — not fatal
|
|
105
|
+
}
|
|
106
|
+
// 4. Attempt merge
|
|
107
|
+
try {
|
|
108
|
+
const mergeOutput = await git(repoPath, [
|
|
109
|
+
"merge",
|
|
110
|
+
childBranch,
|
|
111
|
+
"--no-edit",
|
|
112
|
+
]);
|
|
113
|
+
// Merge succeeded — push to remote
|
|
114
|
+
let pushed = false;
|
|
115
|
+
try {
|
|
116
|
+
await git(repoPath, ["push", "origin", parentBranch]);
|
|
117
|
+
pushed = true;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Push failed — merge is local only, agent can push later
|
|
121
|
+
}
|
|
122
|
+
return mcpSuccess({
|
|
123
|
+
success: true,
|
|
124
|
+
childBranch,
|
|
125
|
+
parentBranch,
|
|
126
|
+
pushed,
|
|
127
|
+
message: `Successfully merged "${childBranch}" into "${parentBranch}".${pushed ? " Pushed to remote." : " Push to remote failed — push manually."}`,
|
|
128
|
+
mergeOutput,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (mergeError) {
|
|
132
|
+
// 5. Merge failed — check if it's a conflict
|
|
133
|
+
const conflictedFiles = await getConflictedFiles(repoPath);
|
|
134
|
+
if (conflictedFiles.length > 0) {
|
|
135
|
+
// Gather conflict details for the agent
|
|
136
|
+
const conflicts = await Promise.all(conflictedFiles.map(async (file) => ({
|
|
137
|
+
file,
|
|
138
|
+
diff: await getConflictContent(repoPath, file),
|
|
139
|
+
})));
|
|
140
|
+
return mcpError({
|
|
141
|
+
error: "MERGE_CONFLICT",
|
|
142
|
+
message: `Merge of "${childBranch}" into "${parentBranch}" resulted in ${conflictedFiles.length} conflicted file(s).`,
|
|
143
|
+
conflictedFiles,
|
|
144
|
+
conflicts,
|
|
145
|
+
hint: "Resolve the conflicts manually in the listed files, then run `git add <file>` and `git commit` to complete the merge. If you cannot resolve, call forge_ask_human for help.",
|
|
146
|
+
repoPath,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// Not a conflict — some other merge error
|
|
150
|
+
const message = mergeError instanceof Error
|
|
151
|
+
? mergeError.message
|
|
152
|
+
: String(mergeError);
|
|
153
|
+
// Abort the failed merge to leave repo in clean state
|
|
154
|
+
try {
|
|
155
|
+
await git(repoPath, ["merge", "--abort"]);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// Abort may fail if no merge in progress
|
|
159
|
+
}
|
|
160
|
+
return mcpError({
|
|
161
|
+
error: MCP_ERROR_CODE.VALIDATION_ERROR,
|
|
162
|
+
message: `Merge failed: ${message}`,
|
|
163
|
+
childBranch,
|
|
164
|
+
parentBranch,
|
|
165
|
+
hint: "The merge was aborted. Check branch names and repository state.",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
171
|
+
return mcpError({
|
|
172
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
173
|
+
message: `Unexpected error during merge: ${message}`,
|
|
174
|
+
hint: "Check that the repository path is valid and accessible.",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=forge-merge-back.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 registerForgeReleaseMerge(server: McpServer, _context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-release-merge.d.ts.map
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { mcpSuccess, mcpError } from "../mcp-response.js";
|
|
5
|
+
import { MCP_ERROR_CODE } from "../types.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export function registerForgeReleaseMerge(server, _context) {
|
|
8
|
+
server.registerTool("forge_release_merge", {
|
|
9
|
+
description: "Create a release branch from a target branch and merge selected workflow branches into it sequentially. Returns the merge result for each branch, including conflict details if any.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
version: z.string().min(1).describe("Release version (e.g. 'v1.0.0')"),
|
|
12
|
+
targetBranch: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1)
|
|
15
|
+
.describe("Target branch to create release from (e.g. 'main')"),
|
|
16
|
+
workflowBranches: z
|
|
17
|
+
.array(z.string().min(1))
|
|
18
|
+
.min(1)
|
|
19
|
+
.describe("List of workflow branch names to merge into the release branch"),
|
|
20
|
+
repoPath: z
|
|
21
|
+
.string()
|
|
22
|
+
.min(1)
|
|
23
|
+
.describe("Absolute path to the git repository"),
|
|
24
|
+
},
|
|
25
|
+
}, async ({ version, targetBranch, workflowBranches, repoPath }) => {
|
|
26
|
+
const releaseBranch = `release/${version}`;
|
|
27
|
+
// 1. Fetch the latest target branch from remote
|
|
28
|
+
try {
|
|
29
|
+
await execFileAsync("git", ["fetch", "origin", targetBranch], {
|
|
30
|
+
cwd: repoPath,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Remote may not be available — use local ref
|
|
35
|
+
}
|
|
36
|
+
// 2. Create the release branch from origin/targetBranch
|
|
37
|
+
try {
|
|
38
|
+
// Check if release branch already exists
|
|
39
|
+
try {
|
|
40
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--verify", releaseBranch], { cwd: repoPath });
|
|
41
|
+
if (stdout.trim()) {
|
|
42
|
+
// Release branch already exists — check it out
|
|
43
|
+
await execFileAsync("git", ["checkout", releaseBranch], {
|
|
44
|
+
cwd: repoPath,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Branch doesn't exist — create it
|
|
50
|
+
let baseRef = targetBranch;
|
|
51
|
+
try {
|
|
52
|
+
await execFileAsync("git", ["rev-parse", "--verify", `origin/${targetBranch}`], { cwd: repoPath });
|
|
53
|
+
baseRef = `origin/${targetBranch}`;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// origin ref not available, use local
|
|
57
|
+
}
|
|
58
|
+
await execFileAsync("git", ["checkout", "-b", releaseBranch, baseRef], { cwd: repoPath });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
63
|
+
return mcpError({
|
|
64
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
65
|
+
message: `Failed to create release branch "${releaseBranch}": ${message}`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// 3. Merge each workflow branch sequentially
|
|
69
|
+
const results = [];
|
|
70
|
+
for (const branch of workflowBranches) {
|
|
71
|
+
// Fetch the workflow branch from remote
|
|
72
|
+
try {
|
|
73
|
+
await execFileAsync("git", ["fetch", "origin", branch], {
|
|
74
|
+
cwd: repoPath,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Use local ref if remote fetch fails
|
|
79
|
+
}
|
|
80
|
+
// Determine the ref to merge (prefer origin/ if available)
|
|
81
|
+
let mergeRef = branch;
|
|
82
|
+
try {
|
|
83
|
+
await execFileAsync("git", ["rev-parse", "--verify", `origin/${branch}`], { cwd: repoPath });
|
|
84
|
+
mergeRef = `origin/${branch}`;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Use local ref
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await execFileAsync("git", ["merge", mergeRef, "--no-edit"], {
|
|
91
|
+
cwd: repoPath,
|
|
92
|
+
});
|
|
93
|
+
results.push({ branch, status: "merged" });
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// Check if it's a merge conflict
|
|
97
|
+
try {
|
|
98
|
+
const { stdout: conflictOutput } = await execFileAsync("git", ["diff", "--name-only", "--diff-filter=U"], { cwd: repoPath });
|
|
99
|
+
const conflictFiles = conflictOutput
|
|
100
|
+
.split("\n")
|
|
101
|
+
.map((f) => f.trim())
|
|
102
|
+
.filter(Boolean);
|
|
103
|
+
if (conflictFiles.length > 0) {
|
|
104
|
+
results.push({
|
|
105
|
+
branch,
|
|
106
|
+
status: "conflict",
|
|
107
|
+
conflictFiles,
|
|
108
|
+
error: `Merge conflict in ${conflictFiles.length} file(s)`,
|
|
109
|
+
});
|
|
110
|
+
// Return immediately on conflict — agent needs to resolve before continuing
|
|
111
|
+
return mcpSuccess({
|
|
112
|
+
releaseBranch,
|
|
113
|
+
targetBranch,
|
|
114
|
+
version,
|
|
115
|
+
mergeResults: results,
|
|
116
|
+
hasConflicts: true,
|
|
117
|
+
conflictBranch: branch,
|
|
118
|
+
conflictFiles,
|
|
119
|
+
message: `Merge conflict detected when merging "${branch}". Resolve conflicts in the listed files, then run git add and git commit to continue.`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// Could not determine conflict files
|
|
125
|
+
}
|
|
126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
127
|
+
results.push({
|
|
128
|
+
branch,
|
|
129
|
+
status: "failed",
|
|
130
|
+
error: message,
|
|
131
|
+
});
|
|
132
|
+
return mcpError({
|
|
133
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
134
|
+
message: `Failed to merge branch "${branch}": ${message}`,
|
|
135
|
+
releaseBranch,
|
|
136
|
+
mergeResults: results,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// 4. Push the release branch to remote
|
|
141
|
+
try {
|
|
142
|
+
await execFileAsync("git", ["push", "-u", "origin", releaseBranch], {
|
|
143
|
+
cwd: repoPath,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Non-fatal — branch exists locally, can be pushed later
|
|
148
|
+
}
|
|
149
|
+
return mcpSuccess({
|
|
150
|
+
releaseBranch,
|
|
151
|
+
targetBranch,
|
|
152
|
+
version,
|
|
153
|
+
mergeResults: results,
|
|
154
|
+
hasConflicts: false,
|
|
155
|
+
message: `Successfully created release branch "${releaseBranch}" and merged ${results.length} workflow branch(es).`,
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=forge-release-merge.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 registerForgeReleaseNotes(server: McpServer, _context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-release-notes.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { mcpSuccess } from "../mcp-response.js";
|
|
3
|
+
export function registerForgeReleaseNotes(server, _context) {
|
|
4
|
+
server.registerTool("forge_release_notes", {
|
|
5
|
+
description: "Generate markdown release notes from workflow and task data. Groups tasks by workflow for a structured changelog.",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
version: z.string().min(1).describe("Release version (e.g. 'v1.0.0')"),
|
|
8
|
+
workflowNames: z
|
|
9
|
+
.array(z.string().min(1))
|
|
10
|
+
.min(1)
|
|
11
|
+
.describe("List of workflow names included in the release"),
|
|
12
|
+
taskTitles: z
|
|
13
|
+
.array(z.string().min(1))
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Optional flat list of task titles. If provided alongside workflowNames, tasks are listed under a general section."),
|
|
16
|
+
},
|
|
17
|
+
}, async ({ version, workflowNames, taskTitles }) => {
|
|
18
|
+
const lines = [];
|
|
19
|
+
lines.push(`# Release ${version}`);
|
|
20
|
+
lines.push("");
|
|
21
|
+
lines.push(`_Released on ${new Date().toISOString().split("T")[0]}_`);
|
|
22
|
+
lines.push("");
|
|
23
|
+
// Workflows section
|
|
24
|
+
lines.push("## Included Workflows");
|
|
25
|
+
lines.push("");
|
|
26
|
+
for (const name of workflowNames) {
|
|
27
|
+
lines.push(`### ${name}`);
|
|
28
|
+
lines.push("");
|
|
29
|
+
}
|
|
30
|
+
// Tasks section (flat list if provided)
|
|
31
|
+
if (taskTitles && taskTitles.length > 0) {
|
|
32
|
+
lines.push("## Changes");
|
|
33
|
+
lines.push("");
|
|
34
|
+
for (const title of taskTitles) {
|
|
35
|
+
lines.push(`- ${title}`);
|
|
36
|
+
}
|
|
37
|
+
lines.push("");
|
|
38
|
+
}
|
|
39
|
+
lines.push("---");
|
|
40
|
+
lines.push(`*Generated by ForgeAI*`);
|
|
41
|
+
const markdown = lines.join("\n");
|
|
42
|
+
return mcpSuccess({
|
|
43
|
+
version,
|
|
44
|
+
markdown,
|
|
45
|
+
workflowCount: workflowNames.length,
|
|
46
|
+
taskCount: taskTitles?.length ?? 0,
|
|
47
|
+
message: `Generated release notes for ${version} with ${workflowNames.length} workflow(s) and ${taskTitles?.length ?? 0} task(s).`,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=forge-release-notes.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 registerForgeReleaseTag(server: McpServer, _context: McpSessionContext): void;
|
|
4
|
+
//# sourceMappingURL=forge-release-tag.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { mcpSuccess, mcpError } from "../mcp-response.js";
|
|
5
|
+
import { MCP_ERROR_CODE } from "../types.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export function registerForgeReleaseTag(server, _context) {
|
|
8
|
+
server.registerTool("forge_release_tag", {
|
|
9
|
+
description: "Merge a release branch into the target branch, create an annotated git tag, and push both to remote. Used during the RELEASING phase after human approval.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
version: z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1)
|
|
14
|
+
.describe("Release version for the tag (e.g. 'v1.0.0')"),
|
|
15
|
+
targetBranch: z
|
|
16
|
+
.string()
|
|
17
|
+
.min(1)
|
|
18
|
+
.describe("Target branch to merge into (e.g. 'main')"),
|
|
19
|
+
releaseBranch: z
|
|
20
|
+
.string()
|
|
21
|
+
.min(1)
|
|
22
|
+
.describe("Release branch to merge from (e.g. 'release/v1.0.0')"),
|
|
23
|
+
repoPath: z
|
|
24
|
+
.string()
|
|
25
|
+
.min(1)
|
|
26
|
+
.describe("Absolute path to the git repository"),
|
|
27
|
+
},
|
|
28
|
+
}, async ({ version, targetBranch, releaseBranch, repoPath }) => {
|
|
29
|
+
// 1. Checkout target branch
|
|
30
|
+
try {
|
|
31
|
+
await execFileAsync("git", ["fetch", "origin", targetBranch], {
|
|
32
|
+
cwd: repoPath,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Remote may not be available
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await execFileAsync("git", ["checkout", targetBranch], {
|
|
40
|
+
cwd: repoPath,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
45
|
+
return mcpError({
|
|
46
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
47
|
+
message: `Failed to checkout "${targetBranch}": ${message}`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// 2. Pull latest target branch
|
|
51
|
+
try {
|
|
52
|
+
await execFileAsync("git", ["pull", "origin", targetBranch], {
|
|
53
|
+
cwd: repoPath,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Non-fatal — continue with local state
|
|
58
|
+
}
|
|
59
|
+
// 3. Merge release branch into target
|
|
60
|
+
let mergeCommit;
|
|
61
|
+
try {
|
|
62
|
+
await execFileAsync("git", ["merge", releaseBranch, "--no-edit"], {
|
|
63
|
+
cwd: repoPath,
|
|
64
|
+
});
|
|
65
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD"], {
|
|
66
|
+
cwd: repoPath,
|
|
67
|
+
});
|
|
68
|
+
mergeCommit = stdout.trim();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
72
|
+
return mcpError({
|
|
73
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
74
|
+
message: `Failed to merge "${releaseBranch}" into "${targetBranch}": ${message}`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// 4. Create annotated tag
|
|
78
|
+
const tagName = version;
|
|
79
|
+
try {
|
|
80
|
+
await execFileAsync("git", ["tag", "-a", tagName, "-m", `Release ${version}`], { cwd: repoPath });
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
+
return mcpError({
|
|
85
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
86
|
+
message: `Failed to create tag "${tagName}": ${message}`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// 5. Push target branch to remote
|
|
90
|
+
try {
|
|
91
|
+
await execFileAsync("git", ["push", "origin", targetBranch], {
|
|
92
|
+
cwd: repoPath,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
return mcpError({
|
|
98
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
99
|
+
message: `Failed to push "${targetBranch}" to remote: ${message}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// 6. Push tag to remote
|
|
103
|
+
try {
|
|
104
|
+
await execFileAsync("git", ["push", "origin", tagName], {
|
|
105
|
+
cwd: repoPath,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
return mcpError({
|
|
111
|
+
error: MCP_ERROR_CODE.NETWORK_ERROR,
|
|
112
|
+
message: `Failed to push tag "${tagName}" to remote: ${message}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return mcpSuccess({
|
|
116
|
+
tagName,
|
|
117
|
+
mergeCommit,
|
|
118
|
+
targetBranch,
|
|
119
|
+
releaseBranch,
|
|
120
|
+
version,
|
|
121
|
+
message: `Successfully merged "${releaseBranch}" into "${targetBranch}", created tag "${tagName}", and pushed to remote.`,
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=forge-release-tag.js.map
|