@codebyplan/cli 2.1.0 → 2.3.0
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/cli.js +332 -43
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,7 @@ var VERSION, PACKAGE_NAME;
|
|
|
37
37
|
var init_version = __esm({
|
|
38
38
|
"src/lib/version.ts"() {
|
|
39
39
|
"use strict";
|
|
40
|
-
VERSION = "2.
|
|
40
|
+
VERSION = "2.3.0";
|
|
41
41
|
PACKAGE_NAME = "@codebyplan/cli";
|
|
42
42
|
}
|
|
43
43
|
});
|
|
@@ -23526,63 +23526,269 @@ var init_read = __esm({
|
|
|
23526
23526
|
}
|
|
23527
23527
|
});
|
|
23528
23528
|
|
|
23529
|
-
// src/lib/
|
|
23529
|
+
// src/lib/git-pr.ts
|
|
23530
23530
|
import { exec as execCb } from "node:child_process";
|
|
23531
23531
|
import { promisify } from "node:util";
|
|
23532
|
-
async function
|
|
23533
|
-
|
|
23532
|
+
async function createPR(options) {
|
|
23533
|
+
const { repoPath, head, base, title, body } = options;
|
|
23534
23534
|
try {
|
|
23535
|
-
|
|
23536
|
-
|
|
23537
|
-
|
|
23535
|
+
try {
|
|
23536
|
+
const { stdout: existing } = await exec(
|
|
23537
|
+
`gh pr list --head "${head}" --base "${base}" --json number,url --jq '.[0]'`,
|
|
23538
|
+
{ cwd: repoPath }
|
|
23539
|
+
);
|
|
23540
|
+
if (existing.trim()) {
|
|
23541
|
+
const pr = JSON.parse(existing.trim());
|
|
23542
|
+
return { pr_url: pr.url, pr_number: pr.number };
|
|
23543
|
+
}
|
|
23544
|
+
} catch {
|
|
23545
|
+
}
|
|
23546
|
+
const { stdout: stdout5 } = await exec(
|
|
23547
|
+
`gh pr create --head "${head}" --base "${base}" --title "${title.replace(/"/g, '\\"')}" --body "${body.replace(/"/g, '\\"')}"`,
|
|
23548
|
+
{ cwd: repoPath }
|
|
23549
|
+
);
|
|
23550
|
+
const prUrl = stdout5.trim();
|
|
23551
|
+
const prNumber = parseInt(prUrl.split("/").pop() ?? "0", 10);
|
|
23552
|
+
return { pr_url: prUrl, pr_number: prNumber || null };
|
|
23553
|
+
} catch (err) {
|
|
23554
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
23555
|
+
return { pr_url: null, pr_number: null, error: errorMessage };
|
|
23556
|
+
}
|
|
23557
|
+
}
|
|
23558
|
+
async function mergePR(options) {
|
|
23559
|
+
const { repoPath, prNumber, mergeMethod } = options;
|
|
23560
|
+
try {
|
|
23561
|
+
const { stdout: stdout5 } = await exec(
|
|
23562
|
+
`gh pr merge ${prNumber} --${mergeMethod} --delete-branch`,
|
|
23563
|
+
{ cwd: repoPath }
|
|
23564
|
+
);
|
|
23565
|
+
return { merged: true, message: stdout5.trim() || `PR #${prNumber} merged via ${mergeMethod}` };
|
|
23566
|
+
} catch (err) {
|
|
23567
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
23568
|
+
return { merged: false, message: "Merge failed", error: errorMessage };
|
|
23569
|
+
}
|
|
23570
|
+
}
|
|
23571
|
+
async function getPRStatus(repoPath, prNumber) {
|
|
23572
|
+
try {
|
|
23573
|
+
const { stdout: stdout5 } = await exec(
|
|
23574
|
+
`gh pr view ${prNumber} --json state,mergeable,title,url,number`,
|
|
23575
|
+
{ cwd: repoPath }
|
|
23576
|
+
);
|
|
23577
|
+
const pr = JSON.parse(stdout5.trim());
|
|
23538
23578
|
return {
|
|
23539
|
-
|
|
23540
|
-
|
|
23541
|
-
|
|
23579
|
+
state: pr.state,
|
|
23580
|
+
mergeable: pr.mergeable,
|
|
23581
|
+
title: pr.title,
|
|
23582
|
+
url: pr.url,
|
|
23583
|
+
number: pr.number
|
|
23542
23584
|
};
|
|
23543
|
-
}
|
|
23544
|
-
|
|
23585
|
+
} catch (err) {
|
|
23586
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
23545
23587
|
return {
|
|
23546
|
-
|
|
23547
|
-
|
|
23588
|
+
state: "UNKNOWN",
|
|
23589
|
+
mergeable: "UNKNOWN",
|
|
23590
|
+
title: "",
|
|
23591
|
+
url: "",
|
|
23592
|
+
number: prNumber,
|
|
23593
|
+
error: errorMessage
|
|
23548
23594
|
};
|
|
23549
23595
|
}
|
|
23550
|
-
|
|
23596
|
+
}
|
|
23597
|
+
var exec;
|
|
23598
|
+
var init_git_pr = __esm({
|
|
23599
|
+
"src/lib/git-pr.ts"() {
|
|
23600
|
+
"use strict";
|
|
23601
|
+
exec = promisify(execCb);
|
|
23602
|
+
}
|
|
23603
|
+
});
|
|
23604
|
+
|
|
23605
|
+
// src/lib/promotion.ts
|
|
23606
|
+
async function promoteCheckpoint(checkpointId) {
|
|
23607
|
+
try {
|
|
23608
|
+
const checkpointRes = await apiGet(`/checkpoints/${checkpointId}`);
|
|
23609
|
+
const checkpoint = checkpointRes.data;
|
|
23610
|
+
if (!checkpoint.branch_name) {
|
|
23611
|
+
return {
|
|
23612
|
+
promoted: false,
|
|
23613
|
+
pr_url: null,
|
|
23614
|
+
pr_number: null,
|
|
23615
|
+
checklist_id: null,
|
|
23616
|
+
message: "Checkpoint has no branch_name set",
|
|
23617
|
+
error: "No branch_name on checkpoint"
|
|
23618
|
+
};
|
|
23619
|
+
}
|
|
23620
|
+
const repoRes = await apiGet(`/repos/${checkpoint.repo_id}`);
|
|
23621
|
+
const repo = repoRes.data;
|
|
23622
|
+
if (!repo.path) {
|
|
23623
|
+
return {
|
|
23624
|
+
promoted: false,
|
|
23625
|
+
pr_url: null,
|
|
23626
|
+
pr_number: null,
|
|
23627
|
+
checklist_id: null,
|
|
23628
|
+
message: "Repo path not configured",
|
|
23629
|
+
error: "Repo path not configured"
|
|
23630
|
+
};
|
|
23631
|
+
}
|
|
23632
|
+
const baseBranch = repo.git_branch ?? "development";
|
|
23633
|
+
const chkNumber = checkpoint.number.toString().padStart(3, "0");
|
|
23634
|
+
const checklist = await createChecklistFromTemplates(
|
|
23635
|
+
checkpoint.repo_id,
|
|
23636
|
+
checkpointId,
|
|
23637
|
+
"feat_to_development",
|
|
23638
|
+
`CHK-${chkNumber}: ${checkpoint.title ?? "Untitled"} \u2192 ${baseBranch}`
|
|
23639
|
+
);
|
|
23640
|
+
const prResult = await createPR({
|
|
23641
|
+
repoPath: repo.path,
|
|
23642
|
+
head: checkpoint.branch_name,
|
|
23643
|
+
base: baseBranch,
|
|
23644
|
+
title: `CHK-${chkNumber}: ${checkpoint.title ?? "Checkpoint completion"}`,
|
|
23645
|
+
body: `## Checkpoint CHK-${chkNumber}
|
|
23646
|
+
|
|
23647
|
+
**Goal**: ${checkpoint.goal ?? "N/A"}
|
|
23648
|
+
|
|
23649
|
+
Automatically created by CodeByPlan promotion engine.`
|
|
23650
|
+
});
|
|
23651
|
+
if (checklist && prResult.pr_url) {
|
|
23652
|
+
await apiPut(`/merge-checklists/${checklist.id}`, {
|
|
23653
|
+
pr_url: prResult.pr_url,
|
|
23654
|
+
pr_number: prResult.pr_number,
|
|
23655
|
+
status: "in_progress"
|
|
23656
|
+
});
|
|
23657
|
+
}
|
|
23658
|
+
let mergeMessage = "";
|
|
23659
|
+
if (prResult.pr_number) {
|
|
23660
|
+
const mergeResult = await mergePR({
|
|
23661
|
+
repoPath: repo.path,
|
|
23662
|
+
prNumber: prResult.pr_number,
|
|
23663
|
+
mergeMethod: "merge"
|
|
23664
|
+
});
|
|
23665
|
+
mergeMessage = mergeResult.merged ? ` \u2192 Merged` : ` \u2192 Merge failed: ${mergeResult.error}`;
|
|
23666
|
+
if (mergeResult.merged && checklist) {
|
|
23667
|
+
await apiPut(`/merge-checklists/${checklist.id}`, {
|
|
23668
|
+
status: "completed",
|
|
23669
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
23670
|
+
});
|
|
23671
|
+
}
|
|
23672
|
+
}
|
|
23551
23673
|
return {
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23674
|
+
promoted: true,
|
|
23675
|
+
pr_url: prResult.pr_url,
|
|
23676
|
+
pr_number: prResult.pr_number,
|
|
23677
|
+
checklist_id: checklist?.id ?? null,
|
|
23678
|
+
message: prResult.error ? `PR creation issue: ${prResult.error}` : `PR created: ${prResult.pr_url}${mergeMessage}`
|
|
23679
|
+
};
|
|
23680
|
+
} catch (err) {
|
|
23681
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
23682
|
+
return {
|
|
23683
|
+
promoted: false,
|
|
23684
|
+
pr_url: null,
|
|
23685
|
+
pr_number: null,
|
|
23686
|
+
checklist_id: null,
|
|
23687
|
+
message: "Promotion failed",
|
|
23688
|
+
error: errorMessage
|
|
23555
23689
|
};
|
|
23556
23690
|
}
|
|
23557
|
-
|
|
23691
|
+
}
|
|
23692
|
+
async function promoteToMain(repoId) {
|
|
23558
23693
|
try {
|
|
23559
|
-
|
|
23560
|
-
|
|
23561
|
-
|
|
23562
|
-
|
|
23694
|
+
const repoRes = await apiGet(`/repos/${repoId}`);
|
|
23695
|
+
const repo = repoRes.data;
|
|
23696
|
+
if (!repo.path) {
|
|
23697
|
+
return {
|
|
23698
|
+
promoted: false,
|
|
23699
|
+
pr_url: null,
|
|
23700
|
+
pr_number: null,
|
|
23701
|
+
checklist_id: null,
|
|
23702
|
+
message: "Repo path not configured",
|
|
23703
|
+
error: "Repo path not configured"
|
|
23704
|
+
};
|
|
23705
|
+
}
|
|
23706
|
+
const sourceBranch = repo.git_branch ?? "development";
|
|
23707
|
+
const prResult = await createPR({
|
|
23708
|
+
repoPath: repo.path,
|
|
23709
|
+
head: sourceBranch,
|
|
23710
|
+
base: "main",
|
|
23711
|
+
title: `Promote ${sourceBranch} \u2192 main`,
|
|
23712
|
+
body: `Promotion from ${sourceBranch} to main.
|
|
23713
|
+
|
|
23714
|
+
Automatically created by CodeByPlan promotion engine.`
|
|
23715
|
+
});
|
|
23716
|
+
let mergeMessage = "";
|
|
23717
|
+
if (prResult.pr_number) {
|
|
23718
|
+
const mergeResult = await mergePR({
|
|
23719
|
+
repoPath: repo.path,
|
|
23720
|
+
prNumber: prResult.pr_number,
|
|
23721
|
+
mergeMethod: "merge"
|
|
23722
|
+
});
|
|
23723
|
+
mergeMessage = mergeResult.merged ? ` \u2192 Merged` : ` \u2192 Merge failed: ${mergeResult.error}`;
|
|
23724
|
+
}
|
|
23563
23725
|
return {
|
|
23564
|
-
|
|
23565
|
-
|
|
23726
|
+
promoted: true,
|
|
23727
|
+
pr_url: prResult.pr_url,
|
|
23728
|
+
pr_number: prResult.pr_number,
|
|
23729
|
+
checklist_id: null,
|
|
23730
|
+
message: prResult.error ? `PR creation issue: ${prResult.error}` : `PR created: ${prResult.pr_url}${mergeMessage}`
|
|
23566
23731
|
};
|
|
23567
23732
|
} catch (err) {
|
|
23568
|
-
try {
|
|
23569
|
-
await exec(`git checkout ${sourceBranch}`, { cwd: repo.path });
|
|
23570
|
-
} catch {
|
|
23571
|
-
}
|
|
23572
23733
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
23573
23734
|
return {
|
|
23574
|
-
|
|
23575
|
-
|
|
23735
|
+
promoted: false,
|
|
23736
|
+
pr_url: null,
|
|
23737
|
+
pr_number: null,
|
|
23738
|
+
checklist_id: null,
|
|
23739
|
+
message: "Promotion to main failed",
|
|
23576
23740
|
error: errorMessage
|
|
23577
23741
|
};
|
|
23578
23742
|
}
|
|
23579
23743
|
}
|
|
23580
|
-
|
|
23581
|
-
|
|
23582
|
-
|
|
23744
|
+
async function createChecklistFromTemplates(repoId, checkpointId, branchLevel, title) {
|
|
23745
|
+
try {
|
|
23746
|
+
const templatesRes = await apiGet(
|
|
23747
|
+
`/repos/${repoId}/checklist-templates`,
|
|
23748
|
+
{ branch_level: branchLevel }
|
|
23749
|
+
);
|
|
23750
|
+
const templates = templatesRes.data ?? [];
|
|
23751
|
+
const items = templates.map((t) => ({
|
|
23752
|
+
title: t.title,
|
|
23753
|
+
description: t.description,
|
|
23754
|
+
is_required: t.is_required,
|
|
23755
|
+
checked: false,
|
|
23756
|
+
checked_at: null
|
|
23757
|
+
}));
|
|
23758
|
+
const checklistRes = await apiPost(
|
|
23759
|
+
"/merge-checklists",
|
|
23760
|
+
{
|
|
23761
|
+
checkpoint_id: checkpointId,
|
|
23762
|
+
branch_level: branchLevel,
|
|
23763
|
+
title,
|
|
23764
|
+
status: "pending",
|
|
23765
|
+
items
|
|
23766
|
+
}
|
|
23767
|
+
);
|
|
23768
|
+
return checklistRes.data;
|
|
23769
|
+
} catch {
|
|
23770
|
+
try {
|
|
23771
|
+
const checklistRes = await apiPost(
|
|
23772
|
+
"/merge-checklists",
|
|
23773
|
+
{
|
|
23774
|
+
checkpoint_id: checkpointId,
|
|
23775
|
+
branch_level: branchLevel,
|
|
23776
|
+
title,
|
|
23777
|
+
status: "pending",
|
|
23778
|
+
items: []
|
|
23779
|
+
}
|
|
23780
|
+
);
|
|
23781
|
+
return checklistRes.data;
|
|
23782
|
+
} catch {
|
|
23783
|
+
return null;
|
|
23784
|
+
}
|
|
23785
|
+
}
|
|
23786
|
+
}
|
|
23787
|
+
var init_promotion = __esm({
|
|
23788
|
+
"src/lib/promotion.ts"() {
|
|
23583
23789
|
"use strict";
|
|
23584
23790
|
init_api();
|
|
23585
|
-
|
|
23791
|
+
init_git_pr();
|
|
23586
23792
|
}
|
|
23587
23793
|
});
|
|
23588
23794
|
|
|
@@ -23659,6 +23865,7 @@ function registerWriteTools(server) {
|
|
|
23659
23865
|
launch_id: external_exports.string().uuid().nullable().optional().describe("Launch UUID to connect (or null to disconnect)"),
|
|
23660
23866
|
worktree_id: external_exports.string().uuid().nullable().optional().describe("Worktree UUID to assign (or null to unassign)"),
|
|
23661
23867
|
assigned_to: external_exports.string().nullable().optional().describe("Who/what claimed this checkpoint"),
|
|
23868
|
+
branch_name: external_exports.string().nullable().optional().describe("Git branch name for this checkpoint (e.g. feat/CHK-061-git-overhaul)"),
|
|
23662
23869
|
ideas: external_exports.array(external_exports.object({
|
|
23663
23870
|
description: external_exports.string().describe("Idea description"),
|
|
23664
23871
|
requirements: external_exports.array(external_exports.string()).optional().describe("List of requirements for this idea"),
|
|
@@ -23668,7 +23875,7 @@ function registerWriteTools(server) {
|
|
|
23668
23875
|
research: external_exports.any().optional().describe("Research JSONB (topics with findings and sources)"),
|
|
23669
23876
|
qa: external_exports.any().optional().describe("QA JSONB (checklist items with type, check, status)")
|
|
23670
23877
|
}
|
|
23671
|
-
}, async ({ checkpoint_id, title, goal, status, deadline, completed_at, launch_id, worktree_id, assigned_to, ideas, context, research, qa }) => {
|
|
23878
|
+
}, async ({ checkpoint_id, title, goal, status, deadline, completed_at, launch_id, worktree_id, assigned_to, branch_name, ideas, context, research, qa }) => {
|
|
23672
23879
|
const update = {};
|
|
23673
23880
|
if (title !== void 0) update.title = title;
|
|
23674
23881
|
if (goal !== void 0) update.goal = goal;
|
|
@@ -23678,6 +23885,7 @@ function registerWriteTools(server) {
|
|
|
23678
23885
|
if (launch_id !== void 0) update.launch_id = launch_id;
|
|
23679
23886
|
if (worktree_id !== void 0) update.worktree_id = worktree_id;
|
|
23680
23887
|
if (assigned_to !== void 0) update.assigned_to = assigned_to;
|
|
23888
|
+
if (branch_name !== void 0) update.branch_name = branch_name;
|
|
23681
23889
|
if (ideas !== void 0) update.ideas = ideas;
|
|
23682
23890
|
if (context !== void 0) update.context = context;
|
|
23683
23891
|
if (research !== void 0) update.research = research;
|
|
@@ -23693,7 +23901,7 @@ function registerWriteTools(server) {
|
|
|
23693
23901
|
}
|
|
23694
23902
|
});
|
|
23695
23903
|
server.registerTool("complete_checkpoint", {
|
|
23696
|
-
description: "Mark a checkpoint as completed. Sets status to 'completed', completed_at to now, and triggers
|
|
23904
|
+
description: "Mark a checkpoint as completed. Sets status to 'completed', completed_at to now, and triggers promotion (creates PR from feat branch to development).",
|
|
23697
23905
|
inputSchema: {
|
|
23698
23906
|
checkpoint_id: external_exports.string().uuid().describe("The checkpoint UUID")
|
|
23699
23907
|
}
|
|
@@ -23704,8 +23912,13 @@ function registerWriteTools(server) {
|
|
|
23704
23912
|
completed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
23705
23913
|
});
|
|
23706
23914
|
const checkpoint = res.data;
|
|
23707
|
-
const
|
|
23708
|
-
|
|
23915
|
+
const featToDevResult = await promoteCheckpoint(checkpoint_id);
|
|
23916
|
+
let devToMainResult = null;
|
|
23917
|
+
const repoRes = await apiGet(`/repos/${checkpoint.repo_id}`);
|
|
23918
|
+
if (repoRes.data.auto_push_enabled) {
|
|
23919
|
+
devToMainResult = await promoteToMain(checkpoint.repo_id);
|
|
23920
|
+
}
|
|
23921
|
+
return { content: [{ type: "text", text: JSON.stringify({ checkpoint, promotion: { feat_to_development: featToDevResult, development_to_main: devToMainResult } }, null, 2) }] };
|
|
23709
23922
|
} catch (err) {
|
|
23710
23923
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
23711
23924
|
}
|
|
@@ -24101,13 +24314,74 @@ function registerWriteTools(server) {
|
|
|
24101
24314
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
24102
24315
|
}
|
|
24103
24316
|
});
|
|
24317
|
+
server.registerTool("create_pr", {
|
|
24318
|
+
description: "Create a GitHub PR for a repo. Uses gh CLI.",
|
|
24319
|
+
inputSchema: {
|
|
24320
|
+
repo_id: external_exports.string().uuid().describe("The repo UUID"),
|
|
24321
|
+
head: external_exports.string().describe("Source branch name"),
|
|
24322
|
+
base: external_exports.string().describe("Target branch name"),
|
|
24323
|
+
title: external_exports.string().describe("PR title"),
|
|
24324
|
+
body: external_exports.string().optional().describe("PR description body")
|
|
24325
|
+
}
|
|
24326
|
+
}, async ({ repo_id, head, base, title, body }) => {
|
|
24327
|
+
try {
|
|
24328
|
+
const repoRes = await apiGet(`/repos/${repo_id}`);
|
|
24329
|
+
const repo = repoRes.data;
|
|
24330
|
+
if (!repo.path) {
|
|
24331
|
+
return { content: [{ type: "text", text: "Error: Repo path is not configured." }], isError: true };
|
|
24332
|
+
}
|
|
24333
|
+
const result = await createPR({
|
|
24334
|
+
repoPath: repo.path,
|
|
24335
|
+
head,
|
|
24336
|
+
base,
|
|
24337
|
+
title,
|
|
24338
|
+
body: body ?? ""
|
|
24339
|
+
});
|
|
24340
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
24341
|
+
} catch (err) {
|
|
24342
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
24343
|
+
}
|
|
24344
|
+
});
|
|
24345
|
+
server.registerTool("get_pr_status", {
|
|
24346
|
+
description: "Get the status of a GitHub PR by number.",
|
|
24347
|
+
inputSchema: {
|
|
24348
|
+
repo_id: external_exports.string().uuid().describe("The repo UUID"),
|
|
24349
|
+
pr_number: external_exports.number().int().describe("The PR number")
|
|
24350
|
+
}
|
|
24351
|
+
}, async ({ repo_id, pr_number }) => {
|
|
24352
|
+
try {
|
|
24353
|
+
const repoRes = await apiGet(`/repos/${repo_id}`);
|
|
24354
|
+
const repo = repoRes.data;
|
|
24355
|
+
if (!repo.path) {
|
|
24356
|
+
return { content: [{ type: "text", text: "Error: Repo path is not configured." }], isError: true };
|
|
24357
|
+
}
|
|
24358
|
+
const status = await getPRStatus(repo.path, pr_number);
|
|
24359
|
+
return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
|
|
24360
|
+
} catch (err) {
|
|
24361
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
24362
|
+
}
|
|
24363
|
+
});
|
|
24364
|
+
server.registerTool("promote_checkpoint", {
|
|
24365
|
+
description: "Trigger full promotion flow for a checkpoint. Creates merge checklist from templates and GitHub PR (feat branch \u2192 development).",
|
|
24366
|
+
inputSchema: {
|
|
24367
|
+
checkpoint_id: external_exports.string().uuid().describe("The checkpoint UUID")
|
|
24368
|
+
}
|
|
24369
|
+
}, async ({ checkpoint_id }) => {
|
|
24370
|
+
try {
|
|
24371
|
+
const result = await promoteCheckpoint(checkpoint_id);
|
|
24372
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
24373
|
+
} catch (err) {
|
|
24374
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
24375
|
+
}
|
|
24376
|
+
});
|
|
24104
24377
|
}
|
|
24105
24378
|
var init_write = __esm({
|
|
24106
24379
|
"src/tools/write.ts"() {
|
|
24107
24380
|
"use strict";
|
|
24108
24381
|
init_zod();
|
|
24109
24382
|
init_api();
|
|
24110
|
-
|
|
24383
|
+
init_promotion();
|
|
24384
|
+
init_git_pr();
|
|
24111
24385
|
init_sync_engine();
|
|
24112
24386
|
}
|
|
24113
24387
|
});
|
|
@@ -24117,30 +24391,45 @@ import { exec as execCb2 } from "node:child_process";
|
|
|
24117
24391
|
import { promisify as promisify2 } from "node:util";
|
|
24118
24392
|
function registerFileGenTools(server) {
|
|
24119
24393
|
server.registerTool("git_commit", {
|
|
24120
|
-
description: "Stage files and create a git commit in a repo. If files are specified, stages only those files. Otherwise stages all changes.",
|
|
24394
|
+
description: "Stage files and create a git commit in a repo. If files are specified, stages only those files. Otherwise stages all changes. Optionally verifies the current branch matches branch_name.",
|
|
24121
24395
|
inputSchema: {
|
|
24122
24396
|
repo_id: external_exports.string().uuid().describe("The repo UUID"),
|
|
24123
24397
|
message: external_exports.string().describe("Commit message"),
|
|
24124
|
-
files: external_exports.array(external_exports.string()).optional().describe("Specific file paths to stage (relative to repo root). If omitted, stages all changes.")
|
|
24398
|
+
files: external_exports.array(external_exports.string()).optional().describe("Specific file paths to stage (relative to repo root). If omitted, stages all changes."),
|
|
24399
|
+
branch_name: external_exports.string().optional().describe("Expected branch name. If provided, verifies the repo is on this branch before committing.")
|
|
24125
24400
|
}
|
|
24126
|
-
}, async ({ repo_id, message, files }) => {
|
|
24401
|
+
}, async ({ repo_id, message, files, branch_name }) => {
|
|
24127
24402
|
try {
|
|
24128
24403
|
const repoRes = await apiGet(`/repos/${repo_id}`);
|
|
24129
24404
|
const repo = repoRes.data;
|
|
24130
24405
|
if (!repo.path) {
|
|
24131
24406
|
return { content: [{ type: "text", text: "Error: Repo path is not configured." }], isError: true };
|
|
24132
24407
|
}
|
|
24408
|
+
if (branch_name) {
|
|
24409
|
+
const { stdout: currentBranch } = await exec2("git rev-parse --abbrev-ref HEAD", { cwd: repo.path });
|
|
24410
|
+
if (currentBranch.trim() !== branch_name) {
|
|
24411
|
+
return {
|
|
24412
|
+
content: [{
|
|
24413
|
+
type: "text",
|
|
24414
|
+
text: `Error: Expected branch "${branch_name}" but currently on "${currentBranch.trim()}".`
|
|
24415
|
+
}],
|
|
24416
|
+
isError: true
|
|
24417
|
+
};
|
|
24418
|
+
}
|
|
24419
|
+
}
|
|
24133
24420
|
const addCmd = files && files.length > 0 ? `git add ${files.map((f) => `"${f}"`).join(" ")}` : "git add .";
|
|
24134
24421
|
await exec2(addCmd, { cwd: repo.path });
|
|
24135
24422
|
const { stdout: stdout5, stderr } = await exec2(
|
|
24136
24423
|
`git commit -m "${message.replace(/"/g, '\\"')}"`,
|
|
24137
24424
|
{ cwd: repo.path }
|
|
24138
24425
|
);
|
|
24426
|
+
const { stdout: branch } = await exec2("git rev-parse --abbrev-ref HEAD", { cwd: repo.path });
|
|
24139
24427
|
return {
|
|
24140
24428
|
content: [{
|
|
24141
24429
|
type: "text",
|
|
24142
24430
|
text: JSON.stringify({
|
|
24143
24431
|
status: "committed",
|
|
24432
|
+
branch: branch.trim(),
|
|
24144
24433
|
output: stdout5.trim(),
|
|
24145
24434
|
warnings: stderr.trim() || void 0
|
|
24146
24435
|
}, null, 2)
|