@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.
Files changed (2) hide show
  1. package/bin/locus.js +114 -13
  2. 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
- prNumber = await createIssuePR(projectRoot, config, issue);
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(projectRoot, config, issue) {
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: projectRoot,
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: projectRoot,
10705
+ cwd: workDir,
10622
10706
  encoding: "utf-8",
10623
10707
  stdio: ["pipe", "pipe", "pipe"]
10624
10708
  }).trim();
10625
10709
  if (!diff) {
10626
- getLogger().verbose("No changes to create PR for");
10710
+ process.stderr.write(` ${yellow2("⚠")} No changes detected skipping PR creation
10711
+ `);
10627
10712
  return;
10628
10713
  }
10629
- pushSubmoduleBranches(projectRoot);
10714
+ pushSubmoduleBranches(workDir);
10630
10715
  execSync14(`git push -u origin ${currentBranch}`, {
10631
- cwd: projectRoot,
10716
+ cwd: workDir,
10632
10717
  encoding: "utf-8",
10633
10718
  stdio: ["pipe", "pipe", "pipe"]
10634
10719
  });
10635
- const submoduleSummary = getSubmoduleChangeSummary(projectRoot, config.agent.baseBranch);
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: projectRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
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: projectRoot,
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: projectRoot });
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
- getLogger().warn(`Failed to create PR: ${e}`);
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",
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.3",
39
+ "@locusai/sdk": "^0.25.5",
40
40
  "@types/bun": "latest",
41
41
  "typescript": "^5.8.3"
42
42
  },