cmdr-mcp 0.4.0 → 0.5.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.
Files changed (33) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +29 -6
  3. package/docs/README.zh-CN.md +29 -6
  4. package/docs/agent-integration.md +2 -2
  5. package/docs/dashboard-plan.md +327 -0
  6. package/docs/dashboard.md +101 -0
  7. package/docs/implementation.md +25 -4
  8. package/docs/publishing.md +10 -8
  9. package/docs/setup.md +1 -1
  10. package/docs/troubleshooting.md +2 -2
  11. package/marketplace.json +1 -1
  12. package/package.json +9 -1
  13. package/plugins/cmdr/.claude-plugin/plugin.json +1 -1
  14. package/plugins/cmdr/.codex-plugin/plugin.json +1 -1
  15. package/plugins/cmdr/.zcode-plugin/plugin.json +1 -1
  16. package/plugins/cmdr/README.md +3 -1
  17. package/plugins/cmdr/THIRD_PARTY_NOTICES.txt +130 -0
  18. package/plugins/cmdr/bin/cmdr-check.mjs +3 -0
  19. package/plugins/cmdr/commands/cmdr.md +2 -0
  20. package/plugins/cmdr/dist/cli.mjs +121 -31
  21. package/plugins/cmdr/dist/daemon.mjs +4729 -137
  22. package/plugins/cmdr/dist/dashboard/app.css +1 -0
  23. package/plugins/cmdr/dist/dashboard/app.js +10 -0
  24. package/plugins/cmdr/dist/dashboard/index.html +13 -0
  25. package/plugins/cmdr/dist/hook.mjs +2 -2
  26. package/plugins/cmdr/dist/integrity.json +20 -17
  27. package/plugins/cmdr/dist/mcp.mjs +140 -64
  28. package/plugins/cmdr/skills/cmdr/SKILL.md +3 -1
  29. package/plugins/cmdr/skills/cmdr/references/commander.md +2 -0
  30. package/plugins/cmdr/skills/cmdr/references/executor.md +2 -0
  31. package/plugins/cmdr/skills/cmdr-commander/SKILL.md +2 -0
  32. package/plugins/cmdr/skills/cmdr-executor/SKILL.md +2 -0
  33. package/plugins/cmdr/skills/using-cmdr/SKILL.md +2 -2
