@paigy/harness 0.3.0 → 0.3.1

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 (3) hide show
  1. package/dist/cli.js +204 -69
  2. package/dist/main.js +589 -496
  3. package/package.json +13 -12
package/dist/cli.js CHANGED
@@ -23175,6 +23175,79 @@ function detectAll(deps = {}) {
23175
23175
  return CATALOG.map((entry) => detect(entry, deps));
23176
23176
  }
23177
23177
 
23178
+ // src/cli-args.ts
23179
+ var USAGE = "usage: paigy-harness setup | pair | host [--grant DIR]\u2026 | service | enable-tools [--scope user|project] | hatch NAME [--voice KEY] [--slot SLOT] | handoff | [--harness claude|codex|agy] [--mode bypass|ask] [--cwd DIR] [--identity NAME] [--grace SECONDS] [--doctor] PROMPT\u2026";
23180
+ function parseArgs(argv) {
23181
+ const args = { harness: "claude", mode: "bypass", cwd: process.cwd(), doctor: false, help: false, host: false, pair: false, service: false, setup: false, enableTools: false, scope: "user", grant: [], hatch: null, voice: null, slot: null, identity: null, graceSeconds: 90, prompt: "", handoff: false };
23182
+ const words = [];
23183
+ for (let i = 0; i < argv.length; i++) {
23184
+ const arg = argv[i];
23185
+ const next = () => argv[++i];
23186
+ if (arg === "--harness") {
23187
+ const v = next();
23188
+ if (v !== "claude" && v !== "codex" && v !== "agy") return { error: `--harness must be claude, codex, or agy, got ${v ?? "nothing"}` };
23189
+ args.harness = v;
23190
+ } else if (arg === "--mode") {
23191
+ const v = next();
23192
+ if (v !== "bypass" && v !== "ask") return { error: `--mode must be bypass or ask, got ${v ?? "nothing"}` };
23193
+ args.mode = v;
23194
+ } else if (arg === "--cwd") {
23195
+ const v = next();
23196
+ if (!v) return { error: "--cwd needs a directory" };
23197
+ args.cwd = v;
23198
+ } else if (arg === "--doctor") {
23199
+ args.doctor = true;
23200
+ } else if (arg === "-h" || arg === "--help" || arg === "help") {
23201
+ args.help = true;
23202
+ } else if (arg === "--scope") {
23203
+ const v = next();
23204
+ if (v !== "user" && v !== "project") return { error: `--scope must be user or project, got ${v ?? "nothing"}` };
23205
+ args.scope = v;
23206
+ } else if (arg === "--grace") {
23207
+ const v = Number(next());
23208
+ if (!Number.isFinite(v) || v < 0) return { error: "--grace needs seconds (0 = straight to phone)" };
23209
+ args.graceSeconds = v;
23210
+ } else if (arg === "host") {
23211
+ args.host = true;
23212
+ } else if (arg === "pair") {
23213
+ args.pair = true;
23214
+ } else if (arg === "setup") {
23215
+ args.setup = true;
23216
+ } else if (arg === "service") {
23217
+ args.service = true;
23218
+ } else if (arg === "--grant") {
23219
+ const v = next();
23220
+ if (!v) return { error: "--grant needs a folder to allow" };
23221
+ args.grant.push(v);
23222
+ } else if (arg === "handoff") {
23223
+ args.handoff = true;
23224
+ } else if (arg === "enable-tools") {
23225
+ args.enableTools = true;
23226
+ } else if (arg === "hatch") {
23227
+ const v = next();
23228
+ if (!v) return { error: 'hatch needs a name: paigy-harness hatch "Voice bug hunter"' };
23229
+ args.hatch = v;
23230
+ } else if (arg === "--voice") {
23231
+ args.voice = next() ?? null;
23232
+ } else if (arg === "--slot") {
23233
+ args.slot = next() ?? null;
23234
+ } else if (arg === "--identity") {
23235
+ const v = next();
23236
+ if (!v) return { error: "--identity needs a hatched agent's name" };
23237
+ args.identity = v;
23238
+ } else if (arg?.startsWith("--")) {
23239
+ return { error: `unknown flag ${arg}` };
23240
+ } else if (arg) {
23241
+ words.push(arg);
23242
+ }
23243
+ }
23244
+ args.prompt = words.join(" ");
23245
+ if (args.help) return args;
23246
+ if (!args.doctor && !args.handoff && !args.hatch && !args.host && !args.pair && !args.service && !args.setup && !args.enableTools && !args.prompt) return { error: "a prompt is required (or setup / --doctor / pair / host / service / enable-tools / hatch NAME / handoff)" };
23247
+ return args;
23248
+ }
23249
+ var ENABLE_TOOLS_COMMAND = "npx -y -p @paigy/mcp@latest paigy-enable-tools";
23250
+
23178
23251
  // ../../packages/sdk/dist/index.js
23179
23252
  import { createRequire as __sdkCreateRequire } from "module";
23180
23253
  import { randomUUID } from "crypto";
@@ -29694,7 +29767,7 @@ var NotifyRequestSchema = external_exports.object({
29694
29767
  if (r.ask !== void 0) {
29695
29768
  for (const f of ["context", "select", "points"]) {
29696
29769
  if (r[f] !== void 0)
29697
- ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives it (use \`needs\` for multi-part asks)` });
29770
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives the answer shape from your prose. Drop ${f} and say it in \`ask\` instead ("should I\u2026" for approve/deny, "which of these\u2026" for a pick), passing \`options\` when you're offering concrete alternatives.` });
29698
29771
  }
29699
29772
  return;
29700
29773
  }
@@ -29967,7 +30040,13 @@ var NotifyPlanUnitSchema = external_exports.object({
29967
30040
  proposal: external_exports.object({
29968
30041
  select: SelectShapeSchema,
29969
30042
  options: external_exports.array(external_exports.object({ label: external_exports.string().min(1) })).optional()
29970
- }).optional()
30043
+ }).optional(),
30044
+ /** What the broker READ this unit as wanting from the human (#952 layer 2): a `decision`
30045
+ * between alternatives, an `approval` the agent is blocked on, or `knowledge` it just
30046
+ * needs to know. Reported so the agent can correct a misread the same way it ratifies a
30047
+ * shape — the read RAISES (a decision always asks) and never silences a question the
30048
+ * agent declared (#731, #923). */
30049
+ wants: external_exports.enum(["decision", "approval", "knowledge"]).optional()
29971
30050
  });
29972
30051
  var NotifyPlanSchema = external_exports.object({
29973
30052
  units: external_exports.array(NotifyPlanUnitSchema),
@@ -30214,6 +30293,15 @@ var HistoryItemSchema = external_exports.object({
30214
30293
  /** When you answered the agent's notification (agent→user only). */
30215
30294
  humanAckedAt: external_exports.string().nullable()
30216
30295
  });
30296
+ var ACTIVITY_LINES = 2;
30297
+ var ACTIVITY_LINE_MAX = 80;
30298
+ var AgentActivitySchema = external_exports.object({
30299
+ /** Oldest first, so the newest line is last — the one that replaces in place. */
30300
+ lines: external_exports.array(external_exports.string().max(ACTIVITY_LINE_MAX)).max(ACTIVITY_LINES),
30301
+ /** When the harness observed this tail. Its own timestamp, not the heartbeat's: a beat
30302
+ * that carries an UNCHANGED tail must not make a stalled agent look like it just moved. */
30303
+ at: external_exports.string().datetime()
30304
+ });
30217
30305
  var ConnectionSummarySchema = external_exports.object({
30218
30306
  /** The connection = the agent's token id (used to address a request). */
30219
30307
  id: external_exports.string(),
@@ -30247,6 +30335,11 @@ var ConnectionSummarySchema = external_exports.object({
30247
30335
  harnesses: external_exports.array(external_exports.object({ name: external_exports.string(), label: external_exports.string(), status: external_exports.string() })).optional(),
30248
30336
  workspaces: external_exports.array(external_exports.string()).optional()
30249
30337
  }).optional(),
30338
+ /** The tail of this agent's working log, when a harness is driving it — the agent page's
30339
+ * live strip. Absent for anything the desktop harness isn't running (a hatched identity
30340
+ * used straight from a terminal emits no work events; the page says so rather than
30341
+ * drawing an empty box). */
30342
+ activity: AgentActivitySchema.optional(),
30250
30343
  /** True = a provider-managed agent running in the provider's cloud (e.g. Anthropic CMA);
30251
30344
  * false = a local MCP connection running on the user's computer (Claude Code/Codex/…). */
30252
30345
  managed: external_exports.boolean()
@@ -31081,10 +31174,14 @@ async function claimSessions(opts = {}) {
31081
31174
  return (await res.json()).sessions;
31082
31175
  }
31083
31176
  async function heartbeat(runtime, opts = {}) {
31177
+ const body = {
31178
+ ...runtime !== void 0 ? { runtime } : {},
31179
+ ...opts.activity !== void 0 ? { activity: opts.activity } : {}
31180
+ };
31084
31181
  const res = ensureAuthed(await reach(`${BACKEND_URL}/api/presence`, {
31085
31182
  method: "POST",
31086
31183
  headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
31087
- ...runtime !== void 0 ? { body: JSON.stringify({ runtime }) } : {}
31184
+ ...Object.keys(body).length > 0 ? { body: JSON.stringify(body) } : {}
31088
31185
  }));
31089
31186
  if (!res.ok) throw new Error(`heartbeat failed: ${res.status}`);
31090
31187
  }
@@ -31299,7 +31396,7 @@ var NotifyRequestSchema2 = external_exports.object({
31299
31396
  if (r.ask !== void 0) {
31300
31397
  for (const f of ["context", "select", "points"]) {
31301
31398
  if (r[f] !== void 0)
31302
- ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives it (use \`needs\` for multi-part asks)` });
31399
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives the answer shape from your prose. Drop ${f} and say it in \`ask\` instead ("should I\u2026" for approve/deny, "which of these\u2026" for a pick), passing \`options\` when you're offering concrete alternatives.` });
31303
31400
  }
31304
31401
  return;
31305
31402
  }
@@ -31534,7 +31631,13 @@ var NotifyPlanUnitSchema2 = external_exports.object({
31534
31631
  proposal: external_exports.object({
31535
31632
  select: SelectShapeSchema2,
31536
31633
  options: external_exports.array(external_exports.object({ label: external_exports.string().min(1) })).optional()
31537
- }).optional()
31634
+ }).optional(),
31635
+ /** What the broker READ this unit as wanting from the human (#952 layer 2): a `decision`
31636
+ * between alternatives, an `approval` the agent is blocked on, or `knowledge` it just
31637
+ * needs to know. Reported so the agent can correct a misread the same way it ratifies a
31638
+ * shape — the read RAISES (a decision always asks) and never silences a question the
31639
+ * agent declared (#731, #923). */
31640
+ wants: external_exports.enum(["decision", "approval", "knowledge"]).optional()
31538
31641
  });
31539
31642
  var NotifyPlanSchema2 = external_exports.object({
31540
31643
  units: external_exports.array(NotifyPlanUnitSchema2),
@@ -31781,6 +31884,15 @@ var HistoryItemSchema2 = external_exports.object({
31781
31884
  /** When you answered the agent's notification (agent→user only). */
31782
31885
  humanAckedAt: external_exports.string().nullable()
31783
31886
  });
31887
+ var ACTIVITY_LINES2 = 2;
31888
+ var ACTIVITY_LINE_MAX2 = 80;
31889
+ var AgentActivitySchema2 = external_exports.object({
31890
+ /** Oldest first, so the newest line is last — the one that replaces in place. */
31891
+ lines: external_exports.array(external_exports.string().max(ACTIVITY_LINE_MAX2)).max(ACTIVITY_LINES2),
31892
+ /** When the harness observed this tail. Its own timestamp, not the heartbeat's: a beat
31893
+ * that carries an UNCHANGED tail must not make a stalled agent look like it just moved. */
31894
+ at: external_exports.string().datetime()
31895
+ });
31784
31896
  var ConnectionSummarySchema2 = external_exports.object({
31785
31897
  /** The connection = the agent's token id (used to address a request). */
31786
31898
  id: external_exports.string(),
@@ -31814,6 +31926,11 @@ var ConnectionSummarySchema2 = external_exports.object({
31814
31926
  harnesses: external_exports.array(external_exports.object({ name: external_exports.string(), label: external_exports.string(), status: external_exports.string() })).optional(),
31815
31927
  workspaces: external_exports.array(external_exports.string()).optional()
31816
31928
  }).optional(),
31929
+ /** The tail of this agent's working log, when a harness is driving it — the agent page's
31930
+ * live strip. Absent for anything the desktop harness isn't running (a hatched identity
31931
+ * used straight from a terminal emits no work events; the page says so rather than
31932
+ * drawing an empty box). */
31933
+ activity: AgentActivitySchema2.optional(),
31817
31934
  /** True = a provider-managed agent running in the provider's cloud (e.g. Anthropic CMA);
31818
31935
  * false = a local MCP connection running on the user's computer (Claude Code/Codex/…). */
31819
31936
  managed: external_exports.boolean()
@@ -32690,9 +32807,30 @@ function startSession(opts) {
32690
32807
  };
32691
32808
  }
32692
32809
 
32810
+ // src/paigy/activity.ts
32811
+ function shortenPaths(s) {
32812
+ return s.replace(/(?<![\w.@+-])(?:\/[\w.@+-]+){3,}/g, (p) => {
32813
+ const parts = p.split("/").filter(Boolean);
32814
+ return `\u2026/${parts.slice(-2).join("/")}`;
32815
+ });
32816
+ }
32817
+ function workLine(event) {
32818
+ const note = (event.note ?? "").split("\n").find((l) => l.trim()) ?? "";
32819
+ const line = shortenPaths([event.tool.trim(), note.trim()].filter(Boolean).join(" \u2014 ").replace(/\s+/g, " "));
32820
+ return line.length > ACTIVITY_LINE_MAX2 ? `${line.slice(0, ACTIVITY_LINE_MAX2 - 1)}\u2026` : line;
32821
+ }
32822
+ function pushWork(lines, line) {
32823
+ if (!line || lines[lines.length - 1] === line) return [...lines];
32824
+ return [...lines, line].slice(-ACTIVITY_LINES2);
32825
+ }
32826
+ function sameTail(a, b) {
32827
+ return a.length === b.length && a.every((l, i) => l === b[i]);
32828
+ }
32829
+
32693
32830
  // src/run.ts
32694
32831
  function runHarness(opts) {
32695
32832
  let running = true;
32833
+ let tail = [];
32696
32834
  const state = { ...opts.exclusive ? { exclusive: true } : {} };
32697
32835
  let session = null;
32698
32836
  const asMe = { token: opts.token };
@@ -32722,6 +32860,7 @@ function runHarness(opts) {
32722
32860
  }
32723
32861
  if (event.kind === "work") {
32724
32862
  opts.log(`\u2699 ${event.tool}${event.note ? ` \u2014 ${event.note.split("\n")[0] ?? ""}` : ""}`);
32863
+ tail = pushWork(tail, workLine(event));
32725
32864
  return;
32726
32865
  }
32727
32866
  const { parentId } = await mirror(event, state, deps);
@@ -32729,6 +32868,7 @@ function runHarness(opts) {
32729
32868
  if (event.kind === "turn") opts.log(`${event.role}: ${event.text.split("\n")[0] ?? ""}`);
32730
32869
  if (event.kind === "idle") {
32731
32870
  state.resting = true;
32871
+ tail = [];
32732
32872
  if (endsWithQuestion(event.result)) {
32733
32873
  const local = await opts.localAsk?.question?.(event.result ?? "") ?? null;
32734
32874
  if (local?.trim() && session && running) {
@@ -32766,8 +32906,10 @@ function runHarness(opts) {
32766
32906
  session?.send(text);
32767
32907
  },
32768
32908
  working: () => running && state.resting !== true,
32909
+ tail: () => [...tail],
32769
32910
  stop() {
32770
32911
  running = false;
32912
+ tail = [];
32771
32913
  cancelAsks(state);
32772
32914
  session?.stop();
32773
32915
  session = null;
@@ -32909,6 +33051,27 @@ function startHost(opts) {
32909
33051
  opts.log(`\u25B6 woke ${label} (${harness}) \u2014 work was waiting in ${workspace}`);
32910
33052
  }
32911
33053
  }
33054
+ const ACTIVITY_MS = 2e3;
33055
+ const published = /* @__PURE__ */ new Map();
33056
+ const streamActivity = () => {
33057
+ const publishTail = (token, lines) => {
33058
+ void heartbeat(void 0, { token, activity: { lines, at: (/* @__PURE__ */ new Date()).toISOString() } }).catch(() => {
33059
+ });
33060
+ };
33061
+ for (const [key, r] of runs) {
33062
+ if (!r.token) continue;
33063
+ const lines = r.run.tail();
33064
+ const was = published.get(key);
33065
+ if (was && sameTail(was.lines, lines)) continue;
33066
+ published.set(key, { token: r.token, lines });
33067
+ publishTail(r.token, lines);
33068
+ }
33069
+ for (const [key, was] of published) {
33070
+ if (runs.has(key)) continue;
33071
+ published.delete(key);
33072
+ if (was.lines.length > 0) publishTail(was.token, []);
33073
+ }
33074
+ };
32912
33075
  const publish = () => {
32913
33076
  try {
32914
33077
  writeFileSync3(HOST_FILE, JSON.stringify({ pid: process.pid, at: Date.now(), roster: api.roster() }));
@@ -32922,6 +33085,7 @@ function startHost(opts) {
32922
33085
  void sweepSlots();
32923
33086
  publish();
32924
33087
  }, 5e3);
33088
+ const activityTick = setInterval(streamActivity, ACTIVITY_MS);
32925
33089
  const stopSessions = () => {
32926
33090
  for (const { run, label } of runs.values()) {
32927
33091
  run.stop();
@@ -32959,6 +33123,8 @@ function startHost(opts) {
32959
33123
  clearInterval(pulse);
32960
33124
  clearInterval(spawnPoll);
32961
33125
  stopSessions();
33126
+ streamActivity();
33127
+ clearInterval(activityTick);
32962
33128
  try {
32963
33129
  writeFileSync3(HOST_FILE, JSON.stringify({ pid: process.pid, at: 0, roster: [] }));
32964
33130
  } catch {
@@ -32970,74 +33136,38 @@ function startHost(opts) {
32970
33136
  }
32971
33137
 
32972
33138
  // src/cli.ts
32973
- function parseArgs(argv) {
32974
- const args = { harness: "claude", mode: "bypass", cwd: process.cwd(), doctor: false, host: false, pair: false, service: false, setup: false, grant: [], hatch: null, voice: null, slot: null, identity: null, graceSeconds: 90, prompt: "", handoff: false };
32975
- const words = [];
32976
- for (let i = 0; i < argv.length; i++) {
32977
- const arg = argv[i];
32978
- const next = () => argv[++i];
32979
- if (arg === "--harness") {
32980
- const v = next();
32981
- if (v !== "claude" && v !== "codex" && v !== "agy") return { error: `--harness must be claude, codex, or agy, got ${v ?? "nothing"}` };
32982
- args.harness = v;
32983
- } else if (arg === "--mode") {
32984
- const v = next();
32985
- if (v !== "bypass" && v !== "ask") return { error: `--mode must be bypass or ask, got ${v ?? "nothing"}` };
32986
- args.mode = v;
32987
- } else if (arg === "--cwd") {
32988
- const v = next();
32989
- if (!v) return { error: "--cwd needs a directory" };
32990
- args.cwd = v;
32991
- } else if (arg === "--doctor") {
32992
- args.doctor = true;
32993
- } else if (arg === "--grace") {
32994
- const v = Number(next());
32995
- if (!Number.isFinite(v) || v < 0) return { error: "--grace needs seconds (0 = straight to phone)" };
32996
- args.graceSeconds = v;
32997
- } else if (arg === "host") {
32998
- args.host = true;
32999
- } else if (arg === "pair") {
33000
- args.pair = true;
33001
- } else if (arg === "setup") {
33002
- args.setup = true;
33003
- } else if (arg === "service") {
33004
- args.service = true;
33005
- } else if (arg === "--grant") {
33006
- const v = next();
33007
- if (!v) return { error: "--grant needs a folder to allow" };
33008
- args.grant.push(v);
33009
- } else if (arg === "handoff") {
33010
- args.handoff = true;
33011
- } else if (arg === "hatch") {
33012
- const v = next();
33013
- if (!v) return { error: 'hatch needs a name: paigy-harness hatch "Voice bug hunter"' };
33014
- args.hatch = v;
33015
- } else if (arg === "--voice") {
33016
- args.voice = next() ?? null;
33017
- } else if (arg === "--slot") {
33018
- args.slot = next() ?? null;
33019
- } else if (arg === "--identity") {
33020
- const v = next();
33021
- if (!v) return { error: "--identity needs a hatched agent's name" };
33022
- args.identity = v;
33023
- } else if (arg?.startsWith("--")) {
33024
- return { error: `unknown flag ${arg}` };
33025
- } else if (arg) {
33026
- words.push(arg);
33027
- }
33028
- }
33029
- args.prompt = words.join(" ");
33030
- if (!args.doctor && !args.handoff && !args.hatch && !args.host && !args.pair && !args.service && !args.setup && !args.prompt) return { error: "a prompt is required (or setup / --doctor / pair / host / service / hatch NAME / handoff)" };
33031
- return args;
33032
- }
33033
33139
  var STATUS_MARK = { ready: "\u2713", login: "\u25D0", "adapter-missing": "\u25D0", missing: "\u2717" };
33034
33140
  async function main() {
33035
33141
  const parsed = parseArgs(process.argv.slice(2));
33036
33142
  if ("error" in parsed) {
33037
33143
  console.error(`paigy-harness: ${parsed.error}`);
33038
- console.error("usage: paigy-harness setup | pair | host [--grant DIR]\u2026 | service | hatch NAME [--voice KEY] [--slot SLOT] | handoff | [--harness claude|codex|agy] [--mode bypass|ask] [--cwd DIR] [--identity NAME] [--grace SECONDS] [--doctor] PROMPT\u2026");
33144
+ console.error(USAGE);
33039
33145
  process.exit(2);
33040
33146
  }
33147
+ if (parsed.help) {
33148
+ console.log(USAGE);
33149
+ console.log("");
33150
+ console.log(" setup pair this machine, set up every agent found here, install the host service");
33151
+ console.log(" pair pair this machine only (QR / code)");
33152
+ console.log(" host host sessions your phone launches (--grant DIR to allow a folder)");
33153
+ console.log(" service install the host as a login service (macOS)");
33154
+ console.log(" enable-tools allowlist Paigy's tools in Claude Code so they don't prompt each time");
33155
+ console.log(" hatch NAME mint a sibling identity from this machine's credential");
33156
+ console.log(" handoff pin this terminal's identity here so your phone can resume it");
33157
+ console.log(" --doctor report which agent CLIs are installed and ready");
33158
+ console.log("");
33159
+ console.log('Anything else is a PROMPT: paigy-harness --harness codex "fix the tests"');
33160
+ return;
33161
+ }
33162
+ if (parsed.enableTools) {
33163
+ try {
33164
+ execSync(`${ENABLE_TOOLS_COMMAND} --scope ${parsed.scope}`, { stdio: "inherit", shell: "/bin/sh" });
33165
+ } catch {
33166
+ console.error(`Couldn't run it \u2014 try directly: ${ENABLE_TOOLS_COMMAND} --scope ${parsed.scope}`);
33167
+ process.exit(1);
33168
+ }
33169
+ return;
33170
+ }
33041
33171
  if (parsed.doctor) {
33042
33172
  for (const a of detectAll()) {
33043
33173
  console.log(`${STATUS_MARK[a.status]} ${a.label}${a.hint ? ` \u2014 ${a.hint}` : ""}${a.install ? `
@@ -33120,6 +33250,14 @@ async function main() {
33120
33250
  } catch {
33121
33251
  }
33122
33252
  }
33253
+ if (which("claude")) {
33254
+ try {
33255
+ execSync(ENABLE_TOOLS_COMMAND, { stdio: "ignore", shell: "/bin/sh" });
33256
+ console.log("\u2713 Paigy's tools allowlisted in Claude Code (they won't prompt each time)");
33257
+ } catch {
33258
+ console.log(` allowlist it later with: paigy-harness enable-tools`);
33259
+ }
33260
+ }
33123
33261
  try {
33124
33262
  const settings = join5(homedir6(), ".claude", "settings.json");
33125
33263
  if (which("claude") && existsSync5(settings)) {
@@ -33306,9 +33444,6 @@ async function main() {
33306
33444
  });
33307
33445
  }
33308
33446
  void main();
33309
- export {
33310
- parseArgs
33311
- };
33312
33447
  /*! Bundled license information:
33313
33448
 
33314
33449
  undici/lib/web/fetch/body.js: