@andrejvysny/symphony 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/client/assets/index-CkX5Wfff.css +1 -0
- package/dist/client/assets/index-D9N3V9nX.js +1 -0
- package/dist/client/index.html +32 -0
- package/dist/main.js +5502 -0
- package/dist/stdio-tracker-server.js +118 -0
- package/package.json +53 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../../packages/tracker/dist/chunk-MRV6YYIL.js
|
|
4
|
+
import net from "net";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
var TRACKER_GET_TASK_DESCRIPTION = "Read the full current state of one issue by id: its title, description, current workflow status, and existing comments. Use it first, before any update, to ground yourself in the issue\u2019s live state. It only reads \u2014 it does not modify the issue, and it does not return other issues or attachments.";
|
|
8
|
+
var TRACKER_UPDATE_STATUS_DESCRIPTION = "Set one issue\u2019s workflow status by id. Use it after reading the issue, and only when the target status differs from the current one. It does not post a comment or change the description.";
|
|
9
|
+
var TRACKER_ADD_COMMENT_DESCRIPTION = "Post one comment to an issue by id. Use it to record your plan on pickup and your evidence-backed summary on completion (what changed, the verification commands you ran and their result, and the commit SHA). It does not change the status.";
|
|
10
|
+
function connectBridge(socketPath) {
|
|
11
|
+
const sock = net.createConnection(socketPath);
|
|
12
|
+
sock.setEncoding("utf8");
|
|
13
|
+
const pending = /* @__PURE__ */ new Map();
|
|
14
|
+
let nextId = 0;
|
|
15
|
+
let buf = "";
|
|
16
|
+
sock.on("data", (chunk) => {
|
|
17
|
+
buf += chunk;
|
|
18
|
+
for (let nl = buf.indexOf("\n"); nl >= 0; nl = buf.indexOf("\n")) {
|
|
19
|
+
const line = buf.slice(0, nl);
|
|
20
|
+
buf = buf.slice(nl + 1);
|
|
21
|
+
if (line.trim().length === 0) continue;
|
|
22
|
+
try {
|
|
23
|
+
const msg = JSON.parse(line);
|
|
24
|
+
const cb = typeof msg.id === "number" ? pending.get(msg.id) : void 0;
|
|
25
|
+
if (cb && msg.result) {
|
|
26
|
+
pending.delete(msg.id);
|
|
27
|
+
cb(msg.result);
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const failAll = (message) => {
|
|
34
|
+
for (const [id, cb] of pending) {
|
|
35
|
+
pending.delete(id);
|
|
36
|
+
cb({ success: false, output: JSON.stringify({ error: message }) });
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
sock.on("error", () => failAll("tracker bridge connection error"));
|
|
40
|
+
sock.on("close", () => failAll("tracker bridge connection closed"));
|
|
41
|
+
return {
|
|
42
|
+
call(tool, input) {
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
const id = nextId++;
|
|
45
|
+
pending.set(id, resolve);
|
|
46
|
+
sock.write(`${JSON.stringify({ id, tool, input })}
|
|
47
|
+
`);
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
close() {
|
|
51
|
+
sock.end();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function buildStdioTrackerServer(deps) {
|
|
56
|
+
const server = new McpServer({ name: "symphony", version: "0.1.0" });
|
|
57
|
+
const statusSchema = deps.allowedStates.length > 0 ? z.enum(deps.allowedStates) : z.string();
|
|
58
|
+
const proxy = (tool) => async (args) => {
|
|
59
|
+
const r = await deps.client.call(tool, args);
|
|
60
|
+
return { content: [{ type: "text", text: r.output }], isError: !r.success };
|
|
61
|
+
};
|
|
62
|
+
server.registerTool(
|
|
63
|
+
"tracker_get_task",
|
|
64
|
+
{
|
|
65
|
+
description: TRACKER_GET_TASK_DESCRIPTION,
|
|
66
|
+
inputSchema: { task_id: z.string().describe("The issue id (issue.id).") }
|
|
67
|
+
},
|
|
68
|
+
proxy("tracker_get_task")
|
|
69
|
+
);
|
|
70
|
+
server.registerTool(
|
|
71
|
+
"tracker_update_status",
|
|
72
|
+
{
|
|
73
|
+
description: TRACKER_UPDATE_STATUS_DESCRIPTION,
|
|
74
|
+
inputSchema: {
|
|
75
|
+
task_id: z.string().describe("The issue id (issue.id)."),
|
|
76
|
+
status: statusSchema.describe("Target workflow state name.")
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
proxy("tracker_update_status")
|
|
80
|
+
);
|
|
81
|
+
server.registerTool(
|
|
82
|
+
"tracker_add_comment",
|
|
83
|
+
{
|
|
84
|
+
description: TRACKER_ADD_COMMENT_DESCRIPTION,
|
|
85
|
+
inputSchema: {
|
|
86
|
+
task_id: z.string().describe("The issue id (issue.id)."),
|
|
87
|
+
body: z.string().describe("Comment body (plain text).")
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
proxy("tracker_add_comment")
|
|
91
|
+
);
|
|
92
|
+
return server;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ../../packages/tracker/dist/tools/stdio-tracker-server.js
|
|
96
|
+
import { pathToFileURL } from "url";
|
|
97
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
98
|
+
async function main() {
|
|
99
|
+
const socketPath = process.env["SYMPHONY_TRACKER_SOCK"];
|
|
100
|
+
if (!socketPath) {
|
|
101
|
+
process.stderr.write("stdio-tracker-server: missing SYMPHONY_TRACKER_SOCK\n");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
let allowedStates = [];
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(process.env["SYMPHONY_AGENT_STATES"] ?? "[]");
|
|
107
|
+
if (Array.isArray(parsed))
|
|
108
|
+
allowedStates = parsed.filter((s) => typeof s === "string");
|
|
109
|
+
} catch {
|
|
110
|
+
allowedStates = [];
|
|
111
|
+
}
|
|
112
|
+
const client = connectBridge(socketPath);
|
|
113
|
+
await buildStdioTrackerServer({ client, allowedStates }).connect(new StdioServerTransport());
|
|
114
|
+
}
|
|
115
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
116
|
+
void main();
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=stdio-tracker-server.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@andrejvysny/symphony",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent-agnostic coding-agent orchestrator — delegate tickets to local coding agents (Claude Code, codex, opencode) in isolated git worktrees, driven by a local file task store.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"coding-agent",
|
|
9
|
+
"orchestrator",
|
|
10
|
+
"ai",
|
|
11
|
+
"automation",
|
|
12
|
+
"git-worktree",
|
|
13
|
+
"cli",
|
|
14
|
+
"symphony"
|
|
15
|
+
],
|
|
16
|
+
"license": "Apache-2.0",
|
|
17
|
+
"author": "Andrej Vyšný <claudera@brch.sk>",
|
|
18
|
+
"homepage": "https://github.com/andrejvysny/symphony-ts#readme",
|
|
19
|
+
"bugs": "https://github.com/andrejvysny/symphony-ts/issues",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/andrejvysny/symphony-ts.git",
|
|
23
|
+
"directory": "apps/cli"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"bin": {
|
|
27
|
+
"symphony": "./dist/main.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=22"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.154",
|
|
40
|
+
"@fastify/multipart": "^10.0.0",
|
|
41
|
+
"@fastify/static": "^9.1.3",
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
43
|
+
"execa": "^9.6.1",
|
|
44
|
+
"fastify": "^5.8.5",
|
|
45
|
+
"liquidjs": "^10.27.0",
|
|
46
|
+
"pino": "^10.3.1",
|
|
47
|
+
"pino-pretty": "^13.0.0",
|
|
48
|
+
"simple-git": "^3.36.0",
|
|
49
|
+
"undici": "^8.3.0",
|
|
50
|
+
"yaml": "^2.7.0",
|
|
51
|
+
"zod": "^4.4.3"
|
|
52
|
+
}
|
|
53
|
+
}
|