@4pm/cli 1.19.7 → 1.20.0-b

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/dist/index.js CHANGED
@@ -29519,8 +29519,8 @@ async function runLink(serverUrl, explicitProfile, token = null) {
29519
29519
  }
29520
29520
 
29521
29521
  // src/commands/start.ts
29522
- import { spawn as spawn10 } from "child_process";
29523
- import { join as join36 } from "path";
29522
+ import { spawn as spawn11 } from "child_process";
29523
+ import { join as join37 } from "path";
29524
29524
 
29525
29525
  // src/core/update.ts
29526
29526
  import { execSync as execSync2 } from "child_process";
@@ -32732,9 +32732,9 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
32732
32732
 
32733
32733
  // src/core/ws-client.ts
32734
32734
  init_wrapper();
32735
- import { randomUUID as randomUUID3 } from "crypto";
32736
- import { basename as basename7, join as join31 } from "path";
32737
- import { existsSync as existsSync16, mkdirSync as mkdirSync7 } from "fs";
32735
+ import { randomUUID as randomUUID4 } from "crypto";
32736
+ import { basename as basename7, join as join32 } from "path";
32737
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8 } from "fs";
32738
32738
 
32739
32739
  // vendor/@4pm/utils/helpers.ts
32740
32740
  function backoffJitterMs(attempt, baseMs = 500, maxMs = 1e4) {
@@ -37191,7 +37191,6 @@ var toolRuleSchema = external_exports.string().min(1).max(500);
37191
37191
  var agentToolsPermissionsSchema = external_exports.object({
37192
37192
  defaultMode: external_exports.enum(["default", "acceptEdits", "plan", "bypassPermissions"]),
37193
37193
  allow: external_exports.array(toolRuleSchema).max(500),
37194
- ask: external_exports.array(toolRuleSchema).max(500),
37195
37194
  deny: external_exports.array(toolRuleSchema).max(500)
37196
37195
  });
37197
37196
  var agentToolsReadQuerySchema = external_exports.object({
@@ -37681,6 +37680,50 @@ async function commitAndPushSubmodules(root, submodules, emit) {
37681
37680
  emit("submodule-push-failed", `Submodules committed locally but the push failed: ${msg}`);
37682
37681
  }
37683
37682
  }
37683
+ function providerSlug(url) {
37684
+ const provider = /github\.com/i.test(url) ? "gh" : /gitlab\.com/i.test(url) ? "glab" : null;
37685
+ const m2 = /(?:github|gitlab)\.com[/:]+([^/]+\/.+?)(?:\.git)?\/?$/i.exec(url);
37686
+ return { provider, slug: m2?.[1] ?? "" };
37687
+ }
37688
+ async function commitAndOpenPr(root, repo, emit) {
37689
+ emit("commit", "Committing the scaffolded project\u2026");
37690
+ await run2("git", ["add", "-A"], { cwd: root, timeout: 6e4 });
37691
+ const staged = (await run2("git", ["diff", "--cached", "--name-only"], { cwd: root, timeout: 3e4 })).stdout.trim();
37692
+ if (!staged) {
37693
+ emit("commit", "Nothing to commit \u2014 skipping the pull request.");
37694
+ return;
37695
+ }
37696
+ const commitArgs = ["commit", "-m", "chore: scaffold project (4PM)"];
37697
+ try {
37698
+ await run2("git", commitArgs, { cwd: root, timeout: 6e4 });
37699
+ } catch {
37700
+ await run2("git", ["-c", "user.name=4PM", "-c", "user.email=noreply@4pm.app", ...commitArgs], { cwd: root, timeout: 6e4 });
37701
+ }
37702
+ const branch = (repo.branch ?? "").trim();
37703
+ emit("push", "Pushing the scaffold branch\u2026");
37704
+ try {
37705
+ await run2("git", ["push", "-u", "origin", ...branch ? [branch] : ["HEAD"]], { cwd: root, timeout: 12e4 });
37706
+ } catch (err) {
37707
+ const msg = err instanceof Error ? err.message : String(err);
37708
+ emit("push-failed", `Scaffold committed locally but the push failed: ${msg} \u2014 push it from the Git tab when credentials are available.`);
37709
+ return;
37710
+ }
37711
+ const { provider, slug } = providerSlug(repo.url ?? "");
37712
+ if (!provider) {
37713
+ emit("pr", "Unknown git host \u2014 skipping the pull request.");
37714
+ return;
37715
+ }
37716
+ emit("pr", `Opening a pull request via ${provider}\u2026`);
37717
+ try {
37718
+ const args = provider === "glab" ? ["mr", "create", "--fill", ...slug ? ["-R", slug] : []] : ["pr", "create", "--fill", ...slug ? ["-R", slug] : []];
37719
+ const { stdout } = await run2(provider, args, { cwd: root, timeout: 12e4 });
37720
+ const link2 = stdout.trim().split(/\s+/).find((s3) => /^https?:\/\//.test(s3)) ?? stdout.trim().split("\n").pop() ?? "";
37721
+ emit("pr", `Pull request opened${link2 ? `: ${link2}` : "."}`);
37722
+ } catch (err) {
37723
+ const msg = err instanceof Error ? err.message : String(err);
37724
+ emit("pr-failed", `Could not open a pull request (${provider}): ${msg}`);
37725
+ }
37726
+ }
37684
37727
  function cloneArgs(url, dest, branch) {
37685
37728
  const b2 = (branch ?? "").trim();
37686
37729
  return b2 ? ["clone", "-b", b2, url, dest] : ["clone", url, dest];
@@ -37787,18 +37830,20 @@ async function scaffoldProject(payload, profileDir2, onProgress) {
37787
37830
  if (!repo) throw new Error("A project must declare one repo (ADR-0314).");
37788
37831
  emit("git", "Cloning repository\u2026");
37789
37832
  await provisionRepo(target, repo, emit, { createMissingBranch: true });
37790
- await scaffoldRepo(target, payload.projectName, payload.spec, emit);
37833
+ await scaffoldRepo(target, payload.projectName, payload.spec, emit, { resetClaude: true });
37791
37834
  await attachSubmodules(target, submodulesOf(reposOf(payload.spec)), emit, true);
37792
37835
  emit("version", "Writing .4pm/.4pm.json\u2026");
37793
37836
  await writeTemplateMarker(target);
37837
+ await commitAndOpenPr(target, repo, emit);
37794
37838
  onProgress?.({ projectId: payload.projectId, step: "done", message: "Scaffold complete.", done: true });
37795
37839
  return { ok: true, path: target };
37796
37840
  } catch (err) {
37797
37841
  return { ok: false, error: err instanceof Error ? err.message : String(err), step: lastStep };
37798
37842
  }
37799
37843
  }
37800
- async function scaffoldRepo(dir2, label, spec, emit) {
37844
+ async function scaffoldRepo(dir2, label, spec, emit, opts = {}) {
37801
37845
  await mkdir(dir2, { recursive: true });
37846
+ if (opts.resetClaude) await resetTemplateManagedClaude(dir2, label, emit);
37802
37847
  emit("copy", `Copying the sample template into ${label}\u2026`);
37803
37848
  await cp(sampleDir(), dir2, { recursive: true, force: false, errorOnExist: false });
37804
37849
  if (spec) {
@@ -37807,6 +37852,13 @@ async function scaffoldRepo(dir2, label, spec, emit) {
37807
37852
  await aiInit(dir2, spec, emit);
37808
37853
  }
37809
37854
  }
37855
+ async function resetTemplateManagedClaude(dir2, label, emit) {
37856
+ const claude = join6(dir2, ".claude");
37857
+ if (!existsSync4(claude)) return;
37858
+ emit("copy", `Resetting template .claude config in ${label}\u2026`);
37859
+ await rm(join6(claude, "settings.json"), { force: true });
37860
+ await rm(join6(claude, "skills"), { recursive: true, force: true });
37861
+ }
37810
37862
  async function writeTemplateMarker(target) {
37811
37863
  const dir2 = join6(target, ".4pm");
37812
37864
  await mkdir(dir2, { recursive: true });
@@ -37854,15 +37906,20 @@ ${specJson}`
37854
37906
  ${specJson}` + (guideInstructions ? `
37855
37907
  Also incorporate these additional instructions/content:
37856
37908
  ${guideInstructions}
37857
- ` : "")
37909
+ ` : ""),
37910
+ guideInstructions
37858
37911
  );
37859
37912
  }
37860
- async function generateFile(path, prompt) {
37913
+ async function generateFile(path, prompt, fallback) {
37861
37914
  try {
37862
37915
  const text = await aiGenerate(prompt);
37863
- if (text.trim()) await writeFile(path, text.trim() + "\n", "utf8");
37916
+ if (text.trim()) {
37917
+ await writeFile(path, text.trim() + "\n", "utf8");
37918
+ return;
37919
+ }
37864
37920
  } catch {
37865
37921
  }
37922
+ if (fallback && fallback.trim()) await writeFile(path, fallback.trim() + "\n", "utf8");
37866
37923
  }
37867
37924
  async function addProject(payload, profileDir2, onProgress) {
37868
37925
  let lastStep = "start";
@@ -38332,6 +38389,12 @@ var WsChannels = {
38332
38389
  // AI knowledge distill (ADR-0190): server → project cli request–reply. The worker runs the AI CLI
38333
38390
  // in the project's working dir with a distillation prompt and replies with the composed markdown.
38334
38391
  KNOWLEDGE_COMPOSE: "knowledge.compose",
38392
+ // Ticket → FAQ synthesis (ADR-0333): server (platform-pool dispatcher) → platform-pool cli
38393
+ // request–reply. The dispatcher sends the selected support tickets + the 4pm-faq repo with a
38394
+ // short-lived WRITE token; the worker runs an agent-write that distils the tickets into FAQ
38395
+ // entries, commits, pushes a branch, opens a PR, then wipes the token/clone, and replies with the
38396
+ // PR URL. The write token never persists on the worker (leak-safety on release/reassign).
38397
+ FAQ_COMPOSE: "faq.compose",
38335
38398
  // Shared AI memory (ADR-0245): cli → server one-way write-back of the compacted rolling memory for
38336
38399
  // the project it serves (persisted per project × machine-user link). The read direction rides
38337
38400
  // `ws_token` (seeds the cli cache on connect); this channel only carries updates.
@@ -42360,7 +42423,8 @@ async function gitRepos(root) {
42360
42423
  if (!top || !(top === base || top.startsWith(base + sep8))) return { repos: [] };
42361
42424
  const repos = [];
42362
42425
  const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
42363
- repos.push({ subdir: "", name: "", remote: rootRemote || null });
42426
+ const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
42427
+ repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
42364
42428
  const subOut = await git(base, ["submodule", "status"]);
42365
42429
  for (const line of subOut.split("\n")) {
42366
42430
  const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
@@ -42369,7 +42433,8 @@ async function gitRepos(root) {
42369
42433
  const abs = resolve8(base, subPath);
42370
42434
  if (!(abs === base || abs.startsWith(base + sep8))) continue;
42371
42435
  const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
42372
- repos.push({ subdir: subPath, name: subPath, remote: remote || null });
42436
+ const branch = (await git(abs, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
42437
+ repos.push({ subdir: subPath, name: subPath, remote: remote || null, branch: branch || null });
42373
42438
  }
42374
42439
  return { repos };
42375
42440
  }
@@ -42740,6 +42805,187 @@ async function runKnowledgeCompose(req, ai2, physicRoot) {
42740
42805
  return { bodyMarkdown: "", error: lastReason };
42741
42806
  }
42742
42807
 
42808
+ // src/core/faq-compose.ts
42809
+ import { spawn as spawn9, execFile as execFile10 } from "child_process";
42810
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, rmSync as rmSync7 } from "fs";
42811
+ import { randomUUID as randomUUID3 } from "crypto";
42812
+ import { join as join24 } from "path";
42813
+ import { tmpdir as tmpdir3 } from "os";
42814
+ import { promisify as promisify10 } from "util";
42815
+ var execFileAsync2 = promisify10(execFile10);
42816
+ var NO_USAGE2 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
42817
+ var COMPOSE_TIMEOUT_MS2 = 10 * 60 * 1e3;
42818
+ function authedUrl2(url, token) {
42819
+ if (url.startsWith("https://")) return url.replace("https://", `https://x-access-token:${token}@`);
42820
+ return url;
42821
+ }
42822
+ function buildPrompt4(tickets, customPrompt) {
42823
+ const transcript = tickets.map((t2) => {
42824
+ const thread = t2.messages.map((m2) => ` [${m2.author}] ${m2.body}`).join("\n");
42825
+ return `### Ticket ${t2.id} \u2014 ${t2.subject} (${t2.category})
42826
+ ${thread}`;
42827
+ }).join("\n\n");
42828
+ const ids = tickets.map((t2) => t2.id);
42829
+ return [
42830
+ "You are maintaining the 4PM product FAQ, stored as markdown in THIS git repository (your CWD).",
42831
+ "From the resolved support tickets below, distil reusable, product-general FAQ entries and add or",
42832
+ "update the appropriate markdown file(s) in this repo (e.g. an FAQ.md, or the closest existing",
42833
+ "topic file \u2014 inspect the repo first and match its structure/style).",
42834
+ "",
42835
+ "Rules:",
42836
+ "- Write generalized Q&A, NOT ticket-specific replies; never include customer names, emails, ids,",
42837
+ " org names or any personal data from the tickets.",
42838
+ "- Merge with existing entries instead of duplicating; keep the existing formatting and headings.",
42839
+ "- If a ticket yields no reusable FAQ value, skip it. If nothing is worth adding, make NO changes.",
42840
+ "- Only edit markdown documentation files; do not touch code, CI, or unrelated files.",
42841
+ "- Do NOT run git commit/push or open a PR yourself \u2014 just leave the edited files in the working",
42842
+ " tree; the surrounding tooling handles commit + PR.",
42843
+ ...customPrompt?.trim() ? ["", "Additional instruction from the operator (follow it too):", customPrompt.trim()] : [],
42844
+ "",
42845
+ "===== RESOLVED TICKETS =====",
42846
+ transcript,
42847
+ "",
42848
+ "===== REQUIRED FINAL OUTPUT =====",
42849
+ "After finishing all file edits, output \u2014 as the LAST thing, on its own \u2014 a single fenced JSON",
42850
+ "block summarizing what you did PER TICKET (created/updated which FAQ entry, or skipped + why):",
42851
+ "```json",
42852
+ `{"perTicket":[${ids.map((id) => `{"ticketId":"${id}","summary":"..."}`).join(",")}]}`,
42853
+ "```",
42854
+ "Include exactly one entry per ticket id above; keep each summary to one or two sentences."
42855
+ ].join("\n");
42856
+ }
42857
+ function parsePerTicket(text) {
42858
+ const idx = text.lastIndexOf('"perTicket"');
42859
+ if (idx === -1) return [];
42860
+ const open = text.lastIndexOf("{", idx);
42861
+ if (open === -1) return [];
42862
+ let depth = 0;
42863
+ for (let i = open; i < text.length; i++) {
42864
+ if (text[i] === "{") depth++;
42865
+ else if (text[i] === "}") {
42866
+ depth--;
42867
+ if (depth === 0) {
42868
+ try {
42869
+ const obj = JSON.parse(text.slice(open, i + 1));
42870
+ if (Array.isArray(obj.perTicket)) {
42871
+ return obj.perTicket.filter((e) => !!e && typeof e.ticketId === "string").map((e) => ({ ticketId: e.ticketId, summary: String(e.summary ?? "") }));
42872
+ }
42873
+ } catch {
42874
+ return [];
42875
+ }
42876
+ return [];
42877
+ }
42878
+ }
42879
+ }
42880
+ return [];
42881
+ }
42882
+ function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv) {
42883
+ return new Promise((resolve10, reject) => {
42884
+ const isClaude = cmd.includes("claude");
42885
+ const args = [
42886
+ "-p",
42887
+ ...profile?.model ? ["--model", profile.model] : [],
42888
+ ...isClaude ? ["--output-format", "stream-json", "--verbose"] : [],
42889
+ // Write-capable headless agent (ADR-0271): auto-approve tools so it never stalls on a prompt.
42890
+ ...isClaude ? ["--permission-mode", "bypassPermissions"] : [],
42891
+ ...cmd.includes("codex") ? ["--dangerously-bypass-approvals-and-sandbox"] : []
42892
+ ];
42893
+ const env3 = {
42894
+ ...process.env,
42895
+ ...extraEnv,
42896
+ ...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
42897
+ };
42898
+ const child = spawn9(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
42899
+ const parser = createAiStreamParser(cmd);
42900
+ let out = "";
42901
+ let err = "";
42902
+ const timer = setTimeout(() => {
42903
+ child.kill("SIGKILL");
42904
+ reject(new Error("faq-compose agent timed out"));
42905
+ }, COMPOSE_TIMEOUT_MS2);
42906
+ timer.unref();
42907
+ child.stdout.on("data", (d) => out += parser.push(d.toString()));
42908
+ child.stderr.on("data", (d) => err += d.toString());
42909
+ child.on("error", (e) => {
42910
+ clearTimeout(timer);
42911
+ reject(e);
42912
+ });
42913
+ child.on("close", (code) => {
42914
+ clearTimeout(timer);
42915
+ out += parser.flush();
42916
+ resolve10({ code: code ?? -1, out, err, usage: parser.usage() });
42917
+ });
42918
+ child.stdin.write(prompt);
42919
+ child.stdin.end();
42920
+ });
42921
+ }
42922
+ async function runClaudeWithFailover2(prompt, cwd2, ai2) {
42923
+ const attempts = ai2.profiles.length > 0 ? ai2.profiles : [null];
42924
+ let lastReason = "no attempt";
42925
+ for (let i = 0; i < attempts.length; i++) {
42926
+ const { code, out, err, usage } = await runClaudeOnce2(ai2.cmd, attempts[i], prompt, cwd2, ai2.env);
42927
+ if (code === 0) return { usage, text: out.trim() };
42928
+ lastReason = `exited ${code}: ${(err || out).slice(0, 500)}`;
42929
+ if (i === attempts.length - 1) break;
42930
+ }
42931
+ throw new Error(`claude ${lastReason}`);
42932
+ }
42933
+ var MAX_OUTPUT_CHARS = 8e3;
42934
+ async function runFaqCompose(req, ai2) {
42935
+ if (!req.repo.token) return { prUrl: "", error: "no write token" };
42936
+ const workDir = join24(tmpdir3(), "4pm-faq-sync", randomUUID3());
42937
+ const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
42938
+ const authed = authedUrl2(req.repo.url, req.repo.token);
42939
+ try {
42940
+ mkdirSync7(workDir, { recursive: true });
42941
+ await execFileAsync2("git", ["clone", "--depth", "1", "--branch", req.repo.branch, authed, workDir], { env: gitEnv });
42942
+ await execFileAsync2("git", ["-C", workDir, "config", "user.name", "4PM FAQ Bot"], { env: gitEnv });
42943
+ await execFileAsync2("git", ["-C", workDir, "config", "user.email", "faq-bot@4pm.app"], { env: gitEnv });
42944
+ await execFileAsync2("git", ["-C", workDir, "checkout", "-b", req.headBranch], { env: gitEnv });
42945
+ const { usage, text } = await runClaudeWithFailover2(buildPrompt4(req.tickets, req.customPrompt), workDir, ai2);
42946
+ const output = text.slice(0, MAX_OUTPUT_CHARS);
42947
+ const perTicket = parsePerTicket(text);
42948
+ await execFileAsync2("git", ["-C", workDir, "add", "-A"], { env: gitEnv });
42949
+ const staged = await execFileAsync2("git", ["-C", workDir, "diff", "--cached", "--name-only"], { env: gitEnv });
42950
+ if (!staged.stdout.trim()) {
42951
+ return { prUrl: "", output, perTicket, tokens: usage.tokens || estimateTokens2(text), finishedAt: (/* @__PURE__ */ new Date()).toISOString() };
42952
+ }
42953
+ await execFileAsync2("git", ["-C", workDir, "commit", "-m", "docs(faq): synthesize from support tickets (4PM)"], { env: gitEnv });
42954
+ await execFileAsync2("git", ["-C", workDir, "push", "-u", "origin", req.headBranch], { env: gitEnv });
42955
+ let prUrl = "";
42956
+ try {
42957
+ const pr2 = await execFileAsync2(
42958
+ "gh",
42959
+ ["pr", "create", "--fill", "--head", req.headBranch, "--base", req.repo.branch],
42960
+ { cwd: workDir, env: { ...gitEnv, GH_TOKEN: req.repo.token } }
42961
+ );
42962
+ prUrl = pr2.stdout.trim().split("\n").pop() ?? "";
42963
+ } catch (err) {
42964
+ logger.warn("faq.compose.pr.failed", { error: String(err) });
42965
+ }
42966
+ const u2 = usage.tokens > 0 ? usage : { ...NO_USAGE2 };
42967
+ return {
42968
+ prUrl,
42969
+ branch: req.headBranch,
42970
+ output,
42971
+ perTicket,
42972
+ tokens: u2.tokens,
42973
+ ...usage.tokens > 0 ? { tokensBreakdown: { input: usage.input, output: usage.output, cacheRead: usage.cacheRead, cacheCreation: usage.cacheCreation } } : {},
42974
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString()
42975
+ };
42976
+ } catch (err) {
42977
+ logger.warn("faq.compose.failed", { error: String(err) });
42978
+ return { prUrl: "", error: String(err) };
42979
+ } finally {
42980
+ if (existsSync14(workDir)) {
42981
+ try {
42982
+ rmSync7(workDir, { recursive: true, force: true });
42983
+ } catch {
42984
+ }
42985
+ }
42986
+ }
42987
+ }
42988
+
42743
42989
  // src/core/ws-client/handlers/support.ts
42744
42990
  function handleSupportChannels(ctx, message, payload) {
42745
42991
  switch (message.channel) {
@@ -42796,6 +43042,28 @@ function handleSupportChannels(ctx, message, payload) {
42796
43042
  }).finally(() => ctx.bus.endBusy("knowledge-compose"));
42797
43043
  return true;
42798
43044
  }
43045
+ case WsChannels.FAQ_COMPOSE: {
43046
+ const freq = payload;
43047
+ const fcfg = readProfileConfig(ctx.profileDir);
43048
+ const fcmd = fcfg.aiCli || "claude";
43049
+ const fai = {
43050
+ cmd: fcmd,
43051
+ profiles: resolveClaudeProfiles(fcfg, getWorkingProfile(ctx.profileDir, fcmd)),
43052
+ env: fcfg.aiEnv
43053
+ };
43054
+ ctx.bus.push({ source: "server", kind: "aireq", text: `${fcmd} \u2039 synthesize FAQ (${freq.tickets.length} tickets)` });
43055
+ ctx.bus.startBusy("faq-compose");
43056
+ void runFaqCompose(freq, fai).then((reply) => {
43057
+ if (reply.error) {
43058
+ ctx.bus.push({ source: "server", kind: "log", text: `faq-compose failed: ${reply.error}`, level: "warn" });
43059
+ } else {
43060
+ ctx.bus.push({ source: "server", kind: "aires", text: `${fcmd} \u203A ${reply.prUrl || "no changes"}` });
43061
+ }
43062
+ reportToolResult(fcmd, !reply.error, reply.error);
43063
+ ctx.send(WsChannels.FAQ_COMPOSE, reply, message.id);
43064
+ }).finally(() => ctx.bus.endBusy("faq-compose"));
43065
+ return true;
43066
+ }
42799
43067
  default:
42800
43068
  return false;
42801
43069
  }
@@ -42894,7 +43162,7 @@ function handleToolsChannels(ctx, message, payload) {
42894
43162
 
42895
43163
  // src/core/agents.ts
42896
43164
  import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, rm as rm5 } from "fs/promises";
42897
- import { join as join24 } from "path";
43165
+ import { join as join25 } from "path";
42898
43166
  var AGENTS_REL = ".claude/agents";
42899
43167
  var SKILLS_REL = ".claude/skills";
42900
43168
  var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
@@ -42918,17 +43186,17 @@ async function listAgents(root) {
42918
43186
  const subagents = [];
42919
43187
  const skills = [];
42920
43188
  try {
42921
- for (const e of await readdir4(join24(root, AGENTS_REL), { withFileTypes: true })) {
43189
+ for (const e of await readdir4(join25(root, AGENTS_REL), { withFileTypes: true })) {
42922
43190
  if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
42923
- const fm = frontmatter(await readText2(join24(root, AGENTS_REL, e.name)));
43191
+ const fm = frontmatter(await readText2(join25(root, AGENTS_REL, e.name)));
42924
43192
  subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
42925
43193
  }
42926
43194
  } catch {
42927
43195
  }
42928
43196
  try {
42929
- for (const e of await readdir4(join24(root, SKILLS_REL), { withFileTypes: true })) {
43197
+ for (const e of await readdir4(join25(root, SKILLS_REL), { withFileTypes: true })) {
42930
43198
  if (!e.isDirectory()) continue;
42931
- const fm = frontmatter(await readText2(join24(root, SKILLS_REL, e.name, "SKILL.md")));
43199
+ const fm = frontmatter(await readText2(join25(root, SKILLS_REL, e.name, "SKILL.md")));
42932
43200
  skills.push({ name: e.name, description: fm.description });
42933
43201
  }
42934
43202
  } catch {
@@ -42939,15 +43207,15 @@ async function listAgents(root) {
42939
43207
  }
42940
43208
  function itemPath(root, kind, name) {
42941
43209
  const n = safeName(name);
42942
- return kind === "subagent" ? join24(root, AGENTS_REL, `${n}.md`) : join24(root, SKILLS_REL, n, "SKILL.md");
43210
+ return kind === "subagent" ? join25(root, AGENTS_REL, `${n}.md`) : join25(root, SKILLS_REL, n, "SKILL.md");
42943
43211
  }
42944
43212
  async function readAgent(root, kind, name) {
42945
43213
  const content = await readText2(itemPath(root, kind, name));
42946
43214
  if (kind !== "subagent") return { content };
42947
43215
  const n = safeName(name);
42948
43216
  const [short, long] = await Promise.all([
42949
- readText2(join24(root, AGENTS_REL, "short-memory", `${n}.md`)),
42950
- readText2(join24(root, AGENTS_REL, "long-memory", `${n}.md`))
43217
+ readText2(join25(root, AGENTS_REL, "short-memory", `${n}.md`)),
43218
+ readText2(join25(root, AGENTS_REL, "long-memory", `${n}.md`))
42951
43219
  ]);
42952
43220
  return { content, memory: { short, long } };
42953
43221
  }
@@ -42957,12 +43225,12 @@ async function writeAgent(root, req) {
42957
43225
  try {
42958
43226
  if (req.action === "delete") {
42959
43227
  if (req.kind === "subagent") await rm5(itemPath(root, "subagent", n), { force: true });
42960
- else await rm5(join24(root, SKILLS_REL, n), { recursive: true, force: true });
43228
+ else await rm5(join25(root, SKILLS_REL, n), { recursive: true, force: true });
42961
43229
  return { ok: true };
42962
43230
  }
42963
43231
  if (req.content === void 0) return { ok: false, error: "content is required" };
42964
43232
  const path = itemPath(root, req.kind, n);
42965
- await mkdir8(join24(path, ".."), { recursive: true });
43233
+ await mkdir8(join25(path, ".."), { recursive: true });
42966
43234
  await writeFile8(path, req.content, "utf8");
42967
43235
  return { ok: true };
42968
43236
  } catch (err) {
@@ -42972,7 +43240,7 @@ async function writeAgent(root, req) {
42972
43240
 
42973
43241
  // src/core/secrets.ts
42974
43242
  import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
42975
- import { join as join25 } from "path";
43243
+ import { join as join26 } from "path";
42976
43244
  var SECURITY_REL = "AI_SECURITY.md";
42977
43245
  var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
42978
43246
  var SECRETS_REL = "project.secrets.json";
@@ -42985,7 +43253,7 @@ async function readText3(path, fallback = "") {
42985
43253
  }
42986
43254
  async function readSecretsObject(root) {
42987
43255
  try {
42988
- const v2 = JSON.parse(await readFile11(join25(root, SECRETS_REL), "utf8"));
43256
+ const v2 = JSON.parse(await readFile11(join26(root, SECRETS_REL), "utf8"));
42989
43257
  return v2 && typeof v2 === "object" ? v2 : {};
42990
43258
  } catch {
42991
43259
  return {};
@@ -43004,8 +43272,8 @@ function placeholderKeys(text) {
43004
43272
  }
43005
43273
  async function readSecrets(root) {
43006
43274
  const [security, placeholder, secrets] = await Promise.all([
43007
- readText3(join25(root, SECURITY_REL)),
43008
- readText3(join25(root, PLACEHOLDER_REL)),
43275
+ readText3(join26(root, SECURITY_REL)),
43276
+ readText3(join26(root, PLACEHOLDER_REL)),
43009
43277
  readSecretsObject(root)
43010
43278
  ]);
43011
43279
  const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
@@ -43018,14 +43286,14 @@ async function readSecrets(root) {
43018
43286
  async function writeSecrets(root, req) {
43019
43287
  try {
43020
43288
  if (req.kind === "security") {
43021
- await writeFile9(join25(root, SECURITY_REL), req.content, "utf8");
43289
+ await writeFile9(join26(root, SECURITY_REL), req.content, "utf8");
43022
43290
  } else if (req.kind === "placeholder") {
43023
- await writeFile9(join25(root, PLACEHOLDER_REL), req.content, "utf8");
43291
+ await writeFile9(join26(root, PLACEHOLDER_REL), req.content, "utf8");
43024
43292
  } else {
43025
43293
  const secrets = await readSecretsObject(root);
43026
43294
  if (req.kind === "secret") secrets[req.key] = req.value;
43027
43295
  else delete secrets[req.key];
43028
- await writeFile9(join25(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
43296
+ await writeFile9(join26(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
43029
43297
  }
43030
43298
  return { ok: true };
43031
43299
  } catch (err) {
@@ -43035,13 +43303,12 @@ async function writeSecrets(root, req) {
43035
43303
 
43036
43304
  // src/core/agent-tools.ts
43037
43305
  import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
43038
- import { dirname as dirname8, join as join26 } from "path";
43306
+ import { dirname as dirname8, join as join27 } from "path";
43039
43307
  var SHARED_REL = ".claude/settings.json";
43040
43308
  var LOCAL_REL = ".claude/settings.local.json";
43041
43309
  var SECRETS_DENY = "Read(./project.secrets.json)";
43042
- var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
43043
43310
  function fileFor(root, scope) {
43044
- return join26(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
43311
+ return join27(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
43045
43312
  }
43046
43313
  async function readSettingsObject(path) {
43047
43314
  try {
@@ -43053,9 +43320,8 @@ async function readSettingsObject(path) {
43053
43320
  }
43054
43321
  function normalizePermissions(raw) {
43055
43322
  const p2 = raw && typeof raw === "object" ? raw : {};
43056
- const mode = MODES.includes(p2.defaultMode) ? p2.defaultMode : "default";
43057
43323
  const list = (v2) => Array.isArray(v2) ? v2.filter((x) => typeof x === "string") : [];
43058
- return { defaultMode: mode, allow: list(p2.allow), ask: list(p2.ask), deny: list(p2.deny) };
43324
+ return { defaultMode: "bypassPermissions", allow: list(p2.allow), deny: list(p2.deny) };
43059
43325
  }
43060
43326
  async function readAgentTools(root, scope) {
43061
43327
  const obj = await readSettingsObject(fileFor(root, scope));
@@ -43079,7 +43345,7 @@ async function writeAgentTools(root, scope, permissions) {
43079
43345
  // src/core/packages.ts
43080
43346
  import { createHash as createHash4 } from "crypto";
43081
43347
  import { readFile as readFile13, writeFile as writeFile11, mkdir as mkdir10, readdir as readdir5, rm as rm6, stat as stat4 } from "fs/promises";
43082
- import { dirname as dirname9, join as join27, relative } from "path";
43348
+ import { dirname as dirname9, join as join28, relative } from "path";
43083
43349
  var AGENTS_REL2 = ".claude/agents";
43084
43350
  var SKILLS_REL2 = ".claude/skills";
43085
43351
  var MANIFEST_REL = ".claude/.4pm-packages.json";
@@ -43103,7 +43369,7 @@ async function listFilesRel(dir2) {
43103
43369
  return;
43104
43370
  }
43105
43371
  for (const e of entries2) {
43106
- const full = join27(d, e.name);
43372
+ const full = join28(d, e.name);
43107
43373
  if (e.isDirectory()) await walk2(full);
43108
43374
  else if (e.isFile()) out.push(relative(dir2, full));
43109
43375
  }
@@ -43114,16 +43380,16 @@ async function listFilesRel(dir2) {
43114
43380
  async function readArtifactFiles(root, kind, slug) {
43115
43381
  const files = [];
43116
43382
  if (kind === "subagent") {
43117
- const agent = await readMaybe(join27(root, AGENTS_REL2, `${slug}.md`));
43383
+ const agent = await readMaybe(join28(root, AGENTS_REL2, `${slug}.md`));
43118
43384
  if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
43119
- const short = await readMaybe(join27(root, AGENTS_REL2, "short-memory", `${slug}.md`));
43385
+ const short = await readMaybe(join28(root, AGENTS_REL2, "short-memory", `${slug}.md`));
43120
43386
  if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
43121
- const long = await readMaybe(join27(root, AGENTS_REL2, "long-memory", `${slug}.md`));
43387
+ const long = await readMaybe(join28(root, AGENTS_REL2, "long-memory", `${slug}.md`));
43122
43388
  if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
43123
43389
  } else {
43124
- const base = join27(root, SKILLS_REL2, slug);
43390
+ const base = join28(root, SKILLS_REL2, slug);
43125
43391
  for (const rel of await listFilesRel(base)) {
43126
- const buf = await readMaybe(join27(base, rel));
43392
+ const buf = await readMaybe(join28(base, rel));
43127
43393
  if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
43128
43394
  }
43129
43395
  }
@@ -43141,7 +43407,7 @@ async function artifactSha256(root, kind, slug) {
43141
43407
  return h.digest("hex");
43142
43408
  }
43143
43409
  async function artifactExists(root, kind, slug) {
43144
- const target = kind === "subagent" ? join27(root, AGENTS_REL2, `${slug}.md`) : join27(root, SKILLS_REL2, slug);
43410
+ const target = kind === "subagent" ? join28(root, AGENTS_REL2, `${slug}.md`) : join28(root, SKILLS_REL2, slug);
43145
43411
  try {
43146
43412
  await stat4(target);
43147
43413
  return true;
@@ -43150,7 +43416,7 @@ async function artifactExists(root, kind, slug) {
43150
43416
  }
43151
43417
  }
43152
43418
  async function readManifest(root) {
43153
- const buf = await readMaybe(join27(root, MANIFEST_REL));
43419
+ const buf = await readMaybe(join28(root, MANIFEST_REL));
43154
43420
  if (!buf) return {};
43155
43421
  try {
43156
43422
  return JSON.parse(buf.toString("utf8"));
@@ -43159,7 +43425,7 @@ async function readManifest(root) {
43159
43425
  }
43160
43426
  }
43161
43427
  async function writeManifest(root, manifest) {
43162
- const path = join27(root, MANIFEST_REL);
43428
+ const path = join28(root, MANIFEST_REL);
43163
43429
  await mkdir10(dirname9(path), { recursive: true });
43164
43430
  await writeFile11(path, JSON.stringify(manifest, null, 2), "utf8");
43165
43431
  }
@@ -43168,24 +43434,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
43168
43434
  if (kind === "subagent") {
43169
43435
  const agent = byPath.get("agent.md");
43170
43436
  if (agent) {
43171
- await mkdir10(join27(root, AGENTS_REL2), { recursive: true });
43172
- await writeFile11(join27(root, AGENTS_REL2, `${slug}.md`), agent);
43437
+ await mkdir10(join28(root, AGENTS_REL2), { recursive: true });
43438
+ await writeFile11(join28(root, AGENTS_REL2, `${slug}.md`), agent);
43173
43439
  }
43174
43440
  const short = byPath.get("short-memory.md");
43175
43441
  if (short) {
43176
- await mkdir10(join27(root, AGENTS_REL2, "short-memory"), { recursive: true });
43177
- await writeFile11(join27(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
43442
+ await mkdir10(join28(root, AGENTS_REL2, "short-memory"), { recursive: true });
43443
+ await writeFile11(join28(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
43178
43444
  }
43179
43445
  const long = byPath.get("long-memory.md");
43180
43446
  if (long) {
43181
- await mkdir10(join27(root, AGENTS_REL2, "long-memory"), { recursive: true });
43182
- await writeFile11(join27(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
43447
+ await mkdir10(join28(root, AGENTS_REL2, "long-memory"), { recursive: true });
43448
+ await writeFile11(join28(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
43183
43449
  }
43184
43450
  } else {
43185
- const base = join27(root, SKILLS_REL2, slug);
43451
+ const base = join28(root, SKILLS_REL2, slug);
43186
43452
  for (const [rel, buf] of byPath) {
43187
43453
  if (rel.includes("..") || rel.startsWith("/")) continue;
43188
- const dest = join27(base, rel);
43454
+ const dest = join28(base, rel);
43189
43455
  await mkdir10(dirname9(dest), { recursive: true });
43190
43456
  await writeFile11(dest, buf);
43191
43457
  }
@@ -43251,11 +43517,11 @@ async function removePackage(root, slug) {
43251
43517
  const entry = manifest[n];
43252
43518
  if (!entry) return { ok: false, error: "not installed" };
43253
43519
  if (entry.kind === "subagent") {
43254
- await rm6(join27(root, AGENTS_REL2, `${n}.md`), { force: true });
43255
- await rm6(join27(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
43256
- await rm6(join27(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
43520
+ await rm6(join28(root, AGENTS_REL2, `${n}.md`), { force: true });
43521
+ await rm6(join28(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
43522
+ await rm6(join28(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
43257
43523
  } else {
43258
- await rm6(join27(root, SKILLS_REL2, n), { recursive: true, force: true });
43524
+ await rm6(join28(root, SKILLS_REL2, n), { recursive: true, force: true });
43259
43525
  }
43260
43526
  delete manifest[n];
43261
43527
  await writeManifest(root, manifest);
@@ -43267,7 +43533,7 @@ async function removePackage(root, slug) {
43267
43533
 
43268
43534
  // src/core/graph.ts
43269
43535
  import { readFile as readFile14, readdir as readdir6 } from "fs/promises";
43270
- import { join as join28, relative as relative2, dirname as dirname10, resolve as resolve9, extname as extname2, basename as basename6 } from "path";
43536
+ import { join as join29, relative as relative2, dirname as dirname10, resolve as resolve9, extname as extname2, basename as basename6 } from "path";
43271
43537
  var MAX_FILES = 2e3;
43272
43538
  var MAX_NODES = 3e3;
43273
43539
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
@@ -43287,7 +43553,7 @@ async function walk(root, accept) {
43287
43553
  if (e.name.startsWith(".") && e.name !== ".claude") {
43288
43554
  if (e.isDirectory()) continue;
43289
43555
  }
43290
- const full = join28(dir2, e.name);
43556
+ const full = join29(dir2, e.name);
43291
43557
  if (e.isDirectory()) {
43292
43558
  if (!SKIP_DIRS.has(e.name)) stack.push(full);
43293
43559
  } else if (accept(full)) {
@@ -43407,12 +43673,12 @@ async function buildGraph(root, mode) {
43407
43673
 
43408
43674
  // src/core/rag.ts
43409
43675
  import { readFile as readFile15, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
43410
- import { existsSync as existsSync14 } from "fs";
43411
- import { execFile as execFile10, spawn as spawn9 } from "child_process";
43676
+ import { existsSync as existsSync15 } from "fs";
43677
+ import { execFile as execFile11, spawn as spawn10 } from "child_process";
43412
43678
  import { totalmem as totalmem2, cpus as cpus2 } from "os";
43413
- import { join as join29 } from "path";
43414
- import { promisify as promisify10 } from "util";
43415
- var run10 = promisify10(execFile10);
43679
+ import { join as join30 } from "path";
43680
+ import { promisify as promisify11 } from "util";
43681
+ var run10 = promisify11(execFile11);
43416
43682
  var RAG_REL = ".claude/rag";
43417
43683
  async function tryRun(cmd, args) {
43418
43684
  try {
@@ -43431,7 +43697,7 @@ async function detectPython() {
43431
43697
  }
43432
43698
  async function installLog(root) {
43433
43699
  try {
43434
- const text = await readFile15(join29(root, RAG_REL, "install.log"), "utf8");
43700
+ const text = await readFile15(join30(root, RAG_REL, "install.log"), "utf8");
43435
43701
  return text.split("\n").filter((l) => l.length > 0).slice(-200);
43436
43702
  } catch {
43437
43703
  return [];
@@ -43493,14 +43759,14 @@ async function ragStatus(root) {
43493
43759
  };
43494
43760
  let modelName = "";
43495
43761
  try {
43496
- modelName = (await readFile15(join29(root, RAG_REL, ".installed"), "utf8")).trim();
43762
+ modelName = (await readFile15(join30(root, RAG_REL, ".installed"), "utf8")).trim();
43497
43763
  } catch {
43498
43764
  modelName = "";
43499
43765
  }
43500
43766
  const modelPresent = modelName.length > 0;
43501
43767
  return {
43502
43768
  installed: fastembed && sqliteVec && modelPresent,
43503
- installing: existsSync14(join29(root, RAG_REL, ".installing")),
43769
+ installing: existsSync15(join30(root, RAG_REL, ".installing")),
43504
43770
  python: { found: py.found, version: py.version },
43505
43771
  deps: { pip, fastembed, sqliteVec },
43506
43772
  model: { present: modelPresent, name: modelName },
@@ -43511,9 +43777,9 @@ async function ragStatus(root) {
43511
43777
  };
43512
43778
  }
43513
43779
  async function readIndexMeta(root) {
43514
- const indexing = existsSync14(join29(root, RAG_REL, ".indexing"));
43780
+ const indexing = existsSync15(join30(root, RAG_REL, ".indexing"));
43515
43781
  try {
43516
- const m2 = JSON.parse(await readFile15(join29(root, RAG_REL, ".index.json"), "utf8"));
43782
+ const m2 = JSON.parse(await readFile15(join30(root, RAG_REL, ".index.json"), "utf8"));
43517
43783
  return {
43518
43784
  present: true,
43519
43785
  indexing,
@@ -43580,14 +43846,14 @@ if __name__ == "__main__":
43580
43846
  elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
43581
43847
  `;
43582
43848
  async function ensureEngine(root) {
43583
- await mkdir11(join29(root, RAG_REL), { recursive: true }).catch(() => void 0);
43584
- await writeFile12(join29(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
43849
+ await mkdir11(join30(root, RAG_REL), { recursive: true }).catch(() => void 0);
43850
+ await writeFile12(join30(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
43585
43851
  }
43586
43852
  async function ragReindex(root) {
43587
43853
  const py = await detectPython();
43588
43854
  if (!py.found) return { ok: false, started: false, error: "python3 not found" };
43589
- if (!existsSync14(join29(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
43590
- if (existsSync14(join29(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
43855
+ if (!existsSync15(join30(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
43856
+ if (existsSync15(join30(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
43591
43857
  await ensureEngine(root);
43592
43858
  const script = `
43593
43859
  set +e
@@ -43595,7 +43861,7 @@ touch "${RAG_REL}/.indexing"
43595
43861
  { echo "[reindex] $(date)"; "$RAG_PY" "${RAG_REL}/rag_engine.py" reindex 2>&1; } >> "${RAG_REL}/index.log" 2>&1
43596
43862
  rm -f "${RAG_REL}/.indexing"
43597
43863
  `;
43598
- const child = spawn9("bash", ["-c", script], {
43864
+ const child = spawn10("bash", ["-c", script], {
43599
43865
  cwd: root,
43600
43866
  env: { ...process.env, RAG_PY: py.bin },
43601
43867
  detached: true,
@@ -43607,12 +43873,12 @@ rm -f "${RAG_REL}/.indexing"
43607
43873
  async function ragQuery(root, queryText, k2 = 8) {
43608
43874
  const py = await detectPython();
43609
43875
  if (!py.found) return { results: [], error: "python3 not found" };
43610
- if (!existsSync14(join29(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
43876
+ if (!existsSync15(join30(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
43611
43877
  await ensureEngine(root);
43612
43878
  try {
43613
43879
  const { stdout } = await run10(
43614
43880
  py.bin,
43615
- [join29(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
43881
+ [join30(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
43616
43882
  { cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
43617
43883
  );
43618
43884
  const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
@@ -43627,9 +43893,9 @@ async function ragInstall(root, model) {
43627
43893
  }
43628
43894
  const py = await detectPython();
43629
43895
  if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
43630
- if (existsSync14(join29(root, RAG_REL, ".installing"))) return { ok: true, started: true };
43896
+ if (existsSync15(join30(root, RAG_REL, ".installing"))) return { ok: true, started: true };
43631
43897
  try {
43632
- await mkdir11(join29(root, RAG_REL), { recursive: true });
43898
+ await mkdir11(join30(root, RAG_REL), { recursive: true });
43633
43899
  } catch {
43634
43900
  }
43635
43901
  const script = `
@@ -43645,7 +43911,7 @@ rm -f "${RAG_REL}/.installed"
43645
43911
  } >> "${RAG_REL}/install.log" 2>&1
43646
43912
  rm -f "${RAG_REL}/.installing"
43647
43913
  `;
43648
- const child = spawn9("bash", ["-c", script], {
43914
+ const child = spawn10("bash", ["-c", script], {
43649
43915
  cwd: root,
43650
43916
  env: { ...process.env, RAG_PY: py.bin, RAG_MODEL: model },
43651
43917
  detached: true,
@@ -43656,8 +43922,8 @@ rm -f "${RAG_REL}/.installing"
43656
43922
  }
43657
43923
 
43658
43924
  // src/core/config-sync.ts
43659
- import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
43660
- import { join as join30 } from "path";
43925
+ import { existsSync as existsSync16, readFileSync as readFileSync16 } from "fs";
43926
+ import { join as join31 } from "path";
43661
43927
  var SERVER_MANAGED_KEYS = [
43662
43928
  "physicPath",
43663
43929
  "sessionSwitchPct",
@@ -43674,8 +43940,8 @@ var SERVER_MANAGED_KEYS = [
43674
43940
  "projectAiMemoryBudgetChars"
43675
43941
  ];
43676
43942
  function readConfigText(profileDir2) {
43677
- const path = join30(profileDir2, "config.json");
43678
- if (existsSync15(path)) {
43943
+ const path = join31(profileDir2, "config.json");
43944
+ if (existsSync16(path)) {
43679
43945
  try {
43680
43946
  return readFileSync16(path, "utf8");
43681
43947
  } catch {
@@ -44464,7 +44730,7 @@ var WsClient = class {
44464
44730
  }
44465
44731
  const { payload, nonce } = encryptPayload(this.sessionKey, data);
44466
44732
  const envelope = {
44467
- id: randomUUID3(),
44733
+ id: randomUUID4(),
44468
44734
  replyTo,
44469
44735
  channel,
44470
44736
  wsToken: this.wsToken,
@@ -44485,7 +44751,7 @@ var WsClient = class {
44485
44751
  }
44486
44752
  const { payload, nonce } = encryptPayload(this.sessionKey, data);
44487
44753
  const envelope = {
44488
- id: randomUUID3(),
44754
+ id: randomUUID4(),
44489
44755
  replyTo: null,
44490
44756
  channel,
44491
44757
  wsToken: this.wsToken,
@@ -44552,8 +44818,8 @@ var WsClient = class {
44552
44818
  /** Create the physic folder at an absolute path if missing (best-effort). */
44553
44819
  ensurePhysicFolderPath(folder) {
44554
44820
  try {
44555
- if (!existsSync16(folder)) {
44556
- mkdirSync7(folder, { recursive: true });
44821
+ if (!existsSync17(folder)) {
44822
+ mkdirSync8(folder, { recursive: true });
44557
44823
  this.bus.log(t("project.createdFolder", { folder }));
44558
44824
  }
44559
44825
  } catch (err) {
@@ -44567,7 +44833,7 @@ var WsClient = class {
44567
44833
  */
44568
44834
  physicFolderPath(projectName) {
44569
44835
  const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
44570
- return safe ? join31(this.context.profileDir, safe) : null;
44836
+ return safe ? join32(this.context.profileDir, safe) : null;
44571
44837
  }
44572
44838
  /**
44573
44839
  * Report machine.status: version + the state of the physic project being served
@@ -44582,7 +44848,7 @@ var WsClient = class {
44582
44848
  projects: [],
44583
44849
  version: CLI_VERSION,
44584
44850
  physicPath,
44585
- physicPathExists: physicPath ? existsSync16(physicPath) : void 0,
44851
+ physicPathExists: physicPath ? existsSync17(physicPath) : void 0,
44586
44852
  // Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
44587
44853
  autonomousRunning: physicPath ? isAutonomousRunning(physicPath, this.context.profileDir) : false,
44588
44854
  // The machine-user AI-run limit (0 = unlimited) + profile count so a dispatch can resolve the
@@ -44676,7 +44942,7 @@ var WsClient = class {
44676
44942
  */
44677
44943
  uploadLog() {
44678
44944
  try {
44679
- const upload = readLogUpload(join31(this.context.profileDir, "logs"));
44945
+ const upload = readLogUpload(join32(this.context.profileDir, "logs"));
44680
44946
  if (!upload) return;
44681
44947
  const sig = `${upload.fileName}:${upload.totalBytes}`;
44682
44948
  if (sig === this.lastLogSig) return;
@@ -44838,10 +45104,10 @@ var WsClient = class {
44838
45104
  };
44839
45105
 
44840
45106
  // src/core/instance-lock.ts
44841
- import { readFileSync as readFileSync17, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "fs";
44842
- import { join as join32 } from "path";
45107
+ import { readFileSync as readFileSync17, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
45108
+ import { join as join33 } from "path";
44843
45109
  function lockPath(profileDir2) {
44844
- return join32(profileDir2, "start.lock");
45110
+ return join33(profileDir2, "start.lock");
44845
45111
  }
44846
45112
  function isAlive(pid) {
44847
45113
  try {
@@ -44867,14 +45133,14 @@ function acquireInstanceLock(profileDir2) {
44867
45133
  function releaseInstanceLock(profileDir2) {
44868
45134
  try {
44869
45135
  const pid = Number.parseInt(readFileSync17(lockPath(profileDir2), "utf8").trim(), 10);
44870
- if (pid === process.pid) rmSync7(lockPath(profileDir2), { force: true });
45136
+ if (pid === process.pid) rmSync8(lockPath(profileDir2), { force: true });
44871
45137
  } catch {
44872
45138
  }
44873
45139
  }
44874
45140
 
44875
45141
  // src/core/session-bus.ts
44876
45142
  import { EventEmitter } from "events";
44877
- import { randomUUID as randomUUID4 } from "crypto";
45143
+ import { randomUUID as randomUUID5 } from "crypto";
44878
45144
  var REPLAY_CAP = 500;
44879
45145
  var SessionBus = class {
44880
45146
  emitter = new EventEmitter();
@@ -44905,7 +45171,7 @@ var SessionBus = class {
44905
45171
  /** Producer → consumers: push one transcript entry. Returns its id (for `updateEntry`). */
44906
45172
  push(input) {
44907
45173
  const entry = {
44908
- id: randomUUID4(),
45174
+ id: randomUUID5(),
44909
45175
  level: "info",
44910
45176
  ...input,
44911
45177
  ts: Date.now()
@@ -45162,8 +45428,8 @@ var SessionBus = class {
45162
45428
 
45163
45429
  // src/core/control-server.ts
45164
45430
  import { createServer } from "net";
45165
- import { chmodSync as chmodSync4, rmSync as rmSync8 } from "fs";
45166
- import { join as join34 } from "path";
45431
+ import { chmodSync as chmodSync4, rmSync as rmSync9 } from "fs";
45432
+ import { join as join35 } from "path";
45167
45433
 
45168
45434
  // src/core/control-protocol.ts
45169
45435
  var CONTROL_SOCKET_FILE = "control.sock";
@@ -45194,9 +45460,9 @@ function createFrameParser() {
45194
45460
  // src/core/control-token.ts
45195
45461
  import { chmodSync as chmodSync3, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
45196
45462
  import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
45197
- import { join as join33 } from "path";
45463
+ import { join as join34 } from "path";
45198
45464
  function controlTokenPath(profileDir2) {
45199
- return join33(profileDir2, CONTROL_TOKEN_FILE);
45465
+ return join34(profileDir2, CONTROL_TOKEN_FILE);
45200
45466
  }
45201
45467
  function writeControlToken(profileDir2) {
45202
45468
  const token = randomBytes3(32).toString("hex");
@@ -45225,9 +45491,9 @@ function tokenMatches(expected, given) {
45225
45491
 
45226
45492
  // src/core/control-server.ts
45227
45493
  function startControlServer(bus, profileDir2, info) {
45228
- const socketPath = join34(profileDir2, CONTROL_SOCKET_FILE);
45494
+ const socketPath = join35(profileDir2, CONTROL_SOCKET_FILE);
45229
45495
  try {
45230
- rmSync8(socketPath, { force: true });
45496
+ rmSync9(socketPath, { force: true });
45231
45497
  } catch {
45232
45498
  }
45233
45499
  const clients = /* @__PURE__ */ new Set();
@@ -45313,8 +45579,8 @@ function startControlServer(bus, profileDir2, info) {
45313
45579
  clients.clear();
45314
45580
  server.close();
45315
45581
  try {
45316
- rmSync8(socketPath, { force: true });
45317
- rmSync8(controlTokenPath(profileDir2), { force: true });
45582
+ rmSync9(socketPath, { force: true });
45583
+ rmSync9(controlTokenPath(profileDir2), { force: true });
45318
45584
  } catch {
45319
45585
  }
45320
45586
  };
@@ -50977,15 +51243,15 @@ var import_react21 = __toESM(require_react(), 1);
50977
51243
  var import_react26 = __toESM(require_react(), 1);
50978
51244
 
50979
51245
  // src/core/input-history.ts
50980
- import { existsSync as existsSync18, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
50981
- import { join as join35 } from "path";
51246
+ import { existsSync as existsSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
51247
+ import { join as join36 } from "path";
50982
51248
  var MAX_HISTORY = 200;
50983
51249
  function historyPath(profileDir2) {
50984
- return join35(profileDir2, "input-history.json");
51250
+ return join36(profileDir2, "input-history.json");
50985
51251
  }
50986
51252
  function loadInputHistory(profileDir2) {
50987
51253
  const path = historyPath(profileDir2);
50988
- if (!existsSync18(path)) return [];
51254
+ if (!existsSync19(path)) return [];
50989
51255
  try {
50990
51256
  const data = JSON.parse(readFileSync20(path, "utf8"));
50991
51257
  return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
@@ -51595,7 +51861,7 @@ async function runStart(profileDir2, profileName) {
51595
51861
  return;
51596
51862
  }
51597
51863
  logger.configure({
51598
- dir: join36(profileDir2, "logs"),
51864
+ dir: join37(profileDir2, "logs"),
51599
51865
  level: resolveLogLevel(),
51600
51866
  base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
51601
51867
  });
@@ -51615,7 +51881,7 @@ async function runStart(profileDir2, profileName) {
51615
51881
  if (result.action === "updated") {
51616
51882
  logger.info("update.updated", { version: result.version });
51617
51883
  console.log(t("start.restarting"));
51618
- const child = spawn10(process.execPath, process.argv.slice(1), {
51884
+ const child = spawn11(process.execPath, process.argv.slice(1), {
51619
51885
  stdio: "inherit",
51620
51886
  env: { ...process.env, FOURPM_NO_UPDATE: "1" }
51621
51887
  });
@@ -51704,7 +51970,7 @@ async function runStart(profileDir2, profileName) {
51704
51970
  }
51705
51971
 
51706
51972
  // src/commands/attach.ts
51707
- import { join as join37 } from "path";
51973
+ import { join as join38 } from "path";
51708
51974
 
51709
51975
  // src/core/control-client.ts
51710
51976
  import { connect as connect2 } from "net";
@@ -51813,7 +52079,7 @@ async function runAttach(profileDir2, profileName) {
51813
52079
  process.exitCode = 1;
51814
52080
  return;
51815
52081
  }
51816
- const socketPath = join37(profileDir2, CONTROL_SOCKET_FILE);
52082
+ const socketPath = join38(profileDir2, CONTROL_SOCKET_FILE);
51817
52083
  const bus = new SessionBus();
51818
52084
  let conn;
51819
52085
  try {
@@ -51844,11 +52110,11 @@ async function runAttach(profileDir2, profileName) {
51844
52110
 
51845
52111
  // src/commands/auto-run.ts
51846
52112
  import { connect as connect3 } from "net";
51847
- import { join as join38 } from "path";
52113
+ import { join as join39 } from "path";
51848
52114
  var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
51849
52115
  async function runAutoRun(profileDir2, profileName) {
51850
52116
  initI18n(readProfileConfig(profileDir2).locale);
51851
- const socketPath = join38(profileDir2, CONTROL_SOCKET_FILE);
52117
+ const socketPath = join39(profileDir2, CONTROL_SOCKET_FILE);
51852
52118
  const token = readControlToken(profileDir2);
51853
52119
  const code = await new Promise((resolve10) => {
51854
52120
  let settled = false;
@@ -51904,12 +52170,12 @@ async function runAutoRun(profileDir2, profileName) {
51904
52170
  }
51905
52171
 
51906
52172
  // src/commands/ai-login.ts
51907
- import { spawn as spawn11 } from "child_process";
51908
- import { mkdirSync as mkdirSync8 } from "fs";
52173
+ import { spawn as spawn12 } from "child_process";
52174
+ import { mkdirSync as mkdirSync9 } from "fs";
51909
52175
  import { basename as basename8 } from "path";
51910
52176
  function runProviderLogin(cmd, envVar, dir2) {
51911
52177
  return new Promise((resolve10) => {
51912
- const child = spawn11(cmd, [], {
52178
+ const child = spawn12(cmd, [], {
51913
52179
  stdio: "inherit",
51914
52180
  env: { ...process.env, [envVar]: dir2 }
51915
52181
  });
@@ -51942,7 +52208,7 @@ async function runAiLogin(profileDir2, profileName, aiFilter) {
51942
52208
  continue;
51943
52209
  }
51944
52210
  const dir2 = resolveHomePath(cred.profile);
51945
- mkdirSync8(dir2, { recursive: true });
52211
+ mkdirSync9(dir2, { recursive: true });
51946
52212
  console.log(t("aiLogin.loggingIn", { provider: cred.provider, profile: cred.profile, envVar, dir: dir2 }));
51947
52213
  console.log(t("aiLogin.completeSignIn"));
51948
52214
  const code = await runProviderLogin(cred.provider, envVar, dir2);
@@ -51954,7 +52220,7 @@ async function runAiLogin(profileDir2, profileName, aiFilter) {
51954
52220
  }
51955
52221
 
51956
52222
  // src/commands/unlink.ts
51957
- import { rmSync as rmSync9 } from "fs";
52223
+ import { rmSync as rmSync10 } from "fs";
51958
52224
  async function runUnlink(profileDir2, profileName) {
51959
52225
  initI18n(readProfileConfig(profileDir2).locale);
51960
52226
  const cred = readCredential(profileDir2, profileName);
@@ -51968,7 +52234,7 @@ async function runUnlink(profileDir2, profileName) {
51968
52234
  } catch (err) {
51969
52235
  console.log(t("unlink.revokeFailed", { message: err.message }));
51970
52236
  }
51971
- rmSync9(profileDir2, { recursive: true, force: true });
52237
+ rmSync10(profileDir2, { recursive: true, force: true });
51972
52238
  clearDefaultProfileIf(profileName);
51973
52239
  console.log(t("unlink.deleted"));
51974
52240
  }
@@ -8,7 +8,6 @@
8
8
  "permissions": {
9
9
  "defaultMode": "bypassPermissions",
10
10
  "allow": [],
11
- "ask": [],
12
11
  "deny": []
13
12
  },
14
13
  "enableAllProjectMcpServers": true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4pm/cli",
3
- "version": "1.19.7",
3
+ "version": "1.20.0b",
4
4
  "private": false,
5
5
  "description": "4PM CLI — drives Claude/Codex CLIs on AI worker machines (Node + TypeScript)",
6
6
  "license": "LicenseRef-4PM-Source-Available",