@adhdev/daemon-standalone 1.0.58-rc.33 → 1.0.58-rc.34

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-standalone",
3
- "version": "1.0.58-rc.33",
3
+ "version": "1.0.58-rc.34",
4
4
  "description": "ADHDev standalone daemon — embedded HTTP/WS server for local dashboard",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "license": "AGPL-3.0-or-later",
38
38
  "dependencies": {
39
39
  "@adhdev/ghostty-vt-node": "*",
40
- "@adhdev/session-host-core": "1.0.58-rc.33",
40
+ "@adhdev/session-host-core": "1.0.58-rc.34",
41
41
  "@xterm/addon-serialize": "^0.14.0",
42
42
  "@xterm/xterm": "^6.0.0",
43
43
  "chalk": "^5.3.0",
@@ -10327,6 +10327,150 @@ async function meshRefineBatch(ctx, args = {}) {
10327
10327
  return JSON.stringify(result, null, 2);
10328
10328
  }
10329
10329
 
10330
+ // src/tools/worker-tools.ts
10331
+ function readWorkerCredentials(env = process.env) {
10332
+ const bind = typeof env.ADHDEV_WORKER_SESSION_BIND === "string" ? env.ADHDEV_WORKER_SESSION_BIND.trim() : "";
10333
+ const token = typeof env.ADHDEV_WORKER_TASK_TOKEN === "string" ? env.ADHDEV_WORKER_TASK_TOKEN.trim() : "";
10334
+ return { ...bind ? { bind } : {}, ...token ? { token } : {} };
10335
+ }
10336
+ var BRANCH_STATES = [
10337
+ "merged_to_main",
10338
+ "pushed_feature_branch_needs_merge",
10339
+ "blocked_review",
10340
+ "cleanup_candidate",
10341
+ "not_mergeable"
10342
+ ];
10343
+ var REPORT_COMPLETION_TOOL = {
10344
+ name: "report_completion",
10345
+ description: "Report the structured outcome of the task you were dispatched to do. Call this ONCE when your work is finished, blocked, or has failed. Your `summary` is recorded verbatim \u2014 it is not scraped from your terminal \u2014 so write what the coordinator actually needs to know. You do not pass a task id: the daemon knows which task you hold.",
10346
+ inputSchema: {
10347
+ type: "object",
10348
+ properties: {
10349
+ outcome: {
10350
+ type: "string",
10351
+ enum: ["completed", "blocked", "failed"],
10352
+ description: "'completed' = the task is done. 'blocked' = you cannot proceed without something external (list it in `blockers`). 'failed' = you tried and it did not work."
10353
+ },
10354
+ summary: {
10355
+ type: "string",
10356
+ description: "What you did and what the result was, in your own words. This is the authoritative record of this task \u2014 prefer specifics (files, commands, findings) over restating the assignment."
10357
+ },
10358
+ handoff_notes: {
10359
+ type: "object",
10360
+ description: "Durable notes for whoever touches this code next \u2014 including the agent that resolves a merge conflict against your change. These are delivered automatically into related future tasks, so write for a reader who cannot ask you questions.",
10361
+ properties: {
10362
+ intent: {
10363
+ type: "string",
10364
+ description: "WHY you made this change \u2014 the part a diff cannot show. Required."
10365
+ },
10366
+ conflict_guidance: {
10367
+ type: "string",
10368
+ description: "If someone else changed the same code, how should the conflict be resolved? State which property of your change must survive."
10369
+ },
10370
+ touched_files: {
10371
+ type: "array",
10372
+ items: { type: "string" },
10373
+ description: "Files you changed. Required \u2014 this is how your note is matched to future work on the same code."
10374
+ },
10375
+ follow_ups: {
10376
+ type: "array",
10377
+ items: { type: "string" },
10378
+ description: "Work you deliberately did not do, that someone should pick up."
10379
+ }
10380
+ },
10381
+ required: ["intent", "touched_files"]
10382
+ },
10383
+ touched_files: {
10384
+ type: "array",
10385
+ items: { type: "string" },
10386
+ description: "Files this task changed."
10387
+ },
10388
+ branch_state: {
10389
+ type: "string",
10390
+ enum: BRANCH_STATES,
10391
+ description: "Where you left the branch. A task on a non-main branch is not fully complete unless this names the follow-up state."
10392
+ },
10393
+ blockers: {
10394
+ type: "array",
10395
+ items: { type: "string" },
10396
+ description: "What is blocking you. Expected when outcome is 'blocked'."
10397
+ }
10398
+ },
10399
+ required: ["outcome", "summary"]
10400
+ }
10401
+ };
10402
+ var PROGRESS_UPDATE_TOOL = {
10403
+ name: "progress_update",
10404
+ description: "Record a short progress note mid-task. Does not end the task. Use it on long work so the coordinator can see movement without interrupting you.",
10405
+ inputSchema: {
10406
+ type: "object",
10407
+ properties: {
10408
+ note: { type: "string", description: "What you are doing or what you just learned." }
10409
+ },
10410
+ required: ["note"]
10411
+ }
10412
+ };
10413
+ var ALL_WORKER_TOOLS = [REPORT_COMPLETION_TOOL, PROGRESS_UPDATE_TOOL];
10414
+ function toDaemonReport(a) {
10415
+ const notes = a.handoff_notes;
10416
+ return {
10417
+ outcome: a.outcome,
10418
+ summary: a.summary,
10419
+ ...a.touched_files !== void 0 ? { touchedFiles: a.touched_files } : {},
10420
+ ...a.branch_state !== void 0 ? { branchState: a.branch_state } : {},
10421
+ ...a.blockers !== void 0 ? { blockers: a.blockers } : {},
10422
+ ...notes && typeof notes === "object" && !Array.isArray(notes) ? {
10423
+ handoffNotes: {
10424
+ ...notes.intent !== void 0 ? { intent: notes.intent } : {},
10425
+ ...notes.conflict_guidance !== void 0 ? { conflictGuidance: notes.conflict_guidance } : {},
10426
+ ...notes.touched_files !== void 0 ? { touchedFiles: notes.touched_files } : {},
10427
+ ...notes.follow_ups !== void 0 ? { followUps: notes.follow_ups } : {}
10428
+ }
10429
+ } : {}
10430
+ };
10431
+ }
10432
+ async function reportCompletion(transport, credentials, args) {
10433
+ const result = await transport.command("worker_report_completion", {
10434
+ ...credentials,
10435
+ report: toDaemonReport(args)
10436
+ });
10437
+ if (result?.success === true) {
10438
+ const lines = [
10439
+ result.duplicate ? `Completion already recorded for task ${result.taskId} \u2014 this repeat was accepted as a duplicate.` : `Completion recorded for task ${result.taskId} (${result.outcome}).`
10440
+ ];
10441
+ if (result.handoffNoteRecorded) {
10442
+ lines.push("Handoff note stored \u2014 it will be delivered to related future tasks automatically.");
10443
+ }
10444
+ return { text: lines.join("\n") };
10445
+ }
10446
+ if (result?.error === "invalid_report" && Array.isArray(result.validationErrors)) {
10447
+ const details = result.validationErrors.map((e) => ` - ${e.field || "(root)"}: ${e.message}`).join("\n");
10448
+ return {
10449
+ text: `report_completion rejected \u2014 fix these and call again:
10450
+ ${details}`,
10451
+ isError: true
10452
+ };
10453
+ }
10454
+ const reason = result?.error || "unknown_error";
10455
+ const hint = result?.hint ? `
10456
+ ${result.hint}` : "";
10457
+ const detail = result?.detail ? `
10458
+ Detail: ${result.detail}` : "";
10459
+ return { text: `report_completion refused (${reason}).${hint}${detail}`, isError: true };
10460
+ }
10461
+ async function progressUpdate(transport, credentials, args) {
10462
+ const note = typeof args?.note === "string" ? args.note.trim() : "";
10463
+ if (!note) return { text: "progress_update requires a non-empty `note`.", isError: true };
10464
+ const result = await transport.command("worker_progress_update", { ...credentials, note });
10465
+ if (result?.success === true) {
10466
+ return { text: `Progress noted for task ${result.taskId ?? "(unknown)"}.` };
10467
+ }
10468
+ return {
10469
+ text: `progress_update refused (${result?.error || "unknown_error"}).`,
10470
+ isError: true
10471
+ };
10472
+ }
10473
+
10330
10474
  // src/help.ts
10331
10475
  var STANDARD_TOOLS = [
10332
10476
  "list_daemons",
@@ -10347,12 +10491,14 @@ var STANDARD_TOOLS = [
10347
10491
  ];
10348
10492
  function buildMcpHelpText() {
10349
10493
  const meshTools = ALL_MESH_TOOLS.map((tool) => tool.name);
10494
+ const workerTools = ALL_WORKER_TOOLS.map((tool) => tool.name);
10350
10495
  return `
10351
10496
  ADHDev MCP Server
10352
10497
 
10353
10498
  Usage:
10354
10499
  adhdev mcp Local mode (requires standalone daemon)
10355
10500
  adhdev mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
10501
+ adhdev mcp --mode ipc --worker Delegated-worker mode (daemon-launched; needs a session bind)
10356
10502
  adhdev-mcp --help Compatibility bin (same server, legacy package entrypoint)
10357
10503
 
10358
10504
  Options:
@@ -10360,18 +10506,70 @@ Options:
10360
10506
  --port <n> Standalone or IPC daemon port (defaults: local 3847, ipc 19222)
10361
10507
  --password <pass> Standalone daemon password (if set)
10362
10508
  --repo-mesh <mesh_id> Enable mesh mode \u2014 exposes only mesh-scoped coordinator tools
10509
+ --worker Enable worker mode \u2014 the minimal delegated-worker toolset.
10510
+ Overrides --repo-mesh: a worker never gets coordinator tools.
10363
10511
  --help Show this help
10364
10512
 
10365
10513
  Environment variables:
10366
10514
  ADHDEV_PASSWORD Daemon password (local mode)
10367
10515
  ADHDEV_MESH_ID Mesh ID (mesh mode)
10368
10516
  ADHDEV_MCP_TRANSPORT Transport: local or ipc
10517
+ ADHDEV_WORKER_SESSION_BIND Worker session bind (worker mode; written by the daemon)
10518
+ ADHDEV_WORKER_TASK_TOKEN Worker task token (worker mode; alternative to the bind)
10369
10519
 
10370
10520
  Standard tools: ${STANDARD_TOOLS.join(", ")}
10371
10521
  Mesh tools: ${meshTools.join(", ")}
10522
+ Worker tools: ${workerTools.join(", ")}, git_status, git_log, git_diff
10372
10523
  `.trim();
10373
10524
  }
10374
10525
 
10526
+ // src/cli-args.ts
10527
+ function parseArgs(argv, env = process.env) {
10528
+ const args = argv.slice(2);
10529
+ let port;
10530
+ let password;
10531
+ let meshId;
10532
+ let explicitMode;
10533
+ let worker = false;
10534
+ for (let i = 0; i < args.length; i++) {
10535
+ const arg = args[i];
10536
+ if (arg === "--mode" && args[i + 1]) {
10537
+ const value = String(args[++i]).trim();
10538
+ if (value === "local" || value === "ipc") explicitMode = value;
10539
+ } else if (arg?.startsWith("--mode=")) {
10540
+ const value = arg.slice("--mode=".length).trim();
10541
+ if (value === "local" || value === "ipc") explicitMode = value;
10542
+ } else if (arg === "--port" && args[i + 1]) {
10543
+ port = Number(args[++i]);
10544
+ } else if (arg?.startsWith("--port=")) {
10545
+ port = Number(arg.slice("--port=".length));
10546
+ } else if (arg === "--password" && args[i + 1]) {
10547
+ password = args[++i];
10548
+ } else if ((arg === "--repo-mesh" || arg === "--mesh") && args[i + 1]) {
10549
+ meshId = args[++i];
10550
+ } else if (arg?.startsWith("--repo-mesh=")) {
10551
+ meshId = arg.slice("--repo-mesh=".length);
10552
+ } else if (arg === "--worker") {
10553
+ worker = true;
10554
+ } else if (arg === "--help" || arg === "-h") {
10555
+ printHelp();
10556
+ process.exit(0);
10557
+ }
10558
+ }
10559
+ if (!password && env.ADHDEV_PASSWORD) password = env.ADHDEV_PASSWORD;
10560
+ if (!meshId && env.ADHDEV_MESH_ID) meshId = env.ADHDEV_MESH_ID;
10561
+ if (!explicitMode && env.ADHDEV_MCP_TRANSPORT) {
10562
+ const value = env.ADHDEV_MCP_TRANSPORT.trim();
10563
+ if (value === "local" || value === "ipc") explicitMode = value;
10564
+ }
10565
+ const mode = explicitMode || (meshId && env.ADHDEV_INLINE_MESH ? "ipc" : "local");
10566
+ if (worker) return { mode, port, password, worker: true };
10567
+ return { mode, port, password, meshId };
10568
+ }
10569
+ function printHelp() {
10570
+ console.error(buildMcpHelpText());
10571
+ }
10572
+
10375
10573
  // src/server.ts
10376
10574
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
10377
10575
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
@@ -11492,6 +11690,66 @@ async function startMcpServer(opts) {
11492
11690
  process.exit(1);
11493
11691
  }
11494
11692
  const isLocal = opts.mode === "local";
11693
+ if (opts.worker) {
11694
+ const credentials = readWorkerCredentials();
11695
+ if (!credentials.bind && !credentials.token) {
11696
+ process.stderr.write(
11697
+ "[adhdev-mcp] Worker mode requires ADHDEV_WORKER_SESSION_BIND (or ADHDEV_WORKER_TASK_TOKEN) in the MCP server env. This is written by the daemon when it launches a delegated worker.\n"
11698
+ );
11699
+ process.exit(1);
11700
+ }
11701
+ const workerTools = [...ALL_WORKER_TOOLS, GIT_STATUS_TOOL, GIT_LOG_TOOL, GIT_DIFF_TOOL];
11702
+ const workerToolByName = new Map(
11703
+ workerTools.map((tool) => [tool.name, tool])
11704
+ );
11705
+ const server2 = new import_server.Server(
11706
+ { name: "adhdev-mcp-server", version: MCP_SERVER_VERSION },
11707
+ { capabilities: { tools: {} } }
11708
+ );
11709
+ server2.setRequestHandler(import_types.ListToolsRequestSchema, async () => ({ tools: workerTools }));
11710
+ server2.setRequestHandler(import_types.CallToolRequestSchema, async (req) => {
11711
+ const { name, arguments: args } = req.params;
11712
+ const a = args ?? {};
11713
+ const workerTool = workerToolByName.get(name);
11714
+ if (workerTool) {
11715
+ const unknownArgsError = unknownToolArgsError(name, workerTool.inputSchema?.properties, a);
11716
+ if (unknownArgsError) return { content: [{ type: "text", text: unknownArgsError }], isError: true };
11717
+ }
11718
+ try {
11719
+ switch (name) {
11720
+ case "report_completion": {
11721
+ const result = await reportCompletion(transport, credentials, a);
11722
+ return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
11723
+ }
11724
+ case "progress_update": {
11725
+ const result = await progressUpdate(transport, credentials, a);
11726
+ return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
11727
+ }
11728
+ case "git_status": {
11729
+ const text = await gitStatus(transport, { workspace: a.workspace, include_diff: a.include_diff, format: a.format });
11730
+ return { content: [{ type: "text", text }] };
11731
+ }
11732
+ case "git_log": {
11733
+ const text = await gitLog(transport, { workspace: a.workspace, limit: a.limit, file: a.file, since: a.since, until: a.until, format: a.format });
11734
+ return { content: [{ type: "text", text }] };
11735
+ }
11736
+ case "git_diff": {
11737
+ const text = await gitDiff(transport, { workspace: a.workspace, file: a.file, max_lines: a.max_lines, staged: a.staged, format: a.format });
11738
+ return { content: [{ type: "text", text }] };
11739
+ }
11740
+ default:
11741
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
11742
+ }
11743
+ } catch (err) {
11744
+ return { content: [{ type: "text", text: `Error: ${err?.message ?? String(err)}` }], isError: true };
11745
+ }
11746
+ });
11747
+ const stdioTransport2 = new import_stdio.StdioServerTransport();
11748
+ await server2.connect(stdioTransport2);
11749
+ process.stderr.write(`[adhdev-mcp] Server running in ${opts.mode} WORKER mode \u2014 ${workerTools.length} tools.
11750
+ `);
11751
+ return;
11752
+ }
11495
11753
  if (opts.meshId) {
11496
11754
  let mesh;
11497
11755
  if (!mesh && process.env.ADHDEV_INLINE_MESH) {
@@ -11952,47 +12210,6 @@ async function startMcpServer(opts) {
11952
12210
  }
11953
12211
 
11954
12212
  // src/index.ts
11955
- function parseArgs(argv, env = process.env) {
11956
- const args = argv.slice(2);
11957
- let port;
11958
- let password;
11959
- let meshId;
11960
- let explicitMode;
11961
- for (let i = 0; i < args.length; i++) {
11962
- const arg = args[i];
11963
- if (arg === "--mode" && args[i + 1]) {
11964
- const value = String(args[++i]).trim();
11965
- if (value === "local" || value === "ipc") explicitMode = value;
11966
- } else if (arg?.startsWith("--mode=")) {
11967
- const value = arg.slice("--mode=".length).trim();
11968
- if (value === "local" || value === "ipc") explicitMode = value;
11969
- } else if (arg === "--port" && args[i + 1]) {
11970
- port = Number(args[++i]);
11971
- } else if (arg?.startsWith("--port=")) {
11972
- port = Number(arg.slice("--port=".length));
11973
- } else if (arg === "--password" && args[i + 1]) {
11974
- password = args[++i];
11975
- } else if ((arg === "--repo-mesh" || arg === "--mesh") && args[i + 1]) {
11976
- meshId = args[++i];
11977
- } else if (arg?.startsWith("--repo-mesh=")) {
11978
- meshId = arg.slice("--repo-mesh=".length);
11979
- } else if (arg === "--help" || arg === "-h") {
11980
- printHelp();
11981
- process.exit(0);
11982
- }
11983
- }
11984
- if (!password && env.ADHDEV_PASSWORD) password = env.ADHDEV_PASSWORD;
11985
- if (!meshId && env.ADHDEV_MESH_ID) meshId = env.ADHDEV_MESH_ID;
11986
- if (!explicitMode && env.ADHDEV_MCP_TRANSPORT) {
11987
- const value = env.ADHDEV_MCP_TRANSPORT.trim();
11988
- if (value === "local" || value === "ipc") explicitMode = value;
11989
- }
11990
- const mode = explicitMode || (meshId && env.ADHDEV_INLINE_MESH ? "ipc" : "local");
11991
- return { mode, port, password, meshId };
11992
- }
11993
- function printHelp() {
11994
- console.error(buildMcpHelpText());
11995
- }
11996
12213
  startMcpServer(parseArgs(process.argv)).catch((err) => {
11997
12214
  process.stderr.write(`[adhdev-mcp] Fatal: ${err?.message ?? err}
11998
12215
  `);