@4pm/cli 1.19.6 → 1.19.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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({
@@ -37611,6 +37610,11 @@ async function attachSubmodules(root, submodules, emit, createMissingBranch = fa
37611
37610
  }
37612
37611
  continue;
37613
37612
  }
37613
+ const abs = join6(root, dir2);
37614
+ if (existsSync4(abs)) {
37615
+ emit("submodule", `Removing placeholder ${dir2} before attaching the submodule\u2026`);
37616
+ await rm(abs, { recursive: true, force: true });
37617
+ }
37614
37618
  emit("submodule", `Adding submodule ${sub.url} \u2192 ${dir2}\u2026`);
37615
37619
  const b2 = (sub.branch ?? "").trim();
37616
37620
  if (b2 && createMissingBranch && !await ensureRemoteBranch(sub.url, b2, emit)) {
@@ -37653,8 +37657,13 @@ async function attachSubmodules(root, submodules, emit, createMissingBranch = fa
37653
37657
  }
37654
37658
  async function commitAndPushSubmodules(root, submodules, emit) {
37655
37659
  const dirs = submodules.map((s3) => (s3.subdir ?? "").trim()).filter(Boolean);
37660
+ const addPaths = [
37661
+ ...existsSync4(join6(root, ".gitmodules")) ? [".gitmodules"] : [],
37662
+ ...dirs.filter((d) => existsSync4(join6(root, d)))
37663
+ ];
37664
+ if (addPaths.length === 0) return;
37656
37665
  emit("submodule", "Committing .gitmodules\u2026");
37657
- await run2("git", ["add", ".gitmodules", ...dirs], { cwd: root, timeout: 6e4 });
37666
+ await run2("git", ["add", ...addPaths], { cwd: root, timeout: 6e4 });
37658
37667
  const staged = (await run2("git", ["diff", "--cached", "--name-only"], { cwd: root, timeout: 3e4 })).stdout.trim();
37659
37668
  if (!staged) return;
37660
37669
  const commitArgs = ["commit", "-m", "chore: add git submodules (4PM)"];
@@ -37671,6 +37680,50 @@ async function commitAndPushSubmodules(root, submodules, emit) {
37671
37680
  emit("submodule-push-failed", `Submodules committed locally but the push failed: ${msg}`);
37672
37681
  }
37673
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
+ }
37674
37727
  function cloneArgs(url, dest, branch) {
37675
37728
  const b2 = (branch ?? "").trim();
37676
37729
  return b2 ? ["clone", "-b", b2, url, dest] : ["clone", url, dest];
@@ -37777,18 +37830,20 @@ async function scaffoldProject(payload, profileDir2, onProgress) {
37777
37830
  if (!repo) throw new Error("A project must declare one repo (ADR-0314).");
37778
37831
  emit("git", "Cloning repository\u2026");
37779
37832
  await provisionRepo(target, repo, emit, { createMissingBranch: true });
37780
- await scaffoldRepo(target, payload.projectName, payload.spec, emit);
37833
+ await scaffoldRepo(target, payload.projectName, payload.spec, emit, { resetClaude: true });
37781
37834
  await attachSubmodules(target, submodulesOf(reposOf(payload.spec)), emit, true);
37782
37835
  emit("version", "Writing .4pm/.4pm.json\u2026");
37783
37836
  await writeTemplateMarker(target);
37837
+ await commitAndOpenPr(target, repo, emit);
37784
37838
  onProgress?.({ projectId: payload.projectId, step: "done", message: "Scaffold complete.", done: true });
37785
37839
  return { ok: true, path: target };
37786
37840
  } catch (err) {
37787
37841
  return { ok: false, error: err instanceof Error ? err.message : String(err), step: lastStep };
37788
37842
  }
37789
37843
  }
37790
- async function scaffoldRepo(dir2, label, spec, emit) {
37844
+ async function scaffoldRepo(dir2, label, spec, emit, opts = {}) {
37791
37845
  await mkdir(dir2, { recursive: true });
37846
+ if (opts.resetClaude) await resetTemplateManagedClaude(dir2, label, emit);
37792
37847
  emit("copy", `Copying the sample template into ${label}\u2026`);
37793
37848
  await cp(sampleDir(), dir2, { recursive: true, force: false, errorOnExist: false });
37794
37849
  if (spec) {
@@ -37797,6 +37852,13 @@ async function scaffoldRepo(dir2, label, spec, emit) {
37797
37852
  await aiInit(dir2, spec, emit);
37798
37853
  }
37799
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
+ }
37800
37862
  async function writeTemplateMarker(target) {
37801
37863
  const dir2 = join6(target, ".4pm");
37802
37864
  await mkdir(dir2, { recursive: true });
@@ -37844,15 +37906,20 @@ ${specJson}`
37844
37906
  ${specJson}` + (guideInstructions ? `
37845
37907
  Also incorporate these additional instructions/content:
37846
37908
  ${guideInstructions}
37847
- ` : "")
37909
+ ` : ""),
37910
+ guideInstructions
37848
37911
  );
37849
37912
  }
37850
- async function generateFile(path, prompt) {
37913
+ async function generateFile(path, prompt, fallback) {
37851
37914
  try {
37852
37915
  const text = await aiGenerate(prompt);
37853
- 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
+ }
37854
37920
  } catch {
37855
37921
  }
37922
+ if (fallback && fallback.trim()) await writeFile(path, fallback.trim() + "\n", "utf8");
37856
37923
  }
37857
37924
  async function addProject(payload, profileDir2, onProgress) {
37858
37925
  let lastStep = "start";
@@ -42350,7 +42417,8 @@ async function gitRepos(root) {
42350
42417
  if (!top || !(top === base || top.startsWith(base + sep8))) return { repos: [] };
42351
42418
  const repos = [];
42352
42419
  const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
42353
- repos.push({ subdir: "", name: "", remote: rootRemote || null });
42420
+ const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
42421
+ repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
42354
42422
  const subOut = await git(base, ["submodule", "status"]);
42355
42423
  for (const line of subOut.split("\n")) {
42356
42424
  const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
@@ -42359,7 +42427,8 @@ async function gitRepos(root) {
42359
42427
  const abs = resolve8(base, subPath);
42360
42428
  if (!(abs === base || abs.startsWith(base + sep8))) continue;
42361
42429
  const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
42362
- repos.push({ subdir: subPath, name: subPath, remote: remote || null });
42430
+ const branch = (await git(abs, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
42431
+ repos.push({ subdir: subPath, name: subPath, remote: remote || null, branch: branch || null });
42363
42432
  }
42364
42433
  return { repos };
42365
42434
  }
@@ -43029,7 +43098,6 @@ import { dirname as dirname8, join as join26 } from "path";
43029
43098
  var SHARED_REL = ".claude/settings.json";
43030
43099
  var LOCAL_REL = ".claude/settings.local.json";
43031
43100
  var SECRETS_DENY = "Read(./project.secrets.json)";
43032
- var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
43033
43101
  function fileFor(root, scope) {
43034
43102
  return join26(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
43035
43103
  }
@@ -43043,9 +43111,8 @@ async function readSettingsObject(path) {
43043
43111
  }
43044
43112
  function normalizePermissions(raw) {
43045
43113
  const p2 = raw && typeof raw === "object" ? raw : {};
43046
- const mode = MODES.includes(p2.defaultMode) ? p2.defaultMode : "default";
43047
43114
  const list = (v2) => Array.isArray(v2) ? v2.filter((x) => typeof x === "string") : [];
43048
- return { defaultMode: mode, allow: list(p2.allow), ask: list(p2.ask), deny: list(p2.deny) };
43115
+ return { defaultMode: "bypassPermissions", allow: list(p2.allow), deny: list(p2.deny) };
43049
43116
  }
43050
43117
  async function readAgentTools(root, scope) {
43051
43118
  const obj = await readSettingsObject(fileFor(root, scope));
@@ -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.6",
3
+ "version": "1.19.9",
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",