agent-coord-mcp 0.4.8 → 0.4.9

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": "agent-coord-mcp",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "description": "File-backed MCP server for coordinating multiple AI coding agents (Claude Code, Cursor, Cline, etc.) on the same machine.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -54,6 +54,11 @@ const ROOT = args.dir ?? process.env.AGENT_COORD_DIR ?? path.join(homedir(), "ag
54
54
  const GROUP_WINDOW = 2 * 60 * 1000;
55
55
  let lastBlock = { who: null, ts: 0, kind: null };
56
56
 
57
+ // Whether the ephemeral suggestion hint is currently occupying the slot above
58
+ // the prompt (see drawHint/clearHint). Declared early so say(), called during
59
+ // startup replay, can reference it without tripping the const/let TDZ.
60
+ let hintActive = false;
61
+
57
62
  // Matches "@<this agent>" not followed by a name char, so we can flag messages
58
63
  // that ping the current user. ID may contain regex metachars — escape it.
59
64
  const SELF_MENTION_RE = new RegExp(
@@ -173,9 +178,14 @@ function completer(line) {
173
178
  } else if (line.startsWith("/")) {
174
179
  hits = SLASH_COMMANDS.filter((c) => c.startsWith(line));
175
180
  }
176
- if (hits.length > 1 && commonPrefix(hits).length <= substr.length) {
181
+ if (hits.length > 1) {
182
+ // Show the options as an ephemeral hint and hand readline only the common
183
+ // prefix — a single-element completion means its native multi-column dump
184
+ // never lands in scrollback. Tab still advances to the shared prefix.
177
185
  const display = hits.map((h) => h.trim()).join(" ");
178
- say(A.dim(" ┄ " + display));
186
+ drawHint(A.dim(" ┄ " + display));
187
+ const cp = commonPrefix(hits);
188
+ return [[cp.length >= substr.length ? cp : substr], substr];
179
189
  }
180
190
  return [hits, substr];
181
191
  }
@@ -202,8 +212,14 @@ const rl = readline.createInterface({
202
212
  // the "@" into its line buffer before we inspect it.
203
213
  if (process.stdin.isTTY) {
204
214
  readline.emitKeypressEvents(process.stdin);
205
- process.stdin.on("keypress", (str) => {
206
- if (str === "@") setImmediate(showMentionPicker);
215
+ process.stdin.on("keypress", (str, key) => {
216
+ // setImmediate lets readline mutate its line buffer first, then we inspect
217
+ // it / redraw the slot above the prompt.
218
+ if (str === "@") { setImmediate(showMentionPicker); return; }
219
+ // Tab is the completer's — it draws/keeps the hint itself. Every other key
220
+ // dismisses a showing hint so the view snaps back to a clean separator.
221
+ if (key && key.name === "tab") return;
222
+ if (hintActive) setImmediate(clearHint);
207
223
  });
208
224
  }
209
225
 
@@ -358,7 +374,28 @@ function agentColor(id) {
358
374
  return AGENT_COLORS[idx];
359
375
  }
360
376
 
377
+ // Ephemeral suggestion line (the @mention / completion picker). It borrows the
378
+ // separator slot directly above the prompt: drawn there, then wiped back to a
379
+ // real separator on the next keystroke — so it never piles up in scrollback.
380
+ function drawHint(content) {
381
+ if (typeof rl === "undefined" || !lastLineWasSep) return;
382
+ process.stdout.write("\x1b[1A\r\x1b[2K"); // up to the slot, clear it
383
+ process.stdout.write(content + "\n"); // draw the hint in place of the sep
384
+ rl.prompt(true); // redraw prompt + preserved input
385
+ hintActive = true;
386
+ }
387
+
388
+ function clearHint() {
389
+ if (typeof rl === "undefined" || !lastLineWasSep) { hintActive = false; return; }
390
+ if (!hintActive) return;
391
+ process.stdout.write("\x1b[1A\r\x1b[2K"); // up to the hint slot, clear it
392
+ process.stdout.write(sepLine() + "\n"); // restore the separator
393
+ rl.prompt(true);
394
+ hintActive = false;
395
+ }
396
+
361
397
  function say(line) {
398
+ hintActive = false; // any real output reclaims the slot the hint borrowed
362
399
  if (lastLineWasSep) {
363
400
  // Async path: a separator we own sits directly above the prompt; we own
364
401
  // that line. Replace it with the incoming message, drop a new sep, and
@@ -878,7 +915,7 @@ function showMentionPicker() {
878
915
  const ids = onlineAgentIds().filter((id) => id !== ID);
879
916
  if (!ids.length) return;
880
917
  const list = ids.map((id) => A.green("●") + agentColor(id)(`@${id}`)).join(" ");
881
- say(A.dim(" ┄ ") + list + A.dim(" · Tab to complete"));
918
+ drawHint(A.dim(" ┄ ") + list + A.dim(" · Tab to complete"));
882
919
  }
883
920
 
884
921
  function readJsonl(file) {