@@ -240,26 +240,26 @@ var Rpc = class extends EventEmitter {
240
240
  if (this.socket.destroyed) return Promise.reject(new CmdrError("DAEMON_UNAVAILABLE"));
241
241
  if (signal?.aborted) return Promise.reject(new CmdrError("REQUEST_CANCELLED"));
242
242
  return new Promise((resolve4, reject) => {
243
- const id = this.next++;
243
+ const id2 = this.next++;
244
244
  const cancel = (code) => {
245
- const p = this.pending.get(id);
245
+ const p = this.pending.get(id2);
246
246
  if (!p) return;
247
- this.pending.delete(id);
247
+ this.pending.delete(id2);
248
248
  clearTimeout(p.timer);
249
249
  p.cleanup();
250
- this.notify("rpc.cancel", { id });
250
+ this.notify("rpc.cancel", { id: id2 });
251
251
  reject(new CmdrError(code));
252
252
  };
253
253
  const abort = () => cancel("REQUEST_CANCELLED");
254
254
  const timer = setTimeout(() => cancel("DAEMON_UNAVAILABLE"), timeout);
255
- this.pending.set(id, {
255
+ this.pending.set(id2, {
256
256
  resolve: resolve4,
257
257
  reject,
258
258
  timer,
259
259
  cleanup: () => signal?.removeEventListener("abort", abort)
260
260
  });
261
261
  signal?.addEventListener("abort", abort, { once: true });
262
- this.send({ jsonrpc: "2.0", id, method, params });
262
+ this.send({ jsonrpc: "2.0", id: id2, method, params });
263
263
  });
264
264
  }
265
265
  notify(method, params) {
@@ -271,7 +271,7 @@ var Rpc = class extends EventEmitter {
271
271
  };
272
272
 
273
273
  // src/shared/version.ts
274
- var VERSION = true ? "0.4.0" : MIN_CLIENT_VERSION;
274
+ var VERSION = true ? "0.5.0" : MIN_CLIENT_VERSION;
275
275
  var PROTOCOL = 1;
276
276
  function newer(a, b) {
277
277
  const x = a.split(".").map(Number), y = b.split(".").map(Number);
@@ -4664,8 +4664,70 @@ var coerce = {
4664
4664
  };
4665
4665
  var NEVER = INVALID;
4666
4666
 
4667
+ // src/shared/dashboard.ts
4668
+ var DASHBOARD_LIMITS = {
4669
+ recordsPerKind: 200,
4670
+ runsPerTask: 100,
4671
+ htmlBytes: 256 * 1024,
4672
+ htmlBytesPerSquad: 8 * 1024 * 1024
4673
+ };
4674
+
4675
+ // src/shared/dashboard-schemas.ts
4676
+ var id = external_exports.string().min(1).max(128);
4677
+ var text = external_exports.string().max(8e3);
4678
+ var artifacts = external_exports.array(id).max(8);
4679
+ var taskFields = {
4680
+ action: external_exports.enum(["create", "update", "get", "list", "archive", "restore"]).default("list"),
4681
+ id: id.optional(),
4682
+ title: external_exports.string().trim().min(1).max(200).optional(),
4683
+ description: text.optional(),
4684
+ acceptance: text.optional(),
4685
+ position: external_exports.number().finite().optional(),
4686
+ artifact_ids: artifacts.optional(),
4687
+ archived: external_exports.boolean().optional(),
4688
+ offset: external_exports.number().int().min(0).default(0),
4689
+ limit: external_exports.number().int().min(1).max(50).default(20)
4690
+ };
4691
+ var questionFields = {
4692
+ target: external_exports.enum(["commander", "user"]).default("commander"),
4693
+ action: external_exports.enum(["create", "update", "get", "list", "withdraw", "handle"]).default("create"),
4694
+ id: id.optional(),
4695
+ task_id: id.optional(),
4696
+ version: external_exports.number().int().positive().optional(),
4697
+ description: text.optional(),
4698
+ kind: external_exports.enum(["single", "multiple", "text", "confirm"]).optional(),
4699
+ options: external_exports.array(external_exports.object({ id, label: external_exports.string().trim().min(1).max(300) }).strict()).max(20).optional(),
4700
+ artifact_ids: artifacts.optional(),
4701
+ result: text.optional(),
4702
+ status: external_exports.enum(["pending", "answered", "handled", "withdrawn"]).optional(),
4703
+ offset: external_exports.number().int().min(0).default(0),
4704
+ limit: external_exports.number().int().min(1).max(50).default(20)
4705
+ };
4706
+ var artifactFields = {
4707
+ action: external_exports.enum(["publish", "get", "list"]).default("publish"),
4708
+ id: id.optional(),
4709
+ version: external_exports.number().int().positive().optional(),
4710
+ title: external_exports.string().trim().min(1).max(200).optional(),
4711
+ html: external_exports.string().min(1).refine((s) => Buffer.byteLength(s) <= DASHBOARD_LIMITS.htmlBytes, "MESSAGE_TOO_LARGE").optional(),
4712
+ offset: external_exports.number().int().min(0).default(0),
4713
+ limit: external_exports.number().int().min(1).max(50).default(20)
4714
+ };
4715
+ var answerSchema = external_exports.object({
4716
+ question_id: id,
4717
+ version: external_exports.number().int().positive(),
4718
+ submission_id: external_exports.string().uuid(),
4719
+ selected: external_exports.array(id).max(20).default([]),
4720
+ text: text.default(""),
4721
+ confirmed: external_exports.boolean().optional()
4722
+ }).strict();
4723
+ var userMessageSchema = external_exports.object({
4724
+ squad_id: id,
4725
+ submission_id: external_exports.string().uuid(),
4726
+ text: external_exports.string().trim().min(1).max(8e3)
4727
+ }).strict();
4728
+
4667
4729
  // src/shared/schemas.ts
4668
- var text = external_exports.string().min(1).refine((v) => Buffer.byteLength(v) <= LIMITS.maxBody, "MESSAGE_TOO_LARGE");
4730
+ var text2 = external_exports.string().min(1).refine((v) => Buffer.byteLength(v) <= LIMITS.maxBody, "MESSAGE_TOO_LARGE");
4669
4731
  var data = external_exports.record(external_exports.unknown()).refine((v) => Buffer.byteLength(JSON.stringify(v)) <= LIMITS.maxData, "MESSAGE_TOO_LARGE").optional();
4670
4732
  var wait = external_exports.number().min(0).max(300).default(0);
4671
4733
  var identity = { _cmdr_session: external_exports.string().min(1).max(256).optional() };
@@ -4675,7 +4737,7 @@ var schemas = {
4675
4737
  role: external_exports.enum(["commander", "executor"]).optional(),
4676
4738
  squad: external_exports.string().optional(),
4677
4739
  name: external_exports.string().trim().min(1).max(64).optional(),
4678
- note: text.optional(),
4740
+ note: text2.optional(),
4679
4741
  squad_name: external_exports.string().trim().min(1).max(64).optional(),
4680
4742
  takeover: external_exports.boolean().default(false),
4681
4743
  standby: external_exports.enum(["auto", "manual"]).optional(),
@@ -4690,17 +4752,27 @@ var schemas = {
4690
4752
  report: external_exports.object({
4691
4753
  ...identity,
4692
4754
  status: external_exports.enum(["ready", "working", "blocked", "done", "failed", "cancelled"]),
4693
- message: text,
4755
+ message: text2,
4756
+ reply_to: external_exports.string().optional(),
4757
+ data
4758
+ }).strict(),
4759
+ ask: external_exports.object({
4760
+ ...identity,
4761
+ ...questionFields,
4762
+ question: text2.optional(),
4763
+ wait,
4694
4764
  reply_to: external_exports.string().optional(),
4695
4765
  data
4696
4766
  }).strict(),
4697
- ask: external_exports.object({ ...identity, question: text, wait, reply_to: external_exports.string().optional(), data }).strict(),
4767
+ task: external_exports.object({ ...identity, ...taskFields }).strict(),
4768
+ artifact: external_exports.object({ ...identity, ...artifactFields }).strict(),
4698
4769
  send: external_exports.object({
4699
4770
  ...identity,
4700
4771
  to: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1).max(1e3)]),
4701
- message: text,
4772
+ message: text2,
4702
4773
  type: external_exports.enum(["command", "cancel", "answer", "info"]).default("command"),
4703
4774
  task_key: external_exports.string().min(1).max(128).optional(),
4775
+ task_id: external_exports.string().min(1).max(128).optional(),
4704
4776
  reassign: external_exports.string().optional(),
4705
4777
  attention: external_exports.boolean().optional(),
4706
4778
  priority: external_exports.enum(["high", "normal", "low"]).optional(),
@@ -4718,7 +4790,7 @@ var schemas = {
4718
4790
  recover: external_exports.boolean().default(false),
4719
4791
  full: external_exports.boolean().default(false)
4720
4792
  }).strict(),
4721
- leave: external_exports.object({ ...identity, dissolve: external_exports.boolean().default(false), message: text.optional() }).strict()
4793
+ leave: external_exports.object({ ...identity, dissolve: external_exports.boolean().default(false), message: text2.optional() }).strict()
4722
4794
  };
4723
4795
  function parse(tool, args) {
4724
4796
  const r = schemas[tool].safeParse(args);
@@ -4738,7 +4810,9 @@ var methods = {
4738
4810
  report: "msg.report",
4739
4811
  ask: "msg.ask",
4740
4812
  read: "msg.read",
4741
- leave: "session.leave"
4813
+ leave: "session.leave",
4814
+ task: "dashboard.task",
4815
+ artifact: "dashboard.artifact"
4742
4816
  };
4743
4817
 
4744
4818
  // src/shared/env.ts
@@ -4787,6 +4861,7 @@ async function runSession(argv) {
4787
4861
  "reply-to": { type: "string" },
4788
4862
  priority: { type: "string" },
4789
4863
  "task-key": { type: "string" },
4864
+ "task-id": { type: "string" },
4790
4865
  reassign: { type: "string" },
4791
4866
  rebind: { type: "string" },
4792
4867
  takeover: { type: "boolean" },
@@ -4804,13 +4879,13 @@ async function runSession(argv) {
4804
4879
  });
4805
4880
  if (v.help) {
4806
4881
  console.log(
4807
- "cmdr session join|list|send|report|ask|read|leave --agent HOST --native-id ID [--input JSON] [--timeout SECONDS]\nUse --squad-name for join; --status and text for report; --to and text for send; text for ask. read supports --wait/--peek/--history. IDs must match the host session; read also supports --recover/--id/--full/--limit. Configure automatic wake with cmdr standby start --session SID; unsupported hosts remain manual."
4882
+ "cmdr session join|list|send|report|ask|read|leave|task|artifact --agent HOST --native-id ID [--input JSON] [--timeout SECONDS]\nUse --squad-name for join; --status and text for report; --to and text for send; text for ask. read supports --wait/--peek/--history. IDs must match the host session; read also supports --recover/--id/--full/--limit. Configure automatic wake with cmdr standby start --session SID; unsupported hosts remain manual."
4808
4883
  );
4809
4884
  return;
4810
4885
  }
4811
4886
  const action = args[0];
4812
4887
  if (!Object.hasOwn(schemas, action))
4813
- throw new Error("Expected session join|list|send|report|ask|read|leave");
4888
+ throw new Error("Expected session join|list|send|report|ask|read|leave|task|artifact");
4814
4889
  const agent = v.agent || process.env.CMDR_AGENT;
4815
4890
  const native = v["native-id"] || process.env.CMDR_SESSION_ID;
4816
4891
  if (!agent || !/^[a-z][a-z0-9_-]{0,63}$/.test(agent))
@@ -4837,6 +4912,7 @@ async function runSession(argv) {
4837
4912
  "reply-to": "reply_to",
4838
4913
  priority: "priority",
4839
4914
  "task-key": "task_key",
4915
+ "task-id": "task_id",
4840
4916
  reassign: "reassign",
4841
4917
  rebind: "rebind",
4842
4918
  takeover: "takeover",
@@ -4952,9 +5028,9 @@ async function probeMcp(root) {
4952
5028
  reject(new Error("MCP exited"));
4953
5029
  return;
4954
5030
  }
4955
- const id = ++requestId;
4956
- pending.set(id, { resolve: resolve4, reject });
4957
- child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
5031
+ const id2 = ++requestId;
5032
+ pending.set(id2, { resolve: resolve4, reject });
5033
+ child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id2, method, params }) + "\n");
4958
5034
  });
