@4pm/cli 1.19.7 → 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({
|
|
@@ -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())
|
|
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";
|
|
@@ -42360,7 +42417,8 @@ async function gitRepos(root) {
|
|
|
42360
42417
|
if (!top || !(top === base || top.startsWith(base + sep8))) return { repos: [] };
|
|
42361
42418
|
const repos = [];
|
|
42362
42419
|
const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
|
|
42363
|
-
|
|
42420
|
+
const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
42421
|
+
repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
|
|
42364
42422
|
const subOut = await git(base, ["submodule", "status"]);
|
|
42365
42423
|
for (const line of subOut.split("\n")) {
|
|
42366
42424
|
const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
|
|
@@ -42369,7 +42427,8 @@ async function gitRepos(root) {
|
|
|
42369
42427
|
const abs = resolve8(base, subPath);
|
|
42370
42428
|
if (!(abs === base || abs.startsWith(base + sep8))) continue;
|
|
42371
42429
|
const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
|
|
42372
|
-
|
|
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 });
|
|
42373
42432
|
}
|
|
42374
42433
|
return { repos };
|
|
42375
42434
|
}
|
|
@@ -43039,7 +43098,6 @@ import { dirname as dirname8, join as join26 } from "path";
|
|
|
43039
43098
|
var SHARED_REL = ".claude/settings.json";
|
|
43040
43099
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
43041
43100
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
43042
|
-
var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
|
43043
43101
|
function fileFor(root, scope) {
|
|
43044
43102
|
return join26(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
43045
43103
|
}
|
|
@@ -43053,9 +43111,8 @@ async function readSettingsObject(path) {
|
|
|
43053
43111
|
}
|
|
43054
43112
|
function normalizePermissions(raw) {
|
|
43055
43113
|
const p2 = raw && typeof raw === "object" ? raw : {};
|
|
43056
|
-
const mode = MODES.includes(p2.defaultMode) ? p2.defaultMode : "default";
|
|
43057
43114
|
const list = (v2) => Array.isArray(v2) ? v2.filter((x) => typeof x === "string") : [];
|
|
43058
|
-
return { defaultMode:
|
|
43115
|
+
return { defaultMode: "bypassPermissions", allow: list(p2.allow), deny: list(p2.deny) };
|
|
43059
43116
|
}
|
|
43060
43117
|
async function readAgentTools(root, scope) {
|
|
43061
43118
|
const obj = await readSettingsObject(fileFor(root, scope));
|