kojee-mcp 0.7.2 → 0.7.3

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.
@@ -118,13 +118,13 @@ function scrubPlaceholderWebhookEnv(envKeys) {
118
118
  function writeCodexConfig(inputs) {
119
119
  const configPath = inputs.configPath ?? defaultCodexConfigPath();
120
120
  const hooksPath = inputs.hooksPath ?? defaultCodexHooksPath();
121
- let toml = "";
121
+ let existingToml = "";
122
122
  try {
123
- toml = fs.readFileSync(configPath, "utf8");
123
+ existingToml = fs.readFileSync(configPath, "utf8");
124
124
  } catch {
125
125
  }
126
- toml = upsertKojeeTomlTables(
127
- toml,
126
+ const newToml = upsertKojeeTomlTables(
127
+ existingToml,
128
128
  inputs.webhookUrl,
129
129
  inputs.webhookSecret,
130
130
  inputs.signatureEnv ?? [],
@@ -132,12 +132,21 @@ function writeCodexConfig(inputs) {
132
132
  inputs.url,
133
133
  inputs.pairedConfigPath
134
134
  );
135
- writeFile600(configPath, toml);
135
+ const tomlChanged = newToml !== existingToml;
136
+ if (tomlChanged) writeFile600(configPath, newToml);
136
137
  const hooks = readJson(hooksPath);
137
138
  hooks.hooks ??= {};
138
139
  upsertKojeeHookEntry(hooks, "Stop", codexStopHookCommand());
139
140
  upsertKojeeHookEntry(hooks, "UserPromptSubmit", codexPromptSubmitHookCommand());
140
- writeFile600(hooksPath, JSON.stringify(hooks, null, 2));
141
+ const newHooks = JSON.stringify(hooks, null, 2);
142
+ let existingHooksRaw = "";
143
+ try {
144
+ existingHooksRaw = fs.readFileSync(hooksPath, "utf8");
145
+ } catch {
146
+ }
147
+ const hooksChanged = newHooks !== existingHooksRaw;
148
+ if (hooksChanged) writeFile600(hooksPath, newHooks);
149
+ return { tomlChanged, hooksChanged };
141
150
  }
142
151
  function upsertKojeeHookEntry(hooks, event, command) {
143
152
  hooks.hooks ??= {};
@@ -6,7 +6,7 @@ import {
6
6
  buildCondensedTandemRules,
7
7
  buildMonitorSpawn,
8
8
  buildReplyRecipe
9
- } from "./chunk-XFGGMDZ4.js";
9
+ } from "./chunk-FJUAMJHU.js";
10
10
  import {
11
11
  translateToolCallResult
12
12
  } from "./chunk-PPTKGWFF.js";
@@ -40,6 +40,45 @@ function buildChannelInstructions(_tandemMembershipCount, eventLogPath) {
40
40
  function tandemIdArg(args) {
41
41
  return typeof args["tandem_id"] === "string" ? args["tandem_id"] : null;
42
42
  }
43
+ function parseJoinDisplayName(content) {
44
+ for (const item of content) {
45
+ if (item.type !== "text" || typeof item.text !== "string") continue;
46
+ try {
47
+ const parsed = JSON.parse(item.text);
48
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
49
+ const name = parsed["display_name"];
50
+ if (typeof name === "string" && name.length > 0) return name;
51
+ }
52
+ } catch {
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ function parseJoinStatus(content) {
58
+ for (const item of content) {
59
+ if (item.type !== "text" || typeof item.text !== "string") continue;
60
+ try {
61
+ const parsed = JSON.parse(item.text);
62
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
63
+ const status = parsed["status"];
64
+ if (typeof status === "string") return status;
65
+ }
66
+ } catch {
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ var SKIP_ADVISORY_STATUSES = /* @__PURE__ */ new Set(["pending_approval", "needs_invite"]);
72
+ function buildUnnamedJoinAdvisory(args, content) {
73
+ const seatName = args["seat_name"];
74
+ if (typeof seatName === "string" && seatName.trim().length > 0) return null;
75
+ const status = parseJoinStatus(content);
76
+ if (status !== null && SKIP_ADVISORY_STATUSES.has(status)) return null;
77
+ const displayName = parseJoinDisplayName(content);
78
+ const handleClause = displayName !== null ? `the room minted a generated handle ('${displayName}')` : "the room minted a generated handle";
79
+ const text = `[kojee] You joined WITHOUT seat_name \u2014 ${handleClause}. If you previously held a name in this room, re-join now with tandem_join(tandem_id, seat_name=<your name>) to rebind your seat; mentions to your old name will NOT reach this seat until you do.`;
80
+ return { type: "text", text };
81
+ }
43
82
  var DRAIN_TOOLS = /* @__PURE__ */ new Set(["tandem_messages", "tandem_ack"]);
44
83
  function extractDrainCursor(name, args, content) {
45
84
  const candidates = [];
@@ -74,13 +113,21 @@ function extractDrainCursor(name, args, content) {
74
113
  }
75
114
  async function executeToolCall(registry, name, args, hooks) {
76
115
  const rawResult = await registry.callTool(name, args);
77
- const result = translateToolCallResult(rawResult);
116
+ let result = translateToolCallResult(rawResult);
78
117
  if (!result.isError && name === "tandem_join") {
79
118
  try {
80
119
  hooks?.onTandemJoin?.(tandemIdArg(args));
81
120
  } catch (err) {
82
121
  console.error("[mcp] onTandemJoin hook failed:", err?.message ?? String(err));
83
122
  }
123
+ try {
124
+ const advisory = buildUnnamedJoinAdvisory(args, result.content);
125
+ if (advisory !== null) {
126
+ result = { ...result, content: [...result.content, advisory] };
127
+ }
128
+ } catch (err) {
129
+ console.error("[mcp] unnamed-join advisory decoration failed:", err?.message ?? String(err));
130
+ }
84
131
  }
85
132
  if (!result.isError && name === "tandem_leave") {
86
133
  try {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  claudeCodeAdapter
3
- } from "./chunk-E35VWFZV.js";
3
+ } from "./chunk-TCWIXG5C.js";
4
4
  import {
5
5
  GatewayClient,
6
6
  applyStableSessionId
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  createMcpServer,
13
13
  startMcpServer
14
- } from "./chunk-ZUIYFRO5.js";
14
+ } from "./chunk-A4IOKD4Z.js";
15
15
  import {
16
16
  findClaudeAncestorPid
17
17
  } from "./chunk-XJEBJIQE.js";
@@ -295,7 +295,7 @@ async function startProxy(config) {
295
295
  }
296
296
  console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
297
297
  let server;
298
- const { selectDelivery } = await import("./registry-42Y45L6I.js");
298
+ const { selectDelivery } = await import("./registry-2QS42EQF.js");
299
299
  const delivery = selectDelivery(adapter.runtime, {
300
300
  supportsChannels: adapter.supportsChannels,
301
301
  // Per-window delivered mirror for the tandem_pending tool (codex only —
@@ -16,7 +16,7 @@ function buildCatchUpNote() {
16
16
  return "Cursors are allocated per-Tandem, and the event-log interleaves every subscribed room into one stream \u2014 so track the last `cursor=<n>` you saw keyed by the line's `tandem=<id>` field, and only treat a jump WITHIN the same tandem_id as a gap (a jump between lines from different rooms is normal interleaving, not a missed message). If the cursor jumps within one tandem, or the log resets (the proxy caps and truncates the messages log in place on overflow \u2014 a `status=rotated` line is recorded in the status sibling), you may have missed messages: catch up with tandem_messages(tandem_id, since=<last cursor you saw for THAT tandem>).";
17
17
  }
18
18
  function buildCondensedTandemRules() {
19
- return "Tandem ground rules: (1) catch up before you speak \u2014 fetch messages since your last cursor (cursors are per-Tandem); never answer from a stale view. (2) close every loop \u2014 taking / done (with evidence) / blocked, said out loud; silence is a bug on a team. (3) your seat_name IS your room identity \u2014 keep it stable across sessions and reconnects. (4) plan your wake path per task and set your wake filter deliberately (heads-down \u2192 mentions; waiting on one reply \u2192 listen; co-working \u2192 all), and tell the room your posture WHEN IT CHANGES \u2014 skip the check-in ceremony when you are merely re-joining/reconnecting with the same posture.";
19
+ return "Tandem ground rules: (1) catch up before you speak \u2014 fetch messages since your last cursor (cursors are per-Tandem); never answer from a stale view. (2) close every loop \u2014 taking / done (with evidence) / blocked, said out loud; silence is a bug on a team. (3) your seat_name IS your room identity \u2014 keep it stable across sessions and reconnects. (4) plan your wake path per task and set your wake filter deliberately (heads-down \u2192 mentions; waiting on one reply \u2192 listen; co-working \u2192 all), and tell the room your posture WHEN IT CHANGES \u2014 skip the check-in ceremony when you are merely re-joining/reconnecting with the same posture. (5) send all replies and check-ins as kind=message \u2014 never kind=status; status is lifecycle-only (joined/left, system-generated).";
20
20
  }
21
21
  function buildMonitorNudge(logPath) {
22
22
  return `[kojee] Tandem events are being logged but no Monitor is reading them \u2014 you may be missing wake notifications. Spawn the watcher once: ${buildMonitorSpawn(logPath)}. Then ${buildReplyRecipe()}.`;
@@ -29,7 +29,7 @@ function buildWebhookReceiverNote(sig) {
29
29
  return "Webhook sink (optional, OFF unless KOJEE_WEBHOOK_URL + KOJEE_WEBHOOK_SECRET are set): the proxy POSTs every Tandem event as JSON to your endpoint. The body is the canonical normalized TandemEvent \u2014 " + WEBHOOK_BODY_SHAPE + ` \u2014 where from.session_id and severity are present only when the wire carried them (the body is fully normalized: it carries from.principal, never the raw backend sender envelope). To build a receiver: (1) verify the ${header} header \u2014 it is ${digestDesc} of the RAW request body bytes keyed by your KOJEE_WEBHOOK_SECRET; recompute over the received bytes and timing-safe compare, reject mismatches. (2) Dedupe by message_id \u2014 the body's \`id\`, also in the X-Kojee-Delivery header: delivery is AT-LEAST-ONCE (the proxy replays backlog from the cursor on restart), so the same event may arrive more than once \u2014 there is no exactly-once promise.`;
30
30
  }
31
31
  var CODEX_LISTEN_CAP_MS = 8e3;
32
- var CODEX_WAKE_BELL = "[kojee] Tandem events may be pending. Call tandem_pending now and drain each room it lists (tandem_messages(id, since=cursor)), then reply in the room. If it returns none for you, ignore this.";
32
+ var CODEX_WAKE_BELL = "[kojee] Tandem events may be pending. Call tandem_pending now and drain each room it lists (tandem_messages(id, since=cursor)), then reply in the room with a normal message (kind=message \u2014 never status). If it returns none for you, ignore this.";
33
33
 
34
34
  export {
35
35
  buildMonitorSpawn,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildReplyRecipe
3
- } from "./chunk-XFGGMDZ4.js";
3
+ } from "./chunk-FJUAMJHU.js";
4
4
 
5
5
  // src/adapters/claude-code.ts
6
6
  function computeSeverity(event) {
@@ -5,7 +5,7 @@ import {
5
5
  isPlaceholderWebhookUrl,
6
6
  removeCodexConfig,
7
7
  writeCodexConfig
8
- } from "./chunk-RVLUZLXD.js";
8
+ } from "./chunk-6XWTUDWW.js";
9
9
  import {
10
10
  removeOpenclawMcpServer,
11
11
  writeOpenclawMcpConfig
@@ -49,7 +49,7 @@ import {
49
49
  CODEX_LISTEN_CAP_MS,
50
50
  buildCondensedTandemRules,
51
51
  buildWebhookReceiverNote
52
- } from "./chunk-XFGGMDZ4.js";
52
+ } from "./chunk-FJUAMJHU.js";
53
53
 
54
54
  // src/wizard/pair-slot.ts
55
55
  import fs from "fs";
@@ -790,7 +790,7 @@ function whatHappensNext(runtime, opts) {
790
790
  return lines.join("\n");
791
791
  }
792
792
  async function configureClaudeCode(opts) {
793
- const { runInit } = await import("./install-GGI6GU6C.js");
793
+ const { runInit } = await import("./install-JQNDGAAQ.js");
794
794
  const report = runInit({
795
795
  ...opts.configPath ? { configPath: opts.configPath } : {},
796
796
  ...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
@@ -854,7 +854,7 @@ function configureCodex(opts) {
854
854
  const secret = connectPairedMode || !hasRealUrl ? void 0 : wh.secret || generateWebhookSecret();
855
855
  const tokenArgs = opts.token && opts.url ? { token: opts.token, url: opts.url } : {};
856
856
  const pairedArgs = opts.pairedConfigPath ? { pairedConfigPath: opts.pairedConfigPath } : {};
857
- writeCodexConfig({
857
+ const writeResult = writeCodexConfig({
858
858
  ...opts.configPath ? { configPath: opts.configPath } : {},
859
859
  ...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
860
860
  ...url !== void 0 ? { webhookUrl: url } : {},
@@ -890,10 +890,12 @@ function configureCodex(opts) {
890
890
  lines.push(indent(buildCodexStopHookBlock()));
891
891
  lines.push(indent(buildCodexPromptSubmitHookBlock()));
892
892
  lines.push("");
893
- lines.push(
894
- "IMPORTANT: Codex will re-prompt to trust hooks on its next launch \u2014 approve it, or wakes silently stop."
895
- );
896
- lines.push("");
893
+ if (writeResult.hooksChanged) {
894
+ lines.push(
895
+ "IMPORTANT: Codex will re-prompt to trust hooks on its next launch \u2014 approve it, or wakes silently stop."
896
+ );
897
+ lines.push("");
898
+ }
897
899
  lines.push(
898
900
  connectPairedMode ? "webhook: (unchanged \u2014 `connect` rewrites only the credential args, not the wake path)" : `webhook: ${wh.url ? wh.redactedSummary : "OPTIONAL \u2014 not configured (wake is self-contained; set KOJEE_WEBHOOK_URL only for an extra low-latency push path)"}`
899
901
  );
@@ -1033,7 +1035,7 @@ async function runWizardUninstall(runtime, opts) {
1033
1035
  const effective = opts.runtime !== void 0 ? runtime : readRecordedRuntime() ?? runtime;
1034
1036
  const lines = [`Uninstalling runtime: ${effective}`];
1035
1037
  if (effective === "claude-code") {
1036
- const { runUninstall } = await import("./install-GGI6GU6C.js");
1038
+ const { runUninstall } = await import("./install-JQNDGAAQ.js");
1037
1039
  const report = runUninstall({
1038
1040
  ...opts.configPath ? { configPath: opts.configPath } : {},
1039
1041
  ...opts.hooksPath ? { hooksPath: opts.hooksPath } : {}
package/dist/cli.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-EIAUW6KO.js";
5
5
  import {
6
6
  startProxy
7
- } from "./chunk-R5GC2GRD.js";
8
- import "./chunk-E35VWFZV.js";
7
+ } from "./chunk-ABECLEYE.js";
8
+ import "./chunk-TCWIXG5C.js";
9
9
  import {
10
10
  pairedConfigPath
11
11
  } from "./chunk-5SZHXYPK.js";
@@ -18,11 +18,11 @@ import {
18
18
  deriveKeystorePath
19
19
  } from "./chunk-6G6YYST6.js";
20
20
  import "./chunk-U5HHHRXA.js";
21
- import "./chunk-ZUIYFRO5.js";
21
+ import "./chunk-A4IOKD4Z.js";
22
22
  import {
23
23
  VERSION
24
24
  } from "./chunk-5DHIUN73.js";
25
- import "./chunk-XFGGMDZ4.js";
25
+ import "./chunk-FJUAMJHU.js";
26
26
  import "./chunk-PPTKGWFF.js";
27
27
  import "./chunk-XJEBJIQE.js";
28
28
  import "./chunk-KNEJTD6G.js";
@@ -48,7 +48,7 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
48
48
  program.command("connect <code>").description(
49
49
  "Connect this runtime to Kojee with a per-agent pair code from the dashboard (claude-code | codex | openclaw | hermes). claude-code/codex/openclaw write a per-runtime paired slot (~/.kojee/agents/<runtime>/config.json) and point the runtime launcher at it via --paired-config; hermes writes the global ~/.kojee/config.json (its daemon + `kojee-mcp send` read that). Idempotent."
50
50
  ).requiredOption("--runtime <id>", "Target runtime: claude-code | codex | openclaw | hermes").option("--url <url>", "Broker base URL (default: the canonical staging broker)").action(async (code, opts) => {
51
- const { runConnect } = await import("./connect-handler-2JFIMQY6.js");
51
+ const { runConnect } = await import("./connect-handler-NZINEMG3.js");
52
52
  const result = await runConnect({
53
53
  code,
54
54
  runtime: opts.runtime,
@@ -63,7 +63,7 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
63
63
  "Hook type: stop, user-prompt-submit, codex-stop, or codex-prompt-submit"
64
64
  ).action(async (opts) => {
65
65
  if (opts.type === "stop") {
66
- const { runStopHook } = await import("./stop-hook-PWTAP227.js");
66
+ const { runStopHook } = await import("./stop-hook-N6TX4YQT.js");
67
67
  await runStopHook();
68
68
  process.exit(0);
69
69
  } else if (opts.type === "user-prompt-submit") {
@@ -71,11 +71,11 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
71
71
  await runUserPromptSubmitHook();
72
72
  process.exit(0);
73
73
  } else if (opts.type === "codex-stop") {
74
- const { runCodexStopHook } = await import("./codex-stop-hook-PNWYNQKM.js");
74
+ const { runCodexStopHook } = await import("./codex-stop-hook-32W4BIOM.js");
75
75
  await runCodexStopHook();
76
76
  process.exit(0);
77
77
  } else if (opts.type === "codex-prompt-submit") {
78
- const { runCodexPromptSubmitHook } = await import("./codex-prompt-submit-hook-FOBPGZHJ.js");
78
+ const { runCodexPromptSubmitHook } = await import("./codex-prompt-submit-hook-J5PZEJSK.js");
79
79
  await runCodexPromptSubmitHook();
80
80
  process.exit(0);
81
81
  } else {
@@ -86,7 +86,7 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
86
86
  }
87
87
  });
88
88
  program.command("install-hooks").description("Install kojee Stop + UserPromptSubmit hooks in ~/.claude/settings.json (idempotent)").option("--hooks-path <path>", "Override default ~/.claude/settings.json").option("--uninstall", "Remove kojee hook entries instead of installing them").action(async (opts) => {
89
- const { installHooks, uninstallHooks } = await import("./install-GGI6GU6C.js");
89
+ const { installHooks, uninstallHooks } = await import("./install-JQNDGAAQ.js");
90
90
  if (opts.uninstall) {
91
91
  const removed = uninstallHooks({ hooksPath: opts.hooksPath });
92
92
  console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
@@ -124,7 +124,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
124
124
  }
125
125
  });
126
126
  program.command("doctor").description("Diagnose the kojee wake path (proxy, hook-server, SSE stream, event log, Monitor) and print the exact wake recipe").action(async () => {
127
- const { runDoctor } = await import("./doctor-HXMCO5PR.js");
127
+ const { runDoctor } = await import("./doctor-WUU5BVPT.js");
128
128
  const code = await runDoctor();
129
129
  process.exit(code);
130
130
  });
@@ -143,7 +143,7 @@ function addInstallOptions(cmd, runtimeHelp) {
143
143
  function makeInstallAction(verb) {
144
144
  return async (opts) => {
145
145
  const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
146
- const { runSetup, resolvePairCode } = await import("./setup-handler-GBPXDETR.js");
146
+ const { runSetup, resolvePairCode } = await import("./setup-handler-44ASXYMS.js");
147
147
  const pairCode = resolvePairCode(opts);
148
148
  const result = await runSetup({
149
149
  verb,
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-IZN7IZPW.js";
7
7
  import {
8
8
  CODEX_WAKE_BELL
9
- } from "./chunk-XFGGMDZ4.js";
9
+ } from "./chunk-FJUAMJHU.js";
10
10
 
11
11
  // src/hooks/codex-prompt-submit-hook.ts
12
12
  function buildCodexPromptSubmitOutput() {
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-IZN7IZPW.js";
9
9
  import {
10
10
  CODEX_WAKE_BELL
11
- } from "./chunk-XFGGMDZ4.js";
11
+ } from "./chunk-FJUAMJHU.js";
12
12
 
13
13
  // src/hooks/codex-stop-hook.ts
14
14
  import fs from "fs";
@@ -2,8 +2,8 @@ import {
2
2
  DEFAULT_BROKER_URL,
3
3
  reconcileConnectPairSlot,
4
4
  runWizard
5
- } from "./chunk-EDHG4375.js";
6
- import "./chunk-RVLUZLXD.js";
5
+ } from "./chunk-XPIW4N55.js";
6
+ import "./chunk-6XWTUDWW.js";
7
7
  import "./chunk-E6WMFMM2.js";
8
8
  import "./chunk-77HWBSRH.js";
9
9
  import "./chunk-TMCNB4JH.js";
@@ -30,7 +30,7 @@ import {
30
30
  } from "./chunk-6G6YYST6.js";
31
31
  import "./chunk-U5HHHRXA.js";
32
32
  import "./chunk-5DHIUN73.js";
33
- import "./chunk-XFGGMDZ4.js";
33
+ import "./chunk-FJUAMJHU.js";
34
34
 
35
35
  // src/wizard/connect-handler.ts
36
36
  import os from "os";
@@ -18,7 +18,7 @@ import "./chunk-U5HHHRXA.js";
18
18
  import {
19
19
  buildMonitorSpawn,
20
20
  buildReplyRecipe
21
- } from "./chunk-XFGGMDZ4.js";
21
+ } from "./chunk-FJUAMJHU.js";
22
22
  import {
23
23
  deriveDiscoveryKey,
24
24
  findClaudeAncestorPid
@@ -354,7 +354,7 @@ function formatDoctorReport(report) {
354
354
  async function runDoctor() {
355
355
  const { readRecordedRuntime } = await import("./runtime-record-OXRLTOLC.js");
356
356
  if (readRecordedRuntime() === "codex") {
357
- const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-ZSALZPH3.js");
357
+ const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-PJFWIABF.js");
358
358
  const report2 = collectCodexDoctorReport();
359
359
  console.error(formatCodexDoctorReport(report2));
360
360
  return report2.verdict === "broken" ? 1 : 0;
@@ -2,7 +2,7 @@ import {
2
2
  defaultCodexConfigPath,
3
3
  defaultCodexHooksPath,
4
4
  isPlaceholderWebhookUrl
5
- } from "./chunk-RVLUZLXD.js";
5
+ } from "./chunk-6XWTUDWW.js";
6
6
  import "./chunk-TMCNB4JH.js";
7
7
  import "./chunk-D6JKFJ6A.js";
8
8
  import {
@@ -20,7 +20,7 @@ import {
20
20
  } from "./chunk-5DHIUN73.js";
21
21
  import {
22
22
  CODEX_LISTEN_CAP_MS
23
- } from "./chunk-XFGGMDZ4.js";
23
+ } from "./chunk-FJUAMJHU.js";
24
24
 
25
25
  // src/doctor-codex.ts
26
26
  import fs from "fs";
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  listTandemIds,
3
3
  startProxy
4
- } from "./chunk-R5GC2GRD.js";
5
- import "./chunk-E35VWFZV.js";
4
+ } from "./chunk-ABECLEYE.js";
5
+ import "./chunk-TCWIXG5C.js";
6
6
  import "./chunk-247WFMCJ.js";
7
7
  import "./chunk-Z5LPNJQ6.js";
8
8
  import "./chunk-I67C2HYA.js";
9
9
  import "./chunk-MIEI4PLB.js";
10
10
  import "./chunk-6G6YYST6.js";
11
11
  import "./chunk-U5HHHRXA.js";
12
- import "./chunk-ZUIYFRO5.js";
12
+ import "./chunk-A4IOKD4Z.js";
13
13
  import "./chunk-5DHIUN73.js";
14
- import "./chunk-XFGGMDZ4.js";
14
+ import "./chunk-FJUAMJHU.js";
15
15
  import "./chunk-PPTKGWFF.js";
16
16
  import "./chunk-XJEBJIQE.js";
17
17
  import "./chunk-KNEJTD6G.js";
@@ -93,7 +93,19 @@ function installHooks(opts = {}) {
93
93
  const cfg = readConfig(p);
94
94
  const stop = installHookEntry(cfg, "Stop", buildHookCommand("stop"));
95
95
  const ups = installHookEntry(cfg, "UserPromptSubmit", buildHookCommand("user-prompt-submit"));
96
- writeConfig(p, cfg);
96
+ if (stop !== "already-installed" || ups !== "already-installed") {
97
+ writeConfig(p, cfg);
98
+ } else {
99
+ const newContent = JSON.stringify(cfg, null, 2);
100
+ let onDisk = "";
101
+ try {
102
+ onDisk = fs.readFileSync(p, "utf8");
103
+ } catch {
104
+ }
105
+ if (newContent !== onDisk) {
106
+ writeConfig(p, cfg);
107
+ }
108
+ }
97
109
  return { stop, ups };
98
110
  }
99
111
  function uninstallHooks(opts = {}) {
@@ -13,12 +13,12 @@ import {
13
13
  } from "./chunk-IZN7IZPW.js";
14
14
  import {
15
15
  claudeCodeAdapter
16
- } from "./chunk-E35VWFZV.js";
17
- import "./chunk-ZUIYFRO5.js";
16
+ } from "./chunk-TCWIXG5C.js";
17
+ import "./chunk-A4IOKD4Z.js";
18
18
  import {
19
19
  VERSION
20
20
  } from "./chunk-5DHIUN73.js";
21
- import "./chunk-XFGGMDZ4.js";
21
+ import "./chunk-FJUAMJHU.js";
22
22
  import "./chunk-PPTKGWFF.js";
23
23
 
24
24
  // src/delivery/lib/fanout.ts
@@ -127,7 +127,7 @@ function createClaudeCodeDelivery() {
127
127
  const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
128
128
  const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
129
129
  const { startEventStream } = await import("./event-stream-WPN3EN7C.js");
130
- const { createMcpServer } = await import("./server-JWIH7OFA.js");
130
+ const { createMcpServer } = await import("./server-DU3LFS32.js");
131
131
  const { deriveDiscoveryKey } = await import("./ancestry-A2F5KQ6A.js");
132
132
  const { resolveSharedSessionId } = await import("./cc-session-id-RURNIHHC.js");
133
133
  sweepStaleDiscovery();
@@ -324,7 +324,7 @@ function createWebhookDelivery(name, pendingLedger) {
324
324
  const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
325
325
  const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
326
326
  const { startEventStream } = await import("./event-stream-WPN3EN7C.js");
327
- const { createMcpServer } = await import("./server-JWIH7OFA.js");
327
+ const { createMcpServer } = await import("./server-DU3LFS32.js");
328
328
  sweepStaleEventLogs();
329
329
  eventLog = startEventLog({
330
330
  key: ctx.instanceKey,
@@ -518,7 +518,7 @@ function createOpenclawDelivery(deps = {}) {
518
518
  const { startEventLog, sweepStaleEventLogs } = await import("./event-log-2NBJEIEP.js");
519
519
  const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
520
520
  const { startEventStream } = await import("./event-stream-WPN3EN7C.js");
521
- const { createMcpServer } = await import("./server-JWIH7OFA.js");
521
+ const { createMcpServer } = await import("./server-DU3LFS32.js");
522
522
  sweepStaleEventLogs();
523
523
  eventLog = startEventLog({
524
524
  key: ctx.instanceKey,
@@ -5,9 +5,9 @@ import {
5
5
  executeToolCall,
6
6
  extractDrainCursor,
7
7
  startMcpServer
8
- } from "./chunk-ZUIYFRO5.js";
8
+ } from "./chunk-A4IOKD4Z.js";
9
9
  import "./chunk-5DHIUN73.js";
10
- import "./chunk-XFGGMDZ4.js";
10
+ import "./chunk-FJUAMJHU.js";
11
11
  import "./chunk-PPTKGWFF.js";
12
12
  export {
13
13
  buildChannelInstructions,
@@ -3,8 +3,8 @@ import {
3
3
  pairSlotFor,
4
4
  reconcileConnectPairSlot,
5
5
  runWizard
6
- } from "./chunk-EDHG4375.js";
7
- import "./chunk-RVLUZLXD.js";
6
+ } from "./chunk-XPIW4N55.js";
7
+ import "./chunk-6XWTUDWW.js";
8
8
  import "./chunk-E6WMFMM2.js";
9
9
  import {
10
10
  isWizardRuntime
@@ -26,7 +26,7 @@ import "./chunk-MIEI4PLB.js";
26
26
  import "./chunk-6G6YYST6.js";
27
27
  import "./chunk-U5HHHRXA.js";
28
28
  import "./chunk-5DHIUN73.js";
29
- import "./chunk-XFGGMDZ4.js";
29
+ import "./chunk-FJUAMJHU.js";
30
30
 
31
31
  // src/wizard/setup-handler.ts
32
32
  var SETUP_SUPPORTED_RUNTIMES = ["claude-code", "codex"];
@@ -19,7 +19,7 @@ import "./chunk-67F67AQ6.js";
19
19
  import "./chunk-U5HHHRXA.js";
20
20
  import {
21
21
  buildMonitorNudge
22
- } from "./chunk-XFGGMDZ4.js";
22
+ } from "./chunk-FJUAMJHU.js";
23
23
  import "./chunk-XJEBJIQE.js";
24
24
  import "./chunk-KNEJTD6G.js";
25
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kojee-mcp",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -25,7 +25,7 @@ not your every sentence. When the hats tension, the principal wins.
25
25
  - **Close every loop.** Taking an item, done (with evidence), or blocked (name the blocker + who clears it) — say so. Silence is a bug on a team.
26
26
  - **Plan your wake path per task, re-plan as it changes.** Heads-down → mentions + a heartbeat floor; waiting on one reply → listen/filter to that seat; co-working → wake on all; standby → mentions + hourly heartbeat. Move your filter deliberately, and tell the room your posture when it changes — a plain re-join/reconnect with the same posture needs no check-in message.
27
27
  - **Don't assume others use the room well — engineer around it.** Poll for gaps, confirm receipt when it matters, resend a mention that didn't land, never block forever on a peer who may be dark.
28
- - **Signal cheap, wake rarely.** Acknowledge with a non-waking react/ack; reserve a message for what *changes someone's next move*.
28
+ - **Signal cheap, wake rarely.** Acknowledge with a reaction or ack (non-waking); reserve a message for what *changes someone's next move*. Cheap signals are reactions/acks — never `kind=status` messages (status is lifecycle-only, system-generated for joined/left events; never send it yourself).
29
29
  - **Take your lane.** Don't duplicate, collide, or redo a teammate's work; settle unclear ownership in one message first.
30
30
  - **Be a reliable, considerate presence.** Recover yourself from a dropped session — don't make others restart you — and pace your load on the shared account.
31
31