4959
5035
  let timer;
4960
5036
  try {
@@ -4970,8 +5046,8 @@ async function probeMcp(root) {
4970
5046
  );
4971
5047
  const result = await request("tools/list", {});
4972
5048
  const names = result.tools?.map((t) => t.name).sort();
4973
- if (JSON.stringify(names) !== JSON.stringify(["ask", "join", "leave", "list", "read", "report", "send"]))
4974
- throw new Error("Expected seven cmdr tools");
5049
+ if (JSON.stringify(names) !== JSON.stringify(Object.keys(schemas).sort()))
5050
+ throw new Error("Unexpected cmdr tool set");
4975
5051
  const call = await request("tools/call", { name: "list", arguments: {} });
4976
5052
  if (call.isError) throw new Error("MCP tool cannot reach daemon");
4977
5053
  return { ok: true, tools: names, isolated: true };
@@ -5837,9 +5913,9 @@ function object(value, label) {
5837
5913
  throw new Error(`${label} must be an object; no configuration was changed.`);
5838
5914
  return value;
5839
5915
  }
5840
- function jsonConfig(text2, label) {
5916
+ function jsonConfig(text3, label) {
5841
5917
  try {
5842
- return object(JSON.parse(text2 || "{}"), label);
5918
+ return object(JSON.parse(text3 || "{}"), label);
5843
5919
  } catch {
5844
5920
  throw new Error(`Cannot parse ${label} as a JSON object; no configuration was changed.`);
5845
5921
  }
@@ -5876,10 +5952,10 @@ function mergeJsonServer(config, command, host) {
5876
5952
  const servers = parent[key] = object(parent[key] ?? {}, key);
5877
5953
  servers.cmdr = serverConfig(servers.cmdr, command, host);
5878
5954
  }
5879
- function codexConfig(text2, command) {
5955
+ function codexConfig(text3, command) {
5880
5956
  let config;
5881
5957
  try {
5882
- config = parse2(text2, { integersAsBigInt: "asNeeded" });
5958
+ config = parse2(text3, { integersAsBigInt: "asNeeded" });
5883
5959
  } catch {
5884
5960
  throw new Error("Cannot parse Codex config.toml; no configuration was changed.");
5885
5961
  }
@@ -5888,20 +5964,20 @@ function codexConfig(text2, command) {
5888
5964
  const server = serverConfig(servers.cmdr, command, "codex");
5889
5965
  const block = `${begin}
5890
5966
  ${stringify({ mcp_servers: { cmdr: server } })}${end}`;
5891
- const start = text2.indexOf(begin), stop = text2.indexOf(end);
5967
+ const start = text3.indexOf(begin), stop = text3.indexOf(end);
5892
5968
  let output;
5893
5969
  if (start >= 0 || stop >= 0) {
5894
- if (start < 0 || stop < start || text2.indexOf(begin, start + begin.length) >= 0 || text2.indexOf(end, stop + end.length) >= 0)
5970
+ if (start < 0 || stop < start || text3.indexOf(begin, start + begin.length) >= 0 || text3.indexOf(end, stop + end.length) >= 0)
5895
5971
  throw new Error(
5896
5972
  "Invalid cmdr setup markers in config.toml. Restore the managed block before retrying."
5897
5973
  );
5898
- output = text2.slice(0, start) + block + text2.slice(stop + end.length);
5974
+ output = text3.slice(0, start) + block + text3.slice(stop + end.length);
5899
5975
  } else {
5900
5976
  if (servers.cmdr !== void 0)
5901
5977
  throw new Error(
5902
5978
  "The cmdr MCP entry is not managed by setup. Remove that entry before retrying."
5903
5979
  );
5904
- output = `${text2}${text2 && !text2.endsWith("\n") ? "\n" : ""}
5980
+ output = `${text3}${text3 && !text3.endsWith("\n") ? "\n" : ""}
5905
5981
  ${block}
5906
5982
  `;
5907
5983
  }
@@ -6314,6 +6390,7 @@ if (process.argv[2] === "setup") {
6314
6390
  follow: { type: "boolean" },
6315
6391
  full: { type: "boolean" },
6316
6392
  json: { type: "boolean" },
6393
+ "no-open": { type: "boolean" },
6317
6394
  help: { type: "boolean" }
6318
6395
  }
6319
6396
  });
@@ -6404,9 +6481,22 @@ if (process.argv[2] === "setup") {
6404
6481
  try {
6405
6482
  if (v.help)
6406
6483
  process.stdout.write(
6407
- "cmdr status | setup --agent claude-code|codex|zcode [--dry-run] [--json] | list [--all] [--squad ID] | tail [--follow] [--full] [--json] [--after EVENT_SEQ|now] [--actionable] [--format line|json] [--for SID] | standby start|status|stop|resume|watch --session SID [--adapter codex|claude|zcode|manual] [--transport auto|proxy|queue] [--once] | send --squad ID [--to MEMBER] [--type command|cancel|info|answer] TEXT | read --session SID [--peek] | daemon start|stop|restart|status|logs | config [--agent HOST] [--session ID] | doctor [--plugin-root PATH] [--deep] | session --help | purge [--all]\n"
6484
+ "cmdr status | dashboard [--no-open] [--json] | setup --agent claude-code|codex|zcode [--dry-run] [--json] | list [--all] [--squad ID] | tail [--follow] [--full] [--json] [--after EVENT_SEQ|now] [--actionable] [--format line|json] [--for SID] | standby start|status|stop|resume|watch --session SID [--adapter codex|claude|zcode|manual] [--transport auto|proxy|queue] [--once] | send --squad ID [--to MEMBER] [--type command|cancel|info|answer] TEXT | read --session SID [--peek] | daemon start|stop|restart|status|logs | config [--agent HOST] [--session ID] | doctor [--plugin-root PATH] [--deep] | session --help | purge [--all]\n"
6408
6485
  );
6409
- else if (cmd === "config") {
6486
+ else if (cmd === "dashboard") {
6487
+ const result = await call("admin.dashboard", {}, true);
6488
+ print(result);
6489
+ if (!v["no-open"]) {
6490
+ try {
6491
+ execFileSync(process.platform === "darwin" ? "open" : "xdg-open", [result.url], {
6492
+ stdio: "ignore",
6493
+ timeout: 5e3
6494
+ });
6495
+ } catch {
6496
+ console.error("Open the printed URL in a local browser (valid for 60 seconds).");
6497
+ }
6498
+ }
6499
+ } else if (cmd === "config") {
6410
6500
  const agent = v.agent || "generic";
6411
6501
  if (!/^[a-z][a-z0-9_-]{0,63}$/.test(agent))
6412
6502
  throw new Error("Invalid --agent: use lowercase letters, digits, underscores or hyphens.");