@locusai/cli 0.25.3 → 0.25.5
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/bin/locus.js +114 -13
- package/package.json +2 -2
package/bin/locus.js
CHANGED
|
@@ -92,6 +92,8 @@ var init_ai_models = __esm(() => {
|
|
|
92
92
|
"claude-haiku-4-5-20251001"
|
|
93
93
|
];
|
|
94
94
|
CODEX_MODELS = [
|
|
95
|
+
"gpt-5.4",
|
|
96
|
+
"gpt-5.4-pro",
|
|
95
97
|
"gpt-5.3-codex",
|
|
96
98
|
"gpt-5.3-codex-spark",
|
|
97
99
|
"gpt-5.2-codex",
|
|
@@ -8178,6 +8180,9 @@ import { join as join16 } from "node:path";
|
|
|
8178
8180
|
function buildExecutionPrompt(ctx) {
|
|
8179
8181
|
const sections = [];
|
|
8180
8182
|
sections.push(buildSystemContext(ctx.projectRoot));
|
|
8183
|
+
const skills = buildSkillsContext(ctx.projectRoot);
|
|
8184
|
+
if (skills)
|
|
8185
|
+
sections.push(skills);
|
|
8181
8186
|
sections.push(buildTaskContext(ctx.issue, ctx.issueComments));
|
|
8182
8187
|
if (ctx.sprintContext || ctx.sprintPosition) {
|
|
8183
8188
|
sections.push(buildSprintContext(ctx.sprintName, ctx.sprintPosition, ctx.sprintContext));
|
|
@@ -8193,6 +8198,9 @@ function buildExecutionPrompt(ctx) {
|
|
|
8193
8198
|
function buildFeedbackPrompt(ctx) {
|
|
8194
8199
|
const sections = [];
|
|
8195
8200
|
sections.push(buildSystemContext(ctx.projectRoot));
|
|
8201
|
+
const skills = buildSkillsContext(ctx.projectRoot);
|
|
8202
|
+
if (skills)
|
|
8203
|
+
sections.push(skills);
|
|
8196
8204
|
sections.push(buildTaskContext(ctx.issue));
|
|
8197
8205
|
sections.push(buildPRContext(ctx.prNumber, ctx.prDiff, ctx.prComments));
|
|
8198
8206
|
sections.push(buildFeedbackInstructions());
|
|
@@ -8397,6 +8405,55 @@ function buildFeedbackInstructions() {
|
|
|
8397
8405
|
6. When done, summarize what you changed in response to each comment.
|
|
8398
8406
|
</instructions>`;
|
|
8399
8407
|
}
|
|
8408
|
+
function buildSkillsContext(projectRoot) {
|
|
8409
|
+
const skillsDir = join16(projectRoot, CLAUDE_SKILLS_DIR);
|
|
8410
|
+
if (!existsSync16(skillsDir))
|
|
8411
|
+
return null;
|
|
8412
|
+
let dirs;
|
|
8413
|
+
try {
|
|
8414
|
+
dirs = readdirSync4(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
8415
|
+
} catch {
|
|
8416
|
+
return null;
|
|
8417
|
+
}
|
|
8418
|
+
if (dirs.length === 0)
|
|
8419
|
+
return null;
|
|
8420
|
+
const entries = [];
|
|
8421
|
+
for (const dir of dirs) {
|
|
8422
|
+
const skillPath = join16(skillsDir, dir, "SKILL.md");
|
|
8423
|
+
const content = readFileSafe(skillPath);
|
|
8424
|
+
if (!content)
|
|
8425
|
+
continue;
|
|
8426
|
+
const fm = parseFrontmatter(content);
|
|
8427
|
+
const name = fm.name || dir;
|
|
8428
|
+
const description = fm.description || "";
|
|
8429
|
+
entries.push(`- **${name}**: ${description}`);
|
|
8430
|
+
}
|
|
8431
|
+
if (entries.length === 0)
|
|
8432
|
+
return null;
|
|
8433
|
+
return `<installed-skills>
|
|
8434
|
+
The following skills are installed in this project. If a skill is relevant to the current task, read its full instructions from \`.claude/skills/<name>/SKILL.md\` before starting work.
|
|
8435
|
+
|
|
8436
|
+
${entries.join(`
|
|
8437
|
+
`)}
|
|
8438
|
+
</installed-skills>`;
|
|
8439
|
+
}
|
|
8440
|
+
function parseFrontmatter(content) {
|
|
8441
|
+
const result = {};
|
|
8442
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8443
|
+
if (!match)
|
|
8444
|
+
return result;
|
|
8445
|
+
for (const line of match[1].split(`
|
|
8446
|
+
`)) {
|
|
8447
|
+
const idx = line.indexOf(":");
|
|
8448
|
+
if (idx === -1)
|
|
8449
|
+
continue;
|
|
8450
|
+
const key = line.slice(0, idx).trim();
|
|
8451
|
+
const val = line.slice(idx + 1).trim();
|
|
8452
|
+
if (key && val)
|
|
8453
|
+
result[key] = val;
|
|
8454
|
+
}
|
|
8455
|
+
return result;
|
|
8456
|
+
}
|
|
8400
8457
|
function readFileSafe(path) {
|
|
8401
8458
|
try {
|
|
8402
8459
|
if (!existsSync16(path))
|
|
@@ -10529,7 +10586,8 @@ ${green("✓")} Issue #${issueNumber} completed ${dim2(`(${timer.formatted()})`)
|
|
|
10529
10586
|
`);
|
|
10530
10587
|
let prNumber;
|
|
10531
10588
|
if (config.agent.autoPR && !options.skipPR) {
|
|
10532
|
-
|
|
10589
|
+
const workDir = options.worktreePath ?? projectRoot;
|
|
10590
|
+
prNumber = await createIssuePR(workDir, config, issue);
|
|
10533
10591
|
}
|
|
10534
10592
|
if (config.agent.autoLabel) {
|
|
10535
10593
|
try {
|
|
@@ -10610,29 +10668,56 @@ ${aiResult.success ? green("✓") : red2("✗")} Iteration ${aiResult.success ?
|
|
|
10610
10668
|
summary: extractSummary(aiResult.output)
|
|
10611
10669
|
};
|
|
10612
10670
|
}
|
|
10613
|
-
async function createIssuePR(
|
|
10671
|
+
async function createIssuePR(workDir, config, issue) {
|
|
10614
10672
|
try {
|
|
10673
|
+
try {
|
|
10674
|
+
commitDirtySubmodules(workDir, issue.number, issue.title);
|
|
10675
|
+
const status = execSync14("git status --porcelain", {
|
|
10676
|
+
cwd: workDir,
|
|
10677
|
+
encoding: "utf-8",
|
|
10678
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10679
|
+
}).trim();
|
|
10680
|
+
if (status) {
|
|
10681
|
+
execSync14("git add -A", {
|
|
10682
|
+
cwd: workDir,
|
|
10683
|
+
encoding: "utf-8",
|
|
10684
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10685
|
+
});
|
|
10686
|
+
const message = `chore: complete #${issue.number} - ${issue.title}
|
|
10687
|
+
|
|
10688
|
+
Co-Authored-By: LocusAgent <agent@locusai.team>`;
|
|
10689
|
+
execSync14("git commit -F -", {
|
|
10690
|
+
input: message,
|
|
10691
|
+
cwd: workDir,
|
|
10692
|
+
encoding: "utf-8",
|
|
10693
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10694
|
+
});
|
|
10695
|
+
process.stderr.write(` ${dim2(`Committed uncommitted changes for #${issue.number}`)}
|
|
10696
|
+
`);
|
|
10697
|
+
}
|
|
10698
|
+
} catch {}
|
|
10615
10699
|
const currentBranch = execSync14("git rev-parse --abbrev-ref HEAD", {
|
|
10616
|
-
cwd:
|
|
10700
|
+
cwd: workDir,
|
|
10617
10701
|
encoding: "utf-8",
|
|
10618
10702
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10619
10703
|
}).trim();
|
|
10620
10704
|
const diff = execSync14(`git diff origin/${config.agent.baseBranch}..HEAD --stat`, {
|
|
10621
|
-
cwd:
|
|
10705
|
+
cwd: workDir,
|
|
10622
10706
|
encoding: "utf-8",
|
|
10623
10707
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10624
10708
|
}).trim();
|
|
10625
10709
|
if (!diff) {
|
|
10626
|
-
|
|
10710
|
+
process.stderr.write(` ${yellow2("⚠")} No changes detected — skipping PR creation
|
|
10711
|
+
`);
|
|
10627
10712
|
return;
|
|
10628
10713
|
}
|
|
10629
|
-
pushSubmoduleBranches(
|
|
10714
|
+
pushSubmoduleBranches(workDir);
|
|
10630
10715
|
execSync14(`git push -u origin ${currentBranch}`, {
|
|
10631
|
-
cwd:
|
|
10716
|
+
cwd: workDir,
|
|
10632
10717
|
encoding: "utf-8",
|
|
10633
10718
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10634
10719
|
});
|
|
10635
|
-
const submoduleSummary = getSubmoduleChangeSummary(
|
|
10720
|
+
const submoduleSummary = getSubmoduleChangeSummary(workDir, config.agent.baseBranch);
|
|
10636
10721
|
let prBody = `Closes #${issue.number}`;
|
|
10637
10722
|
if (submoduleSummary) {
|
|
10638
10723
|
prBody += `
|
|
@@ -10647,7 +10732,7 @@ ${submoduleSummary}`;
|
|
|
10647
10732
|
const prTitle = `${issue.title} (#${issue.number})`;
|
|
10648
10733
|
let prNumber;
|
|
10649
10734
|
try {
|
|
10650
|
-
const existing = execSync14(`gh pr list --head ${currentBranch} --base ${config.agent.baseBranch} --json number --limit 1`, { cwd:
|
|
10735
|
+
const existing = execSync14(`gh pr list --head ${currentBranch} --base ${config.agent.baseBranch} --json number --limit 1`, { cwd: workDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10651
10736
|
const parsed = JSON.parse(existing);
|
|
10652
10737
|
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
10653
10738
|
prNumber = parsed[0].number;
|
|
@@ -10657,7 +10742,7 @@ ${submoduleSummary}`;
|
|
|
10657
10742
|
try {
|
|
10658
10743
|
execSync14(`gh pr edit ${prNumber} --title ${JSON.stringify(prTitle)} --body-file -`, {
|
|
10659
10744
|
input: prBody,
|
|
10660
|
-
cwd:
|
|
10745
|
+
cwd: workDir,
|
|
10661
10746
|
encoding: "utf-8",
|
|
10662
10747
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10663
10748
|
});
|
|
@@ -10667,13 +10752,15 @@ ${submoduleSummary}`;
|
|
|
10667
10752
|
getLogger().warn(`Failed to update PR #${prNumber}: ${editErr}`);
|
|
10668
10753
|
}
|
|
10669
10754
|
} else {
|
|
10670
|
-
prNumber = createPR(prTitle, prBody, currentBranch, config.agent.baseBranch, { cwd:
|
|
10755
|
+
prNumber = createPR(prTitle, prBody, currentBranch, config.agent.baseBranch, { cwd: workDir });
|
|
10671
10756
|
process.stderr.write(` ${green("✓")} Created PR #${prNumber}
|
|
10672
10757
|
`);
|
|
10673
10758
|
}
|
|
10674
10759
|
return prNumber;
|
|
10675
10760
|
} catch (e) {
|
|
10676
|
-
|
|
10761
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
10762
|
+
process.stderr.write(` ${red2("✗")} Failed to create PR: ${errorMsg}
|
|
10763
|
+
`);
|
|
10677
10764
|
return;
|
|
10678
10765
|
}
|
|
10679
10766
|
}
|
|
@@ -11653,7 +11740,7 @@ async function handleSingleIssue(projectRoot, config, issueNumber, flags, sandbo
|
|
|
11653
11740
|
${bold2("Running sprint issue")} ${cyan2(`#${issueNumber}`)} ${dim2("(sequential)")}
|
|
11654
11741
|
|
|
11655
11742
|
`);
|
|
11656
|
-
await executeIssue(projectRoot, {
|
|
11743
|
+
const result2 = await executeIssue(projectRoot, {
|
|
11657
11744
|
issueNumber,
|
|
11658
11745
|
provider: execution.provider,
|
|
11659
11746
|
model: execution.model,
|
|
@@ -11662,6 +11749,11 @@ ${bold2("Running sprint issue")} ${cyan2(`#${issueNumber}`)} ${dim2("(sequential
|
|
|
11662
11749
|
sandboxName: execution.sandboxName,
|
|
11663
11750
|
containerWorkdir: execution.containerWorkdir
|
|
11664
11751
|
});
|
|
11752
|
+
if (!result2.success) {
|
|
11753
|
+
process.stderr.write(`
|
|
11754
|
+
${red2("✗")} Issue #${issueNumber} execution failed${result2.error ? `: ${result2.error}` : ""}
|
|
11755
|
+
`);
|
|
11756
|
+
}
|
|
11665
11757
|
return;
|
|
11666
11758
|
}
|
|
11667
11759
|
const randomSuffix = Math.random().toString(36).slice(2, 8);
|
|
@@ -11704,6 +11796,15 @@ ${bold2("Running issue")} ${cyan2(`#${issueNumber}`)} ${dim2(`(branch: ${branchN
|
|
|
11704
11796
|
});
|
|
11705
11797
|
log.info(`Checked out ${config.agent.baseBranch}`);
|
|
11706
11798
|
} catch {}
|
|
11799
|
+
if (result.prNumber) {
|
|
11800
|
+
process.stderr.write(`
|
|
11801
|
+
${green("✓")} Issue #${issueNumber} done — PR #${result.prNumber} opened
|
|
11802
|
+
`);
|
|
11803
|
+
} else if (config.agent.autoPR) {
|
|
11804
|
+
process.stderr.write(`
|
|
11805
|
+
${yellow2("⚠")} Issue #${issueNumber} done — no PR was created (check errors above)
|
|
11806
|
+
`);
|
|
11807
|
+
}
|
|
11707
11808
|
} else {
|
|
11708
11809
|
process.stderr.write(` ${yellow2("⚠")} Branch ${dim2(branchName)} preserved for debugging.
|
|
11709
11810
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/cli",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.5",
|
|
4
4
|
"description": "GitHub-native AI engineering assistant",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@locusai/sdk": "^0.25.
|
|
39
|
+
"@locusai/sdk": "^0.25.5",
|
|
40
40
|
"@types/bun": "latest",
|
|
41
41
|
"typescript": "^5.8.3"
|
|
42
42
|
},
|