cmdr-mcp 0.4.0 → 0.6.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/.claude-plugin/marketplace.json +2 -2
- package/README.md +44 -17
- package/docs/README.zh-CN.md +33 -8
- package/docs/agent-integration.md +74 -12
- package/docs/dashboard-plan.md +327 -0
- package/docs/dashboard.md +101 -0
- package/docs/implementation.md +25 -4
- package/docs/long-running-collaboration.md +4 -2
- package/docs/publishing.md +11 -9
- package/docs/setup.md +4 -3
- package/docs/troubleshooting.md +3 -3
- package/marketplace.json +2 -2
- package/package.json +10 -2
- package/plugins/cmdr/.claude-plugin/plugin.json +1 -1
- package/plugins/cmdr/.codex-plugin/plugin.json +1 -1
- package/plugins/cmdr/.kimi-plugin/plugin.json +65 -0
- package/plugins/cmdr/.zcode-plugin/plugin.json +1 -1
- package/plugins/cmdr/README.md +7 -5
- package/plugins/cmdr/THIRD_PARTY_NOTICES.txt +130 -0
- package/plugins/cmdr/bin/cmdr-check.mjs +6 -1
- package/plugins/cmdr/bin/cmdr-node +8 -1
- package/plugins/cmdr/commands/cmdr.md +3 -1
- package/plugins/cmdr/dist/cli.mjs +226 -43
- package/plugins/cmdr/dist/daemon.mjs +4792 -161
- package/plugins/cmdr/dist/dashboard/app.css +1 -0
- package/plugins/cmdr/dist/dashboard/app.js +10 -0
- package/plugins/cmdr/dist/dashboard/index.html +13 -0
- package/plugins/cmdr/dist/hook.mjs +22 -4
- package/plugins/cmdr/dist/integrity.json +24 -19
- package/plugins/cmdr/dist/mcp.mjs +148 -66
- package/plugins/cmdr/kimi-identity/SKILL.md +12 -0
- package/plugins/cmdr/skills/cmdr/SKILL.md +4 -2
- package/plugins/cmdr/skills/cmdr/references/commander.md +3 -1
- package/plugins/cmdr/skills/cmdr/references/executor.md +3 -1
- package/plugins/cmdr/skills/cmdr/references/setup.md +1 -1
- package/plugins/cmdr/skills/cmdr-commander/SKILL.md +3 -1
- package/plugins/cmdr/skills/cmdr-executor/SKILL.md +3 -1
- package/plugins/cmdr/skills/using-cmdr/SKILL.md +3 -3
|
@@ -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
|
|
243
|
+
const id2 = this.next++;
|
|
244
244
|
const cancel = (code) => {
|
|
245
|
-
const p = this.pending.get(
|
|
245
|
+
const p = this.pending.get(id2);
|
|
246
246
|
if (!p) return;
|
|
247
|
-
this.pending.delete(
|
|
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(
|
|
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.
|
|
274
|
+
var VERSION = true ? "0.6.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);
|
|
@@ -497,7 +497,7 @@ async function watch(sid, once = false) {
|
|
|
497
497
|
instruction: "Call read and read(recover=true); handle cancellation first and report with reply_to."
|
|
498
498
|
}) + "\n"
|
|
499
499
|
);
|
|
500
|
-
if (once || result.wake_mode === "zcode") stop();
|
|
500
|
+
if (once || result.wake_mode === "zcode" || result.wake_mode === "kimi") stop();
|
|
501
501
|
};
|
|
502
502
|
const check = async () => {
|
|
503
503
|
if (stopped) return;
|
|
@@ -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
|
|
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:
|
|
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:
|
|
4755
|
+
message: text2,
|
|
4694
4756
|
reply_to: external_exports.string().optional(),
|
|
4695
4757
|
data
|
|
4696
4758
|
}).strict(),
|
|
4697
|
-
ask: external_exports.object({
|
|
4759
|
+
ask: external_exports.object({
|
|
4760
|
+
...identity,
|
|
4761
|
+
...questionFields,
|
|
4762
|
+
question: text2.optional(),
|
|
4763
|
+
wait,
|
|
4764
|
+
reply_to: external_exports.string().optional(),
|
|
4765
|
+
data
|
|
4766
|
+
}).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:
|
|
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:
|
|
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,10 +4810,13 @@ 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
|
|
4819
|
+
var cmdrTool = /(?:^|[_:])cmdr(?:__|:)(list|join|report|leave|ask|send|read|task|artifact)$/;
|
|
4745
4820
|
function waitRecommendation(agent, env = process.env) {
|
|
4746
4821
|
const timeout = Number(env.CMDR_TOOL_TIMEOUT_SEC);
|
|
4747
4822
|
if (Number.isFinite(timeout) && timeout > 0) {
|
|
@@ -4787,6 +4862,7 @@ async function runSession(argv) {
|
|
|
4787
4862
|
"reply-to": { type: "string" },
|
|
4788
4863
|
priority: { type: "string" },
|
|
4789
4864
|
"task-key": { type: "string" },
|
|
4865
|
+
"task-id": { type: "string" },
|
|
4790
4866
|
reassign: { type: "string" },
|
|
4791
4867
|
rebind: { type: "string" },
|
|
4792
4868
|
takeover: { type: "boolean" },
|
|
@@ -4804,13 +4880,13 @@ async function runSession(argv) {
|
|
|
4804
4880
|
});
|
|
4805
4881
|
if (v.help) {
|
|
4806
4882
|
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."
|
|
4883
|
+
"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
4884
|
);
|
|
4809
4885
|
return;
|
|
4810
4886
|
}
|
|
4811
4887
|
const action = args[0];
|
|
4812
4888
|
if (!Object.hasOwn(schemas, action))
|
|
4813
|
-
throw new Error("Expected session join|list|send|report|ask|read|leave");
|
|
4889
|
+
throw new Error("Expected session join|list|send|report|ask|read|leave|task|artifact");
|
|
4814
4890
|
const agent = v.agent || process.env.CMDR_AGENT;
|
|
4815
4891
|
const native = v["native-id"] || process.env.CMDR_SESSION_ID;
|
|
4816
4892
|
if (!agent || !/^[a-z][a-z0-9_-]{0,63}$/.test(agent))
|
|
@@ -4837,6 +4913,7 @@ async function runSession(argv) {
|
|
|
4837
4913
|
"reply-to": "reply_to",
|
|
4838
4914
|
priority: "priority",
|
|
4839
4915
|
"task-key": "task_key",
|
|
4916
|
+
"task-id": "task_id",
|
|
4840
4917
|
reassign: "reassign",
|
|
4841
4918
|
rebind: "rebind",
|
|
4842
4919
|
takeover: "takeover",
|
|
@@ -4952,9 +5029,9 @@ async function probeMcp(root) {
|
|
|
4952
5029
|
reject(new Error("MCP exited"));
|
|
4953
5030
|
return;
|
|
4954
5031
|
}
|
|
4955
|
-
const
|
|
4956
|
-
pending.set(
|
|
4957
|
-
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
|
|
5032
|
+
const id2 = ++requestId;
|
|
5033
|
+
pending.set(id2, { resolve: resolve4, reject });
|
|
5034
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id2, method, params }) + "\n");
|
|
4958
5035
|
});
|
|
4959
5036
|
let timer;
|
|
4960
5037
|
try {
|
|
@@ -4970,8 +5047,8 @@ async function probeMcp(root) {
|
|
|
4970
5047
|
);
|
|
4971
5048
|
const result = await request("tools/list", {});
|
|
4972
5049
|
const names = result.tools?.map((t) => t.name).sort();
|
|
4973
|
-
if (JSON.stringify(names) !== JSON.stringify(
|
|
4974
|
-
throw new Error("
|
|
5050
|
+
if (JSON.stringify(names) !== JSON.stringify(Object.keys(schemas).sort()))
|
|
5051
|
+
throw new Error("Unexpected cmdr tool set");
|
|
4975
5052
|
const call = await request("tools/call", { name: "list", arguments: {} });
|
|
4976
5053
|
if (call.isError) throw new Error("MCP tool cannot reach daemon");
|
|
4977
5054
|
return { ok: true, tools: names, isolated: true };
|
|
@@ -5837,9 +5914,9 @@ function object(value, label) {
|
|
|
5837
5914
|
throw new Error(`${label} must be an object; no configuration was changed.`);
|
|
5838
5915
|
return value;
|
|
5839
5916
|
}
|
|
5840
|
-
function jsonConfig(
|
|
5917
|
+
function jsonConfig(text3, label) {
|
|
5841
5918
|
try {
|
|
5842
|
-
return object(JSON.parse(
|
|
5919
|
+
return object(JSON.parse(text3 || "{}"), label);
|
|
5843
5920
|
} catch {
|
|
5844
5921
|
throw new Error(`Cannot parse ${label} as a JSON object; no configuration was changed.`);
|
|
5845
5922
|
}
|
|
@@ -5867,7 +5944,8 @@ function serverConfig(previous, command, host) {
|
|
|
5867
5944
|
command,
|
|
5868
5945
|
args: [],
|
|
5869
5946
|
...host === "codex" ? { tool_timeout_sec: 600 } : { type: "stdio" },
|
|
5870
|
-
...host === "zcode" ? { timeoutMs: 6e5 } : {}
|
|
5947
|
+
...host === "zcode" ? { timeoutMs: 6e5 } : {},
|
|
5948
|
+
...host === "kimi" ? { toolTimeoutMs: 6e5 } : {}
|
|
5871
5949
|
};
|
|
5872
5950
|
}
|
|
5873
5951
|
function mergeJsonServer(config, command, host) {
|
|
@@ -5876,10 +5954,10 @@ function mergeJsonServer(config, command, host) {
|
|
|
5876
5954
|
const servers = parent[key] = object(parent[key] ?? {}, key);
|
|
5877
5955
|
servers.cmdr = serverConfig(servers.cmdr, command, host);
|
|
5878
5956
|
}
|
|
5879
|
-
function codexConfig(
|
|
5957
|
+
function codexConfig(text3, command) {
|
|
5880
5958
|
let config;
|
|
5881
5959
|
try {
|
|
5882
|
-
config = parse2(
|
|
5960
|
+
config = parse2(text3, { integersAsBigInt: "asNeeded" });
|
|
5883
5961
|
} catch {
|
|
5884
5962
|
throw new Error("Cannot parse Codex config.toml; no configuration was changed.");
|
|
5885
5963
|
}
|
|
@@ -5888,20 +5966,20 @@ function codexConfig(text2, command) {
|
|
|
5888
5966
|
const server = serverConfig(servers.cmdr, command, "codex");
|
|
5889
5967
|
const block = `${begin}
|
|
5890
5968
|
${stringify({ mcp_servers: { cmdr: server } })}${end}`;
|
|
5891
|
-
const start =
|
|
5969
|
+
const start = text3.indexOf(begin), stop = text3.indexOf(end);
|
|
5892
5970
|
let output;
|
|
5893
5971
|
if (start >= 0 || stop >= 0) {
|
|
5894
|
-
if (start < 0 || stop < start ||
|
|
5972
|
+
if (start < 0 || stop < start || text3.indexOf(begin, start + begin.length) >= 0 || text3.indexOf(end, stop + end.length) >= 0)
|
|
5895
5973
|
throw new Error(
|
|
5896
5974
|
"Invalid cmdr setup markers in config.toml. Restore the managed block before retrying."
|
|
5897
5975
|
);
|
|
5898
|
-
output =
|
|
5976
|
+
output = text3.slice(0, start) + block + text3.slice(stop + end.length);
|
|
5899
5977
|
} else {
|
|
5900
5978
|
if (servers.cmdr !== void 0)
|
|
5901
5979
|
throw new Error(
|
|
5902
5980
|
"The cmdr MCP entry is not managed by setup. Remove that entry before retrying."
|
|
5903
5981
|
);
|
|
5904
|
-
output = `${
|
|
5982
|
+
output = `${text3}${text3 && !text3.endsWith("\n") ? "\n" : ""}
|
|
5905
5983
|
${block}
|
|
5906
5984
|
`;
|
|
5907
5985
|
}
|
|
@@ -5920,6 +5998,58 @@ ${block}
|
|
|
5920
5998
|
serverDisabled: server.enabled === false
|
|
5921
5999
|
};
|
|
5922
6000
|
}
|
|
6001
|
+
function kimiHooksConfig(text3, executable) {
|
|
6002
|
+
const blockHooks = [
|
|
6003
|
+
{ event: "SessionStart" },
|
|
6004
|
+
{ event: "UserPromptSubmit" },
|
|
6005
|
+
{ event: "PreToolUse", matcher: cmdrTool.source },
|
|
6006
|
+
{ event: "Stop" },
|
|
6007
|
+
{ event: "SessionEnd", timeout: 1 }
|
|
6008
|
+
].map(({ event, matcher, timeout }) => ({
|
|
6009
|
+
event,
|
|
6010
|
+
...matcher ? { matcher } : {},
|
|
6011
|
+
command: `${shellQuote(executable)} ${event}`,
|
|
6012
|
+
timeout: timeout ?? 5
|
|
6013
|
+
}));
|
|
6014
|
+
const block = `${begin}
|
|
6015
|
+
${stringify({ hooks: blockHooks })}${end}`;
|
|
6016
|
+
const start = text3.indexOf(begin), stop = text3.indexOf(end);
|
|
6017
|
+
if ((start >= 0 || stop >= 0) && (start < 0 || stop < start))
|
|
6018
|
+
throw new Error(
|
|
6019
|
+
"Invalid cmdr setup markers in config.toml. Restore the managed block before retrying."
|
|
6020
|
+
);
|
|
6021
|
+
const output = start >= 0 ? text3.slice(0, start) + block + text3.slice(stop + end.length) : `${text3}${text3 && !text3.endsWith("\n") ? "\n" : ""}
|
|
6022
|
+
${block}
|
|
6023
|
+
`;
|
|
6024
|
+
let full;
|
|
6025
|
+
try {
|
|
6026
|
+
full = parse2(text3, { integersAsBigInt: "asNeeded" });
|
|
6027
|
+
} catch {
|
|
6028
|
+
throw new Error("Cannot parse Kimi Code config.toml; no configuration was changed.");
|
|
6029
|
+
}
|
|
6030
|
+
const allHooks = full.hooks ?? [];
|
|
6031
|
+
if (!Array.isArray(allHooks)) throw new Error("hooks must be an array of [[hooks]] tables.");
|
|
6032
|
+
let previous = [];
|
|
6033
|
+
if (start >= 0)
|
|
6034
|
+
try {
|
|
6035
|
+
previous = parse2(text3.slice(start, stop + end.length), {
|
|
6036
|
+
integersAsBigInt: "asNeeded"
|
|
6037
|
+
}).hooks ?? [];
|
|
6038
|
+
} catch {
|
|
6039
|
+
previous = [];
|
|
6040
|
+
}
|
|
6041
|
+
const kept = allHooks.filter((h) => !previous.some((o) => isDeepStrictEqual(o, h)));
|
|
6042
|
+
const expected = { ...full, hooks: [...kept, ...blockHooks] };
|
|
6043
|
+
try {
|
|
6044
|
+
if (!isDeepStrictEqual(parse2(output, { integersAsBigInt: "asNeeded" }), expected))
|
|
6045
|
+
throw new Error();
|
|
6046
|
+
} catch {
|
|
6047
|
+
throw new Error(
|
|
6048
|
+
"Cannot safely merge the cmdr hooks into config.toml. Move conflicting [[hooks]] entries and retry."
|
|
6049
|
+
);
|
|
6050
|
+
}
|
|
6051
|
+
return output;
|
|
6052
|
+
}
|
|
5923
6053
|
function mergeHooks(config, executable, host) {
|
|
5924
6054
|
const container = config.hooks = object(config.hooks ?? {}, "hooks");
|
|
5925
6055
|
const hooks = host === "zcode" ? container.events = object(container.events ?? {}, "hooks.events") : container;
|
|
@@ -6106,7 +6236,7 @@ function launcher(runtime, entry, state, agent, codexHome) {
|
|
|
6106
6236
|
# Managed by cmdr setup (https://github.com/njugray/cmdr).
|
|
6107
6237
|
export CMDR_HOME=${shellQuote(state)}
|
|
6108
6238
|
${agent ? `export CMDR_AGENT=${shellQuote(agent)}
|
|
6109
|
-
` : ""}${agent === "codex" || agent === "zcode" ? "export CMDR_TOOL_TIMEOUT_SEC=600\n" : ""}${codexHome ? `export CODEX_HOME=${shellQuote(codexHome)}
|
|
6239
|
+
` : ""}${agent === "codex" || agent === "zcode" || agent === "kimi" ? "export CMDR_TOOL_TIMEOUT_SEC=600\n" : ""}${codexHome ? `export CODEX_HOME=${shellQuote(codexHome)}
|
|
6110
6240
|
` : ""}exec ${shellQuote(join6(runtime, "bin", entry))} "$@"
|
|
6111
6241
|
`;
|
|
6112
6242
|
}
|
|
@@ -6124,14 +6254,14 @@ async function runSetup(argv, sourceRoot) {
|
|
|
6124
6254
|
});
|
|
6125
6255
|
if (values.help) {
|
|
6126
6256
|
console.log(
|
|
6127
|
-
"cmdr setup --agent claude-code|codex|zcode [--config-dir PATH] [--dry-run] [--json]\nInstall or upgrade for the current user. CMDR_HOME selects runtime/state storage; --config-dir selects the host user profile."
|
|
6257
|
+
"cmdr setup --agent claude-code|codex|zcode|kimi-code [--config-dir PATH] [--dry-run] [--json]\nInstall or upgrade for the current user. CMDR_HOME selects runtime/state storage; --config-dir selects the host user profile."
|
|
6128
6258
|
);
|
|
6129
6259
|
return;
|
|
6130
6260
|
}
|
|
6131
|
-
const agent = values.agent === "claude-code" ? "claude" : values.agent;
|
|
6132
|
-
if (positionals.length || !agent || !["claude", "codex", "zcode"].includes(agent))
|
|
6261
|
+
const agent = values.agent === "claude-code" ? "claude" : values.agent === "kimi-code" ? "kimi" : values.agent;
|
|
6262
|
+
if (positionals.length || !agent || !["claude", "codex", "zcode", "kimi"].includes(agent))
|
|
6133
6263
|
throw new Error(
|
|
6134
|
-
"Use cmdr setup --agent claude-code|codex|zcode. Other MCP hosts can use cmdr config --agent HOST."
|
|
6264
|
+
"Use cmdr setup --agent claude-code|codex|zcode|kimi-code. Other MCP hosts can use cmdr config --agent HOST."
|
|
6135
6265
|
);
|
|
6136
6266
|
const host = agent;
|
|
6137
6267
|
const source = await inspectInstallation(sourceRoot);
|
|
@@ -6141,17 +6271,28 @@ async function runSetup(argv, sourceRoot) {
|
|
|
6141
6271
|
const digest = createHash3("sha256").update(readFileSync4(join6(sourceRoot, "dist/integrity.json"))).digest("hex").slice(0, 16);
|
|
6142
6272
|
const runtime = join6(state, "runtimes", `${source.version}-${digest}`);
|
|
6143
6273
|
const bin = join6(state, "bin");
|
|
6144
|
-
const hostRoot = values["config-dir"] ? resolve2(values["config-dir"]) : host === "claude" ? resolve2(process.env.CLAUDE_CONFIG_DIR || join6(homedir2(), ".claude")) : host === "codex" ? resolve2(process.env.CODEX_HOME || join6(homedir2(), ".codex")) : join6(homedir2(), ".zcode");
|
|
6274
|
+
const hostRoot = values["config-dir"] ? resolve2(values["config-dir"]) : host === "claude" ? resolve2(process.env.CLAUDE_CONFIG_DIR || join6(homedir2(), ".claude")) : host === "codex" ? resolve2(process.env.CODEX_HOME || join6(homedir2(), ".codex")) : host === "kimi" ? resolve2(process.env.KIMI_CODE_HOME || join6(homedir2(), ".kimi-code")) : join6(homedir2(), ".zcode");
|
|
6145
6275
|
const profile = createHash3("sha256").update(hostRoot).digest("hex").slice(0, 12);
|
|
6146
6276
|
const mcp = join6(bin, `cmdr-mcp-${host}-${profile}`), hook = join6(bin, `cmdr-hook-${host}-${profile}`);
|
|
6147
6277
|
const changes = [];
|
|
6148
6278
|
const warnings = [];
|
|
6279
|
+
if (host === "kimi" && existsSync2(join6(hostRoot, "plugins/managed/cmdr")))
|
|
6280
|
+
throw new Error(
|
|
6281
|
+
"The cmdr plugin is already enabled. Use its installation, or remove it before running standalone setup to avoid duplicate tools and hooks."
|
|
6282
|
+
);
|
|
6149
6283
|
const add = (change) => {
|
|
6150
6284
|
if (change) changes.push(change);
|
|
6151
6285
|
};
|
|
6152
6286
|
const writeConfig = (path, config2) => add(fileChange(path, JSON.stringify(config2, null, 2) + "\n"));
|
|
6153
6287
|
let config;
|
|
6154
|
-
if (host === "
|
|
6288
|
+
if (host === "kimi") {
|
|
6289
|
+
config = configPath(join6(hostRoot, "mcp.json"));
|
|
6290
|
+
const servers = jsonConfig(read(config), config);
|
|
6291
|
+
mergeJsonServer(servers, mcp, "kimi");
|
|
6292
|
+
writeConfig(config, servers);
|
|
6293
|
+
const hooksPath = configPath(join6(hostRoot, "config.toml"));
|
|
6294
|
+
add(fileChange(hooksPath, kimiHooksConfig(read(hooksPath), hook)));
|
|
6295
|
+
} else if (host === "codex") {
|
|
6155
6296
|
config = configPath(join6(hostRoot, "config.toml"));
|
|
6156
6297
|
const merged = codexConfig(read(config), mcp);
|
|
6157
6298
|
add(fileChange(config, merged.text));
|
|
@@ -6201,6 +6342,8 @@ async function runSetup(argv, sourceRoot) {
|
|
|
6201
6342
|
}
|
|
6202
6343
|
const skill = join6(hostRoot, "skills/cmdr");
|
|
6203
6344
|
add(skillChange(skill, join6(runtime, "skills/cmdr")));
|
|
6345
|
+
if (host === "kimi")
|
|
6346
|
+
add(skillChange(join6(hostRoot, "skills/cmdr-identity"), join6(runtime, "kimi-identity")));
|
|
6204
6347
|
const result = {
|
|
6205
6348
|
agent: values.agent,
|
|
6206
6349
|
version: source.version,
|
|
@@ -6211,7 +6354,7 @@ async function runSetup(argv, sourceRoot) {
|
|
|
6211
6354
|
dry_run: !!values["dry-run"],
|
|
6212
6355
|
changed: changes.map((change) => change.path),
|
|
6213
6356
|
warnings,
|
|
6214
|
-
restart: "Start a new host session and review any hook trust prompts."
|
|
6357
|
+
restart: host === "kimi" ? "Restart the Kimi Code app: it loads config.toml hooks only at startup." : "Start a new host session and review any hook trust prompts."
|
|
6215
6358
|
};
|
|
6216
6359
|
const output = (extra) => {
|
|
6217
6360
|
if (values.json) console.log(JSON.stringify({ ...result, ...extra }, null, 2));
|
|
@@ -6314,6 +6457,7 @@ if (process.argv[2] === "setup") {
|
|
|
6314
6457
|
follow: { type: "boolean" },
|
|
6315
6458
|
full: { type: "boolean" },
|
|
6316
6459
|
json: { type: "boolean" },
|
|
6460
|
+
"no-open": { type: "boolean" },
|
|
6317
6461
|
help: { type: "boolean" }
|
|
6318
6462
|
}
|
|
6319
6463
|
});
|
|
@@ -6399,14 +6543,52 @@ if (process.argv[2] === "setup") {
|
|
|
6399
6543
|
} catch {
|
|
6400
6544
|
checks.zcode_user_hooks = "No user hook config; installed plugin hooks follow plugin enablement.";
|
|
6401
6545
|
}
|
|
6546
|
+
const kimiRoot = "/Applications/Kimi Code.app/Contents";
|
|
6547
|
+
if (existsSync3(join7(kimiRoot, "Info.plist"))) {
|
|
6548
|
+
try {
|
|
6549
|
+
checks.kimi = {
|
|
6550
|
+
app_version: execFileSync(
|
|
6551
|
+
"/usr/libexec/PlistBuddy",
|
|
6552
|
+
["-c", "Print :CFBundleShortVersionString", join7(kimiRoot, "Info.plist")],
|
|
6553
|
+
{ encoding: "utf8", timeout: 1e3 }
|
|
6554
|
+
).trim(),
|
|
6555
|
+
config_dir: process.env.KIMI_CODE_HOME || join7(homedir3(), ".kimi-code")
|
|
6556
|
+
};
|
|
6557
|
+
} catch {
|
|
6558
|
+
checks.kimi = "Installed";
|
|
6559
|
+
}
|
|
6560
|
+
} else
|
|
6561
|
+
checks.kimi = "Desktop app not found in /Applications; generic MCP configuration is available.";
|
|
6562
|
+
try {
|
|
6563
|
+
const kconfig = readFileSync5(
|
|
6564
|
+
join7(process.env.KIMI_CODE_HOME || join7(homedir3(), ".kimi-code"), "config.toml"),
|
|
6565
|
+
"utf8"
|
|
6566
|
+
);
|
|
6567
|
+
checks.kimi_hooks = /cmdr-hook/.test(kconfig) ? "cmdr hooks present in config.toml" : "No cmdr hooks in config.toml; run cmdr setup --agent kimi-code.";
|
|
6568
|
+
} catch {
|
|
6569
|
+
checks.kimi_hooks = "Kimi Code configuration unavailable";
|
|
6570
|
+
}
|
|
6402
6571
|
print(checks);
|
|
6403
6572
|
}
|
|
6404
6573
|
try {
|
|
6405
6574
|
if (v.help)
|
|
6406
6575
|
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"
|
|
6576
|
+
"cmdr status | dashboard [--no-open] [--json] | setup --agent claude-code|codex|zcode|kimi-code [--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|kimi|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
6577
|
);
|
|
6409
|
-
else if (cmd === "
|
|
6578
|
+
else if (cmd === "dashboard") {
|
|
6579
|
+
const result = await call("admin.dashboard", {}, true);
|
|
6580
|
+
print(result);
|
|
6581
|
+
if (!v["no-open"]) {
|
|
6582
|
+
try {
|
|
6583
|
+
execFileSync(process.platform === "darwin" ? "open" : "xdg-open", [result.url], {
|
|
6584
|
+
stdio: "ignore",
|
|
6585
|
+
timeout: 5e3
|
|
6586
|
+
});
|
|
6587
|
+
} catch {
|
|
6588
|
+
console.error("Open the printed URL in a local browser (valid for 60 seconds).");
|
|
6589
|
+
}
|
|
6590
|
+
}
|
|
6591
|
+
} else if (cmd === "config") {
|
|
6410
6592
|
const agent = v.agent || "generic";
|
|
6411
6593
|
if (!/^[a-z][a-z0-9_-]{0,63}$/.test(agent))
|
|
6412
6594
|
throw new Error("Invalid --agent: use lowercase letters, digits, underscores or hyphens.");
|
|
@@ -6416,9 +6598,10 @@ if (process.argv[2] === "setup") {
|
|
|
6416
6598
|
env: {
|
|
6417
6599
|
CMDR_AGENT: agent,
|
|
6418
6600
|
...v.session ? { CMDR_SESSION_ID: v.session } : {},
|
|
6419
|
-
...agent === "zcode" ? { CMDR_TOOL_TIMEOUT_SEC: "600" } : {}
|
|
6601
|
+
...agent === "zcode" || agent === "kimi" ? { CMDR_TOOL_TIMEOUT_SEC: "600" } : {}
|
|
6420
6602
|
},
|
|
6421
|
-
...agent === "zcode" ? { timeoutMs: 6e5 } : {}
|
|
6603
|
+
...agent === "zcode" ? { timeoutMs: 6e5 } : {},
|
|
6604
|
+
...agent === "kimi" ? { toolTimeoutMs: 6e5 } : {}
|
|
6422
6605
|
};
|
|
6423
6606
|
print(
|
|
6424
6607
|
agent === "zcode" ? { mcp: { servers: { cmdr: server } } } : { mcpServers: { cmdr: server } }
|