@scp-dev/cli 0.2.4 → 0.3.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.
@@ -23,7 +23,7 @@ import { existsSync, realpathSync } from "fs";
23
23
  import { tmpdir } from "os";
24
24
  import { basename, dirname, isAbsolute, join, resolve } from "path";
25
25
  import { shortHostname } from "@scp-dev/broker";
26
- import { LIMITS, validHandle, validHarness } from "@scp-dev/core";
26
+ import { GENERIC_HARNESSES, LIMITS, validHandle, validHarness } from "@scp-dev/core";
27
27
  var SHORT = { "claude-code": "claude" };
28
28
  var SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "fish"]);
29
29
  var LABEL_KEY = /^[a-z0-9][a-z0-9._-]{0,31}$/;
@@ -156,6 +156,42 @@ function command(args) {
156
156
  return basename(args.split(/\s+/)[0].replace(/^-/, "")).toLowerCase();
157
157
  }
158
158
  var namesHarness = (words) => words.some((word) => !word.startsWith("-") && word.split("/").some((part) => HARNESSES.has(part.toLowerCase().replace(/\..*$/, ""))));
159
+ var HARNESS_IDS = /* @__PURE__ */ new Map([
160
+ ["claude", "claude-code"],
161
+ ["claude-code", "claude-code"],
162
+ ["codex", "codex"],
163
+ ["opencode", "opencode"],
164
+ ["opencode-ai", "opencode"],
165
+ ["grok", "grok"],
166
+ ["grok-cli", "grok"],
167
+ ["muse", "muse"],
168
+ ["agy", "agy"],
169
+ ["antigravity", "agy"],
170
+ ["gemini", "gemini"],
171
+ ["gemini-cli", "gemini"],
172
+ ["pi", "pi"],
173
+ ["pi-coding-agent", "pi"]
174
+ ]);
175
+ var harnessId = (name) => HARNESS_IDS.get(name.toLowerCase().replace(/\..*$/, ""));
176
+ function harnessOf(args) {
177
+ if (!args) return void 0;
178
+ const name = command(args);
179
+ const own = harnessId(name);
180
+ if (own) return own;
181
+ if (!INTERPRETERS.has(name) && !RUNNERS.has(name) && !FETCHERS.has(name) && !/^python[0-9.]*$/.test(name)) return void 0;
182
+ for (const word of args.split(/\s+/).slice(1)) {
183
+ if (word.startsWith("-")) continue;
184
+ for (const part of word.split("/").reverse()) {
185
+ const id = part === "pi" ? void 0 : harnessId(part);
186
+ if (id) return id;
187
+ }
188
+ }
189
+ return void 0;
190
+ }
191
+ var inferring = (env) => env.SCP_HARNESS_DETECT !== "0";
192
+ function harnessAt(pid, table = readPsTable()) {
193
+ return inferring(process.env) ? harnessOf(table?.get(pid)?.args) : void 0;
194
+ }
159
195
  var roots;
160
196
  function workspaceRoots() {
161
197
  if (roots) return roots;
@@ -284,10 +320,11 @@ function hookHarnessPid(env, harness, ppid = process.ppid, parentOf = psParent)
284
320
  return harnessPid(ppid, parentOf);
285
321
  }
286
322
  function relayIdentity(input) {
287
- const harness = resolveHarness(input.harness, input.env, input.fallbackHarness);
323
+ const resolved = resolveHarness(input.harness, input.env, input.fallbackHarness);
324
+ const pid = input.harnessPid(resolved);
325
+ const harness = GENERIC_HARNESSES.has(resolved) && input.inferHarness?.(pid) || resolved;
288
326
  const cwd = isAbsolute(input.cwd) ? input.cwd : resolve(input.cwd);
289
327
  const handle = input.as ?? input.env.SCP_NAME ?? deriveHandle(harness, cwd);
290
- const pid = input.harnessPid(harness);
291
328
  const harnessProcess = processKey(pid, input.startOf);
292
329
  const keys = input.lineageOf ? input.lineageOf(pid) : lineage(pid);
293
330
  return {
@@ -464,7 +501,12 @@ function localHandle(raw) {
464
501
  }
465
502
  function walkNow() {
466
503
  const table = readPsTable();
467
- return { keys: lineage(process.ppid, table), self: harnessLineage(process.ppid, table) };
504
+ const self = harnessLineage(process.ppid, table);
505
+ const harness = self.length ? harnessAt(Number(/^pid:(\d+)/.exec(self.at(-1))?.[1]), table) : void 0;
506
+ return { keys: lineage(process.ppid, table), self, ...harness ? { harness } : {} };
507
+ }
508
+ function claimHarness(values, env, walk) {
509
+ return values.harness ?? env.SCP_HARNESS ?? (inferring(env) ? walk?.harness ?? detectHarness(env) : void 0) ?? "cli";
468
510
  }
469
511
  async function open(verb, values, cwd, bodyFromStdin, walk) {
470
512
  const named = values.as ?? process.env.SCP_NAME;
@@ -487,11 +529,11 @@ async function open(verb, values, cwd, bodyFromStdin, walk) {
487
529
  `${CLI_NAME2} ${verb} needs an identity: --as HANDLE (or $SCP_NAME) for an agent, or --human for your own inbox me/ (no terminal is attached, so me/ is not assumed)`
488
530
  );
489
531
  }
490
- const { machineName, openCloud } = await import("./remote-NTHRHVEP.js");
532
+ const { machineName, openCloud } = await import("./remote-P4GFUIAP.js");
491
533
  const handle2 = named ? localHandle(named) : void 0;
492
534
  const claim = handle2 ? {
493
535
  handle: handle2,
494
- harness: values.harness ?? process.env.SCP_HARNESS ?? "cli",
536
+ harness: claimHarness(values, process.env, walk ?? walkNow()),
495
537
  cwd,
496
538
  ...process.env.SCP_PARENT ? { parent: process.env.SCP_PARENT } : {},
497
539
  labels: labelsFor(process.env, machineName(values.machine))
@@ -510,7 +552,8 @@ async function open(verb, values, cwd, bodyFromStdin, walk) {
510
552
  };
511
553
  }
512
554
  if (values.machine !== void 0) throw new RelayUsage("--machine names the machine of the cloud transport: add --cloud");
513
- const { keys, self } = walk ?? walkNow();
555
+ const here = walk ?? walkNow();
556
+ const { keys, self } = here;
514
557
  const joined = !values.as && named && self.length ? membership(process.env, sessionKey(process.env, self.at(-1))) : {};
515
558
  const handle = "handle" in joined && joined.handle ? joined.handle : named ? localHandle(named) : void 0;
516
559
  const harnessPid2 = self.length ? Number(/^pid:(\d+)/.exec(self.at(-1))?.[1]) : void 0;
@@ -526,7 +569,7 @@ async function open(verb, values, cwd, bodyFromStdin, walk) {
526
569
  claim: {
527
570
  handle,
528
571
  exact: true,
529
- harness: values.harness ?? process.env.SCP_HARNESS ?? "cli",
572
+ harness: claimHarness(values, process.env, here),
530
573
  cwd,
531
574
  ...process.env.SCP_PARENT ? { parent: process.env.SCP_PARENT } : {},
532
575
  labels: labelsFor(process.env),
@@ -744,7 +787,7 @@ function relayVerb(positionals, asJson, env = process.env) {
744
787
  }
745
788
  async function loginMain(positionals, values) {
746
789
  if (positionals.length) throw new RelayUsage(`${CLI_NAME3} login takes only flags`);
747
- return (await import("./login-IPS75GLQ.js")).login(values);
790
+ return (await import("./login-AUMLI4FS.js")).login(values);
748
791
  }
749
792
  function logoutMain(positionals, asJson, env = process.env) {
750
793
  if (positionals.length) throw new RelayUsage(`${CLI_NAME3} logout takes no arguments`);
@@ -767,6 +810,7 @@ export {
767
810
  sessionKey,
768
811
  psStart,
769
812
  readPsTable,
813
+ harnessAt,
770
814
  spawnerKey,
771
815
  psParent,
772
816
  harnessPid,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  hooksTarget
3
- } from "./chunk-ZMVFN6MU.js";
3
+ } from "./chunk-Q5UI4Z7H.js";
4
4
  import {
5
5
  AGY_HOOK_SET,
6
6
  BLOCK_STOP,
@@ -12,12 +12,12 @@ import {
12
12
  mergeHooks,
13
13
  musePlugin,
14
14
  opencodePlugin
15
- } from "./chunk-X4ZLEM47.js";
15
+ } from "./chunk-QRWQIHV6.js";
16
16
  import {
17
17
  RelayUsage,
18
18
  gitToplevel,
19
19
  setJoinMode
20
- } from "./chunk-E7JG5PJB.js";
20
+ } from "./chunk-2LIJXXTL.js";
21
21
 
22
22
  // src/setup.ts
23
23
  import { spawnSync } from "child_process";
@@ -3,10 +3,10 @@ import {
3
3
  agySettingsFile,
4
4
  mcpFile,
5
5
  mountedEntry
6
- } from "./chunk-W3VNCRBX.js";
6
+ } from "./chunk-2OSUVJ2J.js";
7
7
  import {
8
8
  steerPrompt
9
- } from "./chunk-X4ZLEM47.js";
9
+ } from "./chunk-QRWQIHV6.js";
10
10
  import {
11
11
  OPTIONS,
12
12
  RelayUsage,
@@ -18,7 +18,7 @@ import {
18
18
  selectorList,
19
19
  spawnerKey,
20
20
  walkNow
21
- } from "./chunk-E7JG5PJB.js";
21
+ } from "./chunk-2LIJXXTL.js";
22
22
  import {
23
23
  SELF,
24
24
  note
@@ -918,7 +918,9 @@ export {
918
918
  SPAWN_RUNNER,
919
919
  spawnEnvironment,
920
920
  codexMount,
921
+ toml,
921
922
  codexOverrides,
923
+ CODEX_VALUED,
922
924
  codexOverrideAt,
923
925
  programAt,
924
926
  MESSAGING,
@@ -5,7 +5,7 @@ import {
5
5
  configDir,
6
6
  relayCredential,
7
7
  writePrivateJson
8
- } from "./chunk-E7JG5PJB.js";
8
+ } from "./chunk-2LIJXXTL.js";
9
9
  import {
10
10
  note,
11
11
  printTable
@@ -150,13 +150,15 @@ function readCloudFile(path, env = process.env) {
150
150
  }
151
151
  if (value.redact !== void 0 && typeof value.redact !== "boolean") throw new Error(`${path}: redact is true or false`);
152
152
  if (value.private_ok !== void 0 && typeof value.private_ok !== "boolean") throw new Error(`${path}: private_ok is true or false`);
153
+ if (value.mirror_local !== void 0 && typeof value.mirror_local !== "boolean") throw new Error(`${path}: mirror_local is true or false`);
153
154
  return {
154
155
  bridge: true,
155
156
  machine: value.machine,
156
157
  url,
157
158
  ...value.mirror ? { mirror: value.mirror } : {},
158
159
  ...value.redact !== void 0 ? { redact: value.redact } : {},
159
- ...value.private_ok !== void 0 ? { private_ok: value.private_ok } : {}
160
+ ...value.private_ok !== void 0 ? { private_ok: value.private_ok } : {},
161
+ ...value.mirror_local !== void 0 ? { mirror_local: value.mirror_local } : {}
160
162
  };
161
163
  }
162
164
  async function daemonBridge(env = process.env, log = console.error) {
@@ -184,7 +186,8 @@ async function daemonBridge(env = process.env, log = console.error) {
184
186
  machine: file.machine,
185
187
  mirror: file.mirror,
186
188
  redact: file.redact,
187
- privateOk: file.private_ok
189
+ privateOk: file.private_ok,
190
+ mirrorLocal: file.mirror_local
188
191
  };
189
192
  }
190
193
  async function cloudMain(positionals, values, brokerStatus) {
@@ -10,11 +10,11 @@ import {
10
10
  mergeHooks,
11
11
  musePlugin,
12
12
  opencodePlugin
13
- } from "./chunk-X4ZLEM47.js";
13
+ } from "./chunk-QRWQIHV6.js";
14
14
  import {
15
15
  RelayUsage,
16
16
  gitToplevel
17
- } from "./chunk-E7JG5PJB.js";
17
+ } from "./chunk-2LIJXXTL.js";
18
18
 
19
19
  // src/install.ts
20
20
  import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
@@ -4,6 +4,7 @@ import {
4
4
  readCapped
5
5
  } from "./chunk-YYSLYVNZ.js";
6
6
  import {
7
+ harnessAt,
7
8
  hookHarness,
8
9
  hookHarnessPid,
9
10
  membership,
@@ -12,7 +13,7 @@ import {
12
13
  psStart,
13
14
  relayIdentity,
14
15
  sessionKey
15
- } from "./chunk-E7JG5PJB.js";
16
+ } from "./chunk-2LIJXXTL.js";
16
17
 
17
18
  // src/hooks.ts
18
19
  import { Readable } from "stream";
@@ -354,9 +355,44 @@ function launchedSubagent(harness, event, payload) {
354
355
  const title = titleText(asString(input.description));
355
356
  return id && title && /^[A-Za-z0-9_-]{1,64}$/.test(id) ? { id, title } : void 0;
356
357
  }
357
- function hookReply(harness, kind, text, unread = 0, continues = true) {
358
- if (kind === "stop" && harness === "claude-code" && continues)
359
- return JSON.stringify({ hookSpecificOutput: { hookEventName: "Stop", additionalContext: text } });
358
+ var CLAUDE_CAPS = { context: { chars: 8e3, lines: 200 }, reason: { chars: 2e3, lines: 20 } };
359
+ var MARKER_ROOM = 80;
360
+ var BLOCK = /(<relay-message [^>]*?\bshort="([^"]*)"[^>]*>\n)([\s\S]*?)(\n<\/relay-message>)/g;
361
+ var cutAt = (text, chars) => text.slice(0, chars > 0 && /[\ud800-\udbff]/.test(text[chars - 1] ?? "") ? chars - 1 : chars);
362
+ function fitText(text, cap2) {
363
+ const parts = [];
364
+ let at = 0;
365
+ for (const match of text.matchAll(BLOCK)) {
366
+ parts.push(text.slice(at, match.index) + match[1], { short: match[2], body: match[3], chars: match[3].length, lines: match[3].split("\n").length });
367
+ at = match.index + match[0].length - match[4].length;
368
+ }
369
+ parts.push(text.slice(at));
370
+ const blocks = parts.filter((part) => typeof part !== "string");
371
+ const render = () => parts.map((part) => {
372
+ if (typeof part === "string") return part;
373
+ if (part.chars >= part.body.length && part.lines >= part.body.split("\n").length) return part.body;
374
+ const head = cutAt(part.body.split("\n").slice(0, part.lines).join("\n"), part.chars).trimEnd();
375
+ const marker = `[truncated, ${part.body.length - head.length} more chars: ${CLI_NAME} show ${part.short}]`;
376
+ return head ? `${head}
377
+ ${marker}` : marker;
378
+ }).join("");
379
+ for (let i = blocks.length - 1, rendered = render(); ; rendered = render()) {
380
+ const chars = rendered.length - cap2.chars;
381
+ const lines = rendered.split("\n").length - cap2.lines;
382
+ if (chars <= 0 && lines <= 0) return rendered;
383
+ while (i >= 0 && blocks[i].chars === 0 && blocks[i].lines === 0) i--;
384
+ if (i < 0) return rendered;
385
+ const block = blocks[i];
386
+ if (chars > 0) block.chars = Math.max(0, Math.min(block.chars, block.body.length) - chars - MARKER_ROOM);
387
+ if (lines > 0) block.lines = Math.max(0, Math.min(block.lines, block.body.split("\n").length) - lines - 1);
388
+ if (chars <= 0 && block.lines === 0) block.chars = 0;
389
+ if (lines <= 0 && block.chars === 0) block.lines = 0;
390
+ }
391
+ }
392
+ function hookReply(harness, kind, raw, unread = 0, continues = true) {
393
+ const claude = harness === "claude-code";
394
+ const text = claude ? fitText(raw, kind === "stop" && !continues ? CLAUDE_CAPS.reason : CLAUDE_CAPS.context) : raw;
395
+ if (kind === "stop" && claude && continues) return JSON.stringify({ hookSpecificOutput: { hookEventName: "Stop", additionalContext: text } });
360
396
  if (kind === "stop")
361
397
  return JSON.stringify(
362
398
  harness === "agy" ? { decision: "continue", reason: text } : { decision: "block", reason: text, ...harness === "opencode" ? { unread } : {} }
@@ -416,6 +452,7 @@ async function runHook(args, options) {
416
452
  cwd: event.cwd ?? options.cwd,
417
453
  env: options.env,
418
454
  harnessPid: () => pid,
455
+ ...options.harnessPid ? {} : { inferHarness: (at) => harnessAt(at) },
419
456
  startOf: () => started
420
457
  });
421
458
  if (!validHandle(claim.handle)) return;
@@ -513,7 +550,8 @@ async function wakeHook(harnessArg, payload, waitS, options) {
513
550
  fallbackHarness: "hook",
514
551
  cwd: asString(payload.cwd) ?? options.cwd,
515
552
  env: options.env,
516
- harnessPid: options.harnessPid ?? ((harness) => hookHarnessPid(options.env, harness))
553
+ harnessPid: options.harnessPid ?? ((harness) => hookHarnessPid(options.env, harness)),
554
+ ...options.harnessPid ? {} : { inferHarness: (at) => harnessAt(at) }
517
555
  });
518
556
  if (!validHandle(claim.handle)) return;
519
557
  const waitMs = Math.min(waitS, LIMITS.waitMaxS) * 1e3;
@@ -729,6 +767,8 @@ function opencodePlugin(options = {}) {
729
767
  import { spawn } from "node:child_process"
730
768
  import { text as readAll } from "node:stream/consumers"
731
769
  const BLOCK_STOP = ${options.blockStop === true}
770
+ /** before a session exists, the waiter starts this long after load, so a session opened at launch claims the first message */
771
+ const STARTUP_MS = 3000
732
772
 
733
773
  type Reply = { hookSpecificOutput?: { additionalContext?: unknown }; decision?: unknown; reason?: unknown }
734
774
 
@@ -775,6 +815,7 @@ export const Scp = async ({ client, directory }: { client: any; directory: strin
775
815
  const WAKE = process.env.SCP_WAKE !== "0"
776
816
  const busy = new Set<string>()
777
817
  let target: string | undefined
818
+ let fresh = true
778
819
  let waiter: ReturnType<typeof spawn> | undefined
779
820
  let looping = false
780
821
  let armed = 0
@@ -784,7 +825,7 @@ export const Scp = async ({ client, directory }: { client: any; directory: strin
784
825
  looping = true
785
826
  let version = armed
786
827
  try {
787
- while (WAKE && target && busy.size === 0) {
828
+ while (WAKE && busy.size === 0 && (target || fresh)) {
788
829
  const id = target
789
830
  const began = Date.now()
790
831
  let text: string | undefined
@@ -797,7 +838,7 @@ export const Scp = async ({ client, directory }: { client: any; directory: strin
797
838
  child.once("close", () => resolve())
798
839
  })
799
840
  child.stdin!.on("error", () => {})
800
- child.stdin!.end(JSON.stringify({ hook_event_name: "Wake", session_id: id, cwd: directory }))
841
+ child.stdin!.end(JSON.stringify({ hook_event_name: "Wake", ...(id ? { session_id: id } : {}), cwd: directory }))
801
842
  const [output] = await Promise.all([readAll(child.stdout!), ended])
802
843
  text = output.trim() ? context(JSON.parse(output) as Reply) : undefined
803
844
  } catch {} finally {
@@ -805,7 +846,8 @@ export const Scp = async ({ client, directory }: { client: any; directory: strin
805
846
  }
806
847
  if (text) {
807
848
  version = armed
808
- await client.session.promptAsync({ path: { id }, body: { parts: [{ type: "text", text }] } }).catch(() => {})
849
+ const into = target ?? (await client.session.create({ body: {} }).then((made: any) => made?.data?.id).catch(() => undefined))
850
+ if (into) await client.session.promptAsync({ path: { id: into }, body: { parts: [{ type: "text", text }] } }).catch(() => {})
809
851
  break
810
852
  }
811
853
  if (Date.now() - began < 2000) await new Promise((resolve) => setTimeout(resolve, 5000))
@@ -817,6 +859,7 @@ export const Scp = async ({ client, directory }: { client: any; directory: strin
817
859
  }
818
860
  const arm = (id: string) => {
819
861
  target = id
862
+ fresh = false
820
863
  armed++
821
864
  void wake()
822
865
  }
@@ -835,6 +878,9 @@ export const Scp = async ({ client, directory }: { client: any; directory: strin
835
878
  if (child === undefined && children.get(id) === known) children.delete(id)
836
879
  return child === true
837
880
  }
881
+ setTimeout(() => {
882
+ if (fresh && busy.size === 0) void wake()
883
+ }, STARTUP_MS).unref?.()
838
884
  return {
839
885
  "tool.execute.after": async (input: { tool: string; sessionID: string; args?: unknown }, output: { output?: unknown }) => {
840
886
  const payload = { hook_event_name: "PostToolUse", session_id: input.sessionID, cwd: directory, tool_name: input.tool, tool_input: input.args }
@@ -969,6 +1015,8 @@ export {
969
1015
  titleText,
970
1016
  promptTitle,
971
1017
  transcriptTitle,
1018
+ CLAUDE_CAPS,
1019
+ fitText,
972
1020
  hookReply,
973
1021
  TLDR_EVERY_DEFAULT,
974
1022
  TLDR,
@@ -0,0 +1,130 @@
1
+ import {
2
+ entryArgv,
3
+ mcpFile,
4
+ mountedEntry,
5
+ whichOnPath
6
+ } from "./chunk-2OSUVJ2J.js";
7
+ import "./chunk-Q5UI4Z7H.js";
8
+ import "./chunk-QRWQIHV6.js";
9
+ import "./chunk-YYSLYVNZ.js";
10
+ import {
11
+ CHANNELS_ENV,
12
+ RelayUsage,
13
+ deriveHandle,
14
+ joinMode
15
+ } from "./chunk-2LIJXXTL.js";
16
+ import "./chunk-MNTIEWKF.js";
17
+
18
+ // src/claude.ts
19
+ import { spawn } from "child_process";
20
+ import { existsSync, readFileSync } from "fs";
21
+ import { join } from "path";
22
+ import { CLI_NAME, validHandle } from "@scp-dev/core";
23
+ var CHANNEL_FLAGS = ["--dangerously-load-development-channels", "server:scp"];
24
+ function claudeArgv(argv) {
25
+ let name;
26
+ let i = 0;
27
+ for (; ; ) {
28
+ const arg = argv[i];
29
+ if (arg === "--name" || arg === "--as") {
30
+ if (argv[i + 1] === void 0) throw new RelayUsage(`${CLI_NAME} claude ${arg} takes a handle`);
31
+ name = argv[i + 1];
32
+ i += 2;
33
+ } else if (arg?.startsWith("--name=") || arg?.startsWith("--as=")) {
34
+ name = arg.slice(arg.indexOf("=") + 1);
35
+ i++;
36
+ } else break;
37
+ }
38
+ if (argv[i] === "--") i++;
39
+ name = name?.replace(/^me\//, "");
40
+ if (name !== void 0 && !validHandle(name)) throw new RelayUsage(`--name ${JSON.stringify(name)} is not a valid agent handle`);
41
+ return { ...name !== void 0 ? { name } : {}, args: argv.slice(i) };
42
+ }
43
+ function claudeLaunchArgs(args, added) {
44
+ const end = args.indexOf("--");
45
+ const at = end < 0 ? args.length : end;
46
+ const channels = args.some((arg, i) => i < at && (arg === "server:scp" || arg === `${CHANNEL_FLAGS[0]}=server:scp`)) ? [] : CHANNEL_FLAGS;
47
+ return [...args.slice(0, at), ...channels, ...added, ...args.slice(at)];
48
+ }
49
+ var STRICT_HINT = `${CLI_NAME} claude: --strict-mcp-config without --mcp-config drops the scp mount, so messages cannot wake this session`;
50
+ function externalMcp(args) {
51
+ return args.some((arg) => arg === "--mcp-config" || arg.startsWith("--mcp-config="));
52
+ }
53
+ function namePrompt(name, args, env, cwd) {
54
+ if (name !== void 0 || joinMode(env) === "manual" || args.some((arg) => /^--(append-)?system-prompt/.test(arg))) return [];
55
+ const handle = env.SCP_NAME || deriveHandle("claude-code", cwd);
56
+ return [
57
+ "--append-system-prompt",
58
+ `You are on the SCP network as ${handle}. Pick a short descriptive name for yourself with \`${CLI_NAME} name <name>\` when you know what you're working on.`
59
+ ];
60
+ }
61
+ function mountHint(env, cwd = process.cwd()) {
62
+ const file = mcpFile("claude", env);
63
+ const entries = [];
64
+ try {
65
+ const user = mountedEntry("claude", file);
66
+ if (user) entries.push(user);
67
+ const config = existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : {};
68
+ const local = config.projects?.[cwd]?.mcpServers?.scp;
69
+ if (local && typeof local === "object") entries.push(local);
70
+ } catch {
71
+ return `${CLI_NAME} claude: ${file} is unreadable, so the scp mount is unchecked; fix it or run ${CLI_NAME} setup --write`;
72
+ }
73
+ const project = join(cwd, ".mcp.json");
74
+ try {
75
+ const entry = existsSync(project) ? mountedEntry("claude", project) : void 0;
76
+ if (entry) entries.push(entry);
77
+ } catch {
78
+ }
79
+ if (!entries.length) return `${CLI_NAME} claude: Claude Code has no scp MCP mount, so messages cannot wake this session; run ${CLI_NAME} setup --write`;
80
+ if (!entries.some((entry) => entryArgv("claude", entry).includes("--channels")))
81
+ return `${CLI_NAME} claude: the scp mount lacks --channels, so messages cannot wake this session; run ${CLI_NAME} setup --write`;
82
+ return void 0;
83
+ }
84
+ async function claudeCommand(argv, env = process.env, err = (line) => void process.stderr.write(`${line}
85
+ `), cwd = process.cwd()) {
86
+ if (argv[0] === "--help" || argv[0] === "-h") {
87
+ process.stdout.write(
88
+ `${CLI_NAME} claude [--name N | --as N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake it
89
+ `
90
+ );
91
+ return 0;
92
+ }
93
+ const { name, args } = claudeArgv(argv);
94
+ const bin = whichOnPath("claude", env);
95
+ if (!bin) throw new RelayUsage("claude is not on PATH");
96
+ const external = externalMcp(args);
97
+ const hint = external ? void 0 : args.includes("--strict-mcp-config") ? STRICT_HINT : mountHint(env, cwd);
98
+ if (hint) throw new RelayUsage(hint);
99
+ if (external) err(`${CLI_NAME} claude: using your --mcp-config; it needs the scp server with --channels for messages to wake this session`);
100
+ const child = spawn(bin, claudeLaunchArgs(args, namePrompt(name, args, env, cwd)), {
101
+ cwd,
102
+ stdio: "inherit",
103
+ env: { ...env, ...name !== void 0 ? { SCP_NAME: name } : {}, [CHANNELS_ENV]: String(process.pid) }
104
+ });
105
+ const forward = (signal) => () => void child.kill(signal);
106
+ const handlers = ["SIGTERM", "SIGHUP"].map((signal) => [signal, forward(signal)]);
107
+ const ignore = () => {
108
+ };
109
+ process.on("SIGINT", ignore);
110
+ for (const [signal, handler] of handlers) process.on(signal, handler);
111
+ try {
112
+ return await new Promise((resolve, reject) => {
113
+ child.once("error", reject);
114
+ child.once("exit", (code, signal) => resolve(code ?? (signal ? 128 + (signalNumber(signal) ?? 1) : 1)));
115
+ });
116
+ } finally {
117
+ process.off("SIGINT", ignore);
118
+ for (const [signal, handler] of handlers) process.off(signal, handler);
119
+ }
120
+ }
121
+ var SIGNALS = { SIGHUP: 1, SIGINT: 2, SIGKILL: 9, SIGTERM: 15 };
122
+ var signalNumber = (signal) => SIGNALS[signal];
123
+ export {
124
+ CHANNEL_FLAGS,
125
+ claudeArgv,
126
+ claudeCommand,
127
+ claudeLaunchArgs,
128
+ mountHint,
129
+ namePrompt
130
+ };
package/dist/cli.js CHANGED
@@ -17,6 +17,6 @@ function quietSqliteWarning() {
17
17
  // src/cli.ts
18
18
  if (!await subagentFastPath(process.argv.slice(2))) {
19
19
  quietSqliteWarning();
20
- const { run } = await import("./main-MCNGHJP2.js");
20
+ const { run } = await import("./main-BR6TYEGJ.js");
21
21
  await run(process.argv.slice(2));
22
22
  }