@4pm/cli 1.5.42 → 1.8.4

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 (2) hide show
  1. package/dist/index.js +108 -29
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28635,6 +28635,37 @@ function isValidRoleSet(roles) {
28635
28635
  return true;
28636
28636
  }
28637
28637
 
28638
+ // vendor/@4pm/constants/worker-pool.ts
28639
+ var AiJob = {
28640
+ /** Spec field/section/subagent suggest (one-shot). */
28641
+ SPEC_SUGGEST: "spec-suggest",
28642
+ /** Spec review — advisory (one-shot). */
28643
+ SPEC_REVIEW: "spec-review",
28644
+ /** Spec compose + create verdict (one-shot). */
28645
+ SPEC_COMPOSE: "spec-compose",
28646
+ /** Generators: WBS/est/proposal/feasibility/report (one-shot). */
28647
+ DOC_GENERATE: "doc-generate",
28648
+ /** Console prompt — full agent. */
28649
+ CONSOLE_AGENT: "console-agent",
28650
+ /** Git merge / AI conflict resolve — full agent. */
28651
+ GIT_MERGE: "git-merge",
28652
+ /** Graph add-link/add-code/delete-unused advice (one-shot). */
28653
+ GRAPH_SUGGEST: "graph-suggest",
28654
+ /** Subagent/skill content generate (one-shot). */
28655
+ AGENTS_GENERATE: "agents-generate",
28656
+ /** Template "Analyze impact" report — read-only agent. */
28657
+ ANALYZE_IMPACT: "analyze-impact",
28658
+ /** Template "Update" → branch + PR — write-capable agent. */
28659
+ TEMPLATE_UPDATE: "template-update",
28660
+ /** Autonomous-mode self-spawn — full agent. */
28661
+ AUTONOMOUS: "autonomous",
28662
+ /** Memo → community post compose (vision) — full agent. */
28663
+ MEMO_COMPOSE: "memo-compose",
28664
+ /** Outbound input review — the reviewer cli (one-shot). */
28665
+ OUTBOUND_REVIEW: "outbound-review"
28666
+ };
28667
+ var AI_JOBS = Object.values(AiJob);
28668
+
28638
28669
  // vendor/@4pm/constants/worker-tools.ts
28639
28670
  var WORKER_TOOL_CATALOG = [
28640
28671
  { id: "git", label: "Git", category: "vcs", versionArg: "--version", installPackage: null, installable: false, updatable: false },
@@ -29636,7 +29667,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
29636
29667
  init_wrapper();
29637
29668
  import { randomUUID as randomUUID2 } from "crypto";
29638
29669
  import { basename as basename7, join as join28 } from "path";
29639
- import { existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
29670
+ import { existsSync as existsSync16, mkdirSync as mkdirSync7 } from "fs";
29640
29671
 
29641
29672
  // vendor/@4pm/utils/helpers.ts
29642
29673
  function backoffJitterMs(attempt, baseMs = 500, maxMs = 1e4) {
@@ -33828,7 +33859,15 @@ var dispatchCommandRequestSchema = external_exports.object({
33828
33859
  * "AI settings" modal (per-user localStorage), layered over the profile default by the cli.
33829
33860
  * Only meaningful with `ai:true`.
33830
33861
  */
33831
- aiConfig: aiRunConfigSchema.optional()
33862
+ aiConfig: aiRunConfigSchema.optional(),
33863
+ /**
33864
+ * AI job (ADR-0284): which of the 13 AI functions this dispatch is — the caller stamps it (the
33865
+ * end user never picks). With `pick:"idle"` it drives the job-aware routing over the project's
33866
+ * worker-routing table (pools before lone machines → job priority → member index, with
33867
+ * exhaustion failover). Absent ⇒ the flat idle pool (backward compatible). Ignored with an
33868
+ * explicit `machineLinkId`.
33869
+ */
33870
+ job: external_exports.enum(AI_JOBS).optional()
33832
33871
  }).superRefine((v, ctx) => {
33833
33872
  if (v.machineLinkId == null === (v.pick == null)) {
33834
33873
  ctx.addIssue({
@@ -38100,29 +38139,54 @@ function resolveInRoot5(root, relPath) {
38100
38139
  const target = resolve6(base, relPath || ".");
38101
38140
  return target === base || target.startsWith(base + sep7) ? target : null;
38102
38141
  }
38142
+ function looksBinary(buf) {
38143
+ return buf.subarray(0, 8e3).includes(0);
38144
+ }
38145
+ function cappedBase64(buf) {
38146
+ if (!buf || buf.byteLength > FS_TRANSFER_MAX_BYTES) return "";
38147
+ return buf.toString("base64");
38148
+ }
38103
38149
  async function gitDiff(root, path) {
38104
38150
  const target = resolveInRoot5(root, path);
38105
38151
  if (!target) {
38106
- return { path, oldContent: "", newContent: "" };
38152
+ return { path, oldContent: "", newContent: "", isBinary: false };
38107
38153
  }
38108
38154
  const dir2 = dirname6(target);
38109
38155
  const base = basename5(target);
38110
- let newContent = "";
38156
+ let newBuf = null;
38111
38157
  try {
38112
- newContent = await readFile5(target, "utf8");
38158
+ newBuf = await readFile5(target);
38113
38159
  } catch {
38114
38160
  }
38115
- let oldContent = "";
38161
+ let oldBuf = null;
38116
38162
  try {
38117
38163
  const { stdout } = await run4("git", ["show", `HEAD:./${base}`], {
38118
38164
  cwd: dir2,
38119
38165
  timeout: 15e3,
38120
- maxBuffer: 4 * 1024 * 1024
38166
+ maxBuffer: FS_TRANSFER_MAX_BYTES + 1024,
38167
+ encoding: "buffer"
38121
38168
  });
38122
- oldContent = stdout;
38169
+ oldBuf = stdout;
38123
38170
  } catch {
38124
38171
  }
38125
- return { path: target, oldContent, newContent };
38172
+ const sample = newBuf ?? oldBuf;
38173
+ if (sample && looksBinary(sample)) {
38174
+ return {
38175
+ path: target,
38176
+ oldContent: "",
38177
+ newContent: "",
38178
+ isBinary: true,
38179
+ contentType: guessMime(target),
38180
+ oldContentBase64: cappedBase64(oldBuf),
38181
+ newContentBase64: cappedBase64(newBuf)
38182
+ };
38183
+ }
38184
+ return {
38185
+ path: target,
38186
+ oldContent: oldBuf ? oldBuf.toString("utf8") : "",
38187
+ newContent: newBuf ? newBuf.toString("utf8") : "",
38188
+ isBinary: false
38189
+ };
38126
38190
  }
38127
38191
 
38128
38192
  // src/core/git-env.ts
@@ -38574,7 +38638,7 @@ function handleMiscChannels(ctx, message, payload) {
38574
38638
  }
38575
38639
 
38576
38640
  // src/core/ws-client/handlers/project.ts
38577
- import { mkdirSync as mkdirSync6, rmSync as rmSync6 } from "fs";
38641
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, renameSync, rmSync as rmSync6 } from "fs";
38578
38642
  import { join as join20 } from "path";
38579
38643
 
38580
38644
  // src/core/scaffold.ts
@@ -38597,6 +38661,10 @@ async function provisionRepos(target, repos, emit, opts = {}) {
38597
38661
  for (const repo of repos) {
38598
38662
  if (repo.primary) {
38599
38663
  if (opts.clonePrimary && repo.url) {
38664
+ if (existsSync12(join19(target, ".git"))) {
38665
+ emit("git", `Primary repo already present \u2014 skipping clone.`);
38666
+ continue;
38667
+ }
38600
38668
  emit("git", `Cloning ${repo.url}\u2026`);
38601
38669
  await run9("git", ["clone", repo.url, target], { timeout: 12e4 });
38602
38670
  continue;
@@ -38612,6 +38680,10 @@ async function provisionRepos(target, repos, emit, opts = {}) {
38612
38680
  }
38613
38681
  const dir2 = join19(target, subDirName(repo));
38614
38682
  if (repo.url) {
38683
+ if (existsSync12(join19(dir2, ".git"))) {
38684
+ emit("git", `Sub-repo ${subDirName(repo)} already present \u2014 skipping clone.`);
38685
+ continue;
38686
+ }
38615
38687
  emit("git", `Cloning sub-repo ${repo.url}\u2026`);
38616
38688
  await run9("git", ["clone", repo.url, dir2], { timeout: 12e4 });
38617
38689
  } else {
@@ -38727,12 +38799,19 @@ function handleProjectChannels(ctx, message, payload) {
38727
38799
  switch (message.channel) {
38728
38800
  case WsChannels.PHYSIC_SYNC: {
38729
38801
  const sync = payload;
38730
- if (sync.oldName) {
38731
- const oldRoot = ctx.physicFolderPath(sync.oldName);
38802
+ const oldName = sync.oldName;
38803
+ const oldPath = oldName ? join20(ctx.profileDir, oldName) : null;
38804
+ const newPath = join20(ctx.profileDir, sync.newName);
38805
+ if (oldName) {
38806
+ const oldRoot = ctx.physicFolderPath(oldName);
38732
38807
  if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
38733
38808
  }
38734
- if (sync.oldName) rmSync6(join20(ctx.profileDir, sync.oldName), { recursive: true, force: true });
38735
- mkdirSync6(join20(ctx.profileDir, sync.newName), { recursive: true });
38809
+ if (oldPath && oldName !== sync.newName && existsSync13(oldPath) && !existsSync13(newPath)) {
38810
+ renameSync(oldPath, newPath);
38811
+ } else {
38812
+ if (oldPath && oldName !== sync.newName && existsSync13(oldPath)) rmSync6(oldPath, { recursive: true, force: true });
38813
+ mkdirSync6(newPath, { recursive: true });
38814
+ }
38736
38815
  ctx.physicRoot = ctx.physicFolderPath(sync.newName);
38737
38816
  ctx.bus.setProject(sync.newName);
38738
38817
  ctx.bus.log(t("project.folderSynced", { name: sync.newName }));
@@ -39523,7 +39602,7 @@ async function buildGraph(root, mode) {
39523
39602
 
39524
39603
  // src/core/rag.ts
39525
39604
  import { readFile as readFile12, writeFile as writeFile10, statfs as statfs2, mkdir as mkdir10 } from "fs/promises";
39526
- import { existsSync as existsSync13 } from "fs";
39605
+ import { existsSync as existsSync14 } from "fs";
39527
39606
  import { execFile as execFile10, spawn as spawn9 } from "child_process";
39528
39607
  import { totalmem as totalmem2, cpus as cpus2 } from "os";
39529
39608
  import { join as join26 } from "path";
@@ -39616,7 +39695,7 @@ async function ragStatus(root) {
39616
39695
  const modelPresent = modelName.length > 0;
39617
39696
  return {
39618
39697
  installed: fastembed && sqliteVec && modelPresent,
39619
- installing: existsSync13(join26(root, RAG_REL, ".installing")),
39698
+ installing: existsSync14(join26(root, RAG_REL, ".installing")),
39620
39699
  python: { found: py.found, version: py.version },
39621
39700
  deps: { pip, fastembed, sqliteVec },
39622
39701
  model: { present: modelPresent, name: modelName },
@@ -39627,7 +39706,7 @@ async function ragStatus(root) {
39627
39706
  };
39628
39707
  }
39629
39708
  async function readIndexMeta(root) {
39630
- const indexing = existsSync13(join26(root, RAG_REL, ".indexing"));
39709
+ const indexing = existsSync14(join26(root, RAG_REL, ".indexing"));
39631
39710
  try {
39632
39711
  const m = JSON.parse(await readFile12(join26(root, RAG_REL, ".index.json"), "utf8"));
39633
39712
  return {
@@ -39702,8 +39781,8 @@ async function ensureEngine(root) {
39702
39781
  async function ragReindex(root) {
39703
39782
  const py = await detectPython();
39704
39783
  if (!py.found) return { ok: false, started: false, error: "python3 not found" };
39705
- if (!existsSync13(join26(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
39706
- if (existsSync13(join26(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
39784
+ if (!existsSync14(join26(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
39785
+ if (existsSync14(join26(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
39707
39786
  await ensureEngine(root);
39708
39787
  const script = `
39709
39788
  set +e
@@ -39723,7 +39802,7 @@ rm -f "${RAG_REL}/.indexing"
39723
39802
  async function ragQuery(root, queryText, k = 8) {
39724
39803
  const py = await detectPython();
39725
39804
  if (!py.found) return { results: [], error: "python3 not found" };
39726
- if (!existsSync13(join26(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
39805
+ if (!existsSync14(join26(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
39727
39806
  await ensureEngine(root);
39728
39807
  try {
39729
39808
  const { stdout } = await run10(
@@ -39743,7 +39822,7 @@ async function ragInstall(root, model) {
39743
39822
  }
39744
39823
  const py = await detectPython();
39745
39824
  if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
39746
- if (existsSync13(join26(root, RAG_REL, ".installing"))) return { ok: true, started: true };
39825
+ if (existsSync14(join26(root, RAG_REL, ".installing"))) return { ok: true, started: true };
39747
39826
  try {
39748
39827
  await mkdir10(join26(root, RAG_REL), { recursive: true });
39749
39828
  } catch {
@@ -39772,7 +39851,7 @@ rm -f "${RAG_REL}/.installing"
39772
39851
  }
39773
39852
 
39774
39853
  // src/core/config-sync.ts
39775
- import { existsSync as existsSync14, readFileSync as readFileSync15 } from "fs";
39854
+ import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
39776
39855
  import { join as join27 } from "path";
39777
39856
  var SERVER_MANAGED_KEYS = [
39778
39857
  "physicPath",
@@ -39791,7 +39870,7 @@ var SERVER_MANAGED_KEYS = [
39791
39870
  ];
39792
39871
  function readConfigText(profileDir2) {
39793
39872
  const path = join27(profileDir2, "config.json");
39794
- if (existsSync14(path)) {
39873
+ if (existsSync15(path)) {
39795
39874
  try {
39796
39875
  return readFileSync15(path, "utf8");
39797
39876
  } catch {
@@ -40644,7 +40723,7 @@ var WsClient = class {
40644
40723
  /** Create the physic folder at an absolute path if missing (best-effort). */
40645
40724
  ensurePhysicFolderPath(folder) {
40646
40725
  try {
40647
- if (!existsSync15(folder)) {
40726
+ if (!existsSync16(folder)) {
40648
40727
  mkdirSync7(folder, { recursive: true });
40649
40728
  this.bus.log(t("project.createdFolder", { folder }));
40650
40729
  }
@@ -40674,7 +40753,7 @@ var WsClient = class {
40674
40753
  projects: [],
40675
40754
  version: CLI_VERSION,
40676
40755
  physicPath,
40677
- physicPathExists: physicPath ? existsSync15(physicPath) : void 0,
40756
+ physicPathExists: physicPath ? existsSync16(physicPath) : void 0,
40678
40757
  // Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
40679
40758
  autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false,
40680
40759
  // The machine-user AI-run limit (0 = unlimited) + profile count so a dispatch can resolve the
@@ -43309,7 +43388,7 @@ var src_default = Yoga;
43309
43388
 
43310
43389
  // ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/reconciler.js
43311
43390
  var import_react_reconciler = __toESM(require_react_reconciler(), 1);
43312
- var import_constants10 = __toESM(require_constants2(), 1);
43391
+ var import_constants11 = __toESM(require_constants2(), 1);
43313
43392
  import process3 from "process";
43314
43393
 
43315
43394
  // ../../node_modules/.pnpm/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
@@ -44551,7 +44630,7 @@ var reconciler_default = (0, import_react_reconciler.default)({
44551
44630
  scheduleTimeout: setTimeout,
44552
44631
  cancelTimeout: clearTimeout,
44553
44632
  noTimeout: -1,
44554
- getCurrentEventPriority: () => import_constants10.DefaultEventPriority,
44633
+ getCurrentEventPriority: () => import_constants11.DefaultEventPriority,
44555
44634
  beforeActiveInstanceBlur() {
44556
44635
  },
44557
44636
  afterActiveInstanceBlur() {
@@ -46993,7 +47072,7 @@ var import_react21 = __toESM(require_react(), 1);
46993
47072
  var import_react26 = __toESM(require_react(), 1);
46994
47073
 
46995
47074
  // src/core/input-history.ts
46996
- import { existsSync as existsSync17, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
47075
+ import { existsSync as existsSync18, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
46997
47076
  import { join as join31 } from "path";
46998
47077
  var MAX_HISTORY = 200;
46999
47078
  function historyPath(profileDir2) {
@@ -47001,7 +47080,7 @@ function historyPath(profileDir2) {
47001
47080
  }
47002
47081
  function loadInputHistory(profileDir2) {
47003
47082
  const path = historyPath(profileDir2);
47004
- if (!existsSync17(path)) return [];
47083
+ if (!existsSync18(path)) return [];
47005
47084
  try {
47006
47085
  const data = JSON.parse(readFileSync18(path, "utf8"));
47007
47086
  return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4pm/cli",
3
- "version": "1.5.42",
3
+ "version": "1.8.4",
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",