@locusai/cli 0.25.2 → 0.25.3

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 +220 -75
  2. package/package.json +2 -2
package/bin/locus.js CHANGED
@@ -3352,46 +3352,155 @@ var init_lock = __esm(() => {
3352
3352
  });
3353
3353
 
3354
3354
  // src/skills/installer.ts
3355
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, rmSync, writeFileSync as writeFileSync8 } from "node:fs";
3355
+ import {
3356
+ existsSync as existsSync11,
3357
+ mkdirSync as mkdirSync8,
3358
+ readFileSync as readFileSync8,
3359
+ renameSync as renameSync2,
3360
+ rmSync,
3361
+ writeFileSync as writeFileSync8
3362
+ } from "node:fs";
3363
+ import { tmpdir } from "node:os";
3356
3364
  import { join as join11 } from "node:path";
3357
3365
  async function installSkill(projectRoot, name, content, source) {
3358
3366
  const claudeDir = join11(projectRoot, CLAUDE_SKILLS_DIR, name);
3359
3367
  const agentsDir = join11(projectRoot, AGENTS_SKILLS_DIR, name);
3360
- mkdirSync8(claudeDir, { recursive: true });
3361
- mkdirSync8(agentsDir, { recursive: true });
3362
- writeFileSync8(join11(claudeDir, "SKILL.md"), content, "utf-8");
3363
- writeFileSync8(join11(agentsDir, "SKILL.md"), content, "utf-8");
3364
- const lock = readLockFile(projectRoot);
3365
- lock.skills[name] = {
3366
- source,
3367
- sourceType: "github",
3368
- computedHash: computeSkillHash(content)
3369
- };
3370
- writeLockFile(projectRoot, lock);
3368
+ const stagingDir = join11(tmpdir(), `locus-skill-${name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
3369
+ const stagingClaudeDir = join11(stagingDir, "claude");
3370
+ const stagingAgentsDir = join11(stagingDir, "agents");
3371
+ let wroteClaudeDir = false;
3372
+ let wroteAgentsDir = false;
3373
+ try {
3374
+ try {
3375
+ mkdirSync8(stagingClaudeDir, { recursive: true });
3376
+ mkdirSync8(stagingAgentsDir, { recursive: true });
3377
+ writeFileSync8(join11(stagingClaudeDir, "SKILL.md"), content, "utf-8");
3378
+ writeFileSync8(join11(stagingAgentsDir, "SKILL.md"), content, "utf-8");
3379
+ } catch (err) {
3380
+ throw new SkillInstallError(name, "stage", err);
3381
+ }
3382
+ try {
3383
+ if (!content || content.trim().length === 0) {
3384
+ throw new Error("Skill content is empty");
3385
+ }
3386
+ const expectedHash = computeSkillHash(content);
3387
+ const stagedContent = readFileSync8(join11(stagingClaudeDir, "SKILL.md"), "utf-8");
3388
+ const stagedHash = computeSkillHash(stagedContent);
3389
+ if (stagedHash !== expectedHash) {
3390
+ throw new Error("Staged file hash does not match expected content");
3391
+ }
3392
+ } catch (err) {
3393
+ if (err instanceof SkillInstallError)
3394
+ throw err;
3395
+ throw new SkillInstallError(name, "validate", err);
3396
+ }
3397
+ try {
3398
+ mkdirSync8(join11(projectRoot, CLAUDE_SKILLS_DIR), { recursive: true });
3399
+ mkdirSync8(join11(projectRoot, AGENTS_SKILLS_DIR), { recursive: true });
3400
+ if (existsSync11(claudeDir)) {
3401
+ rmSync(claudeDir, { recursive: true, force: true });
3402
+ }
3403
+ if (existsSync11(agentsDir)) {
3404
+ rmSync(agentsDir, { recursive: true, force: true });
3405
+ }
3406
+ try {
3407
+ renameSync2(stagingClaudeDir, claudeDir);
3408
+ wroteClaudeDir = true;
3409
+ } catch {
3410
+ mkdirSync8(claudeDir, { recursive: true });
3411
+ writeFileSync8(join11(claudeDir, "SKILL.md"), content, "utf-8");
3412
+ wroteClaudeDir = true;
3413
+ }
3414
+ try {
3415
+ renameSync2(stagingAgentsDir, agentsDir);
3416
+ wroteAgentsDir = true;
3417
+ } catch {
3418
+ mkdirSync8(agentsDir, { recursive: true });
3419
+ writeFileSync8(join11(agentsDir, "SKILL.md"), content, "utf-8");
3420
+ wroteAgentsDir = true;
3421
+ }
3422
+ } catch (err) {
3423
+ if (err instanceof SkillInstallError)
3424
+ throw err;
3425
+ throw new SkillInstallError(name, "write", err);
3426
+ }
3427
+ try {
3428
+ const lock = readLockFile(projectRoot);
3429
+ lock.skills[name] = {
3430
+ source,
3431
+ sourceType: "github",
3432
+ computedHash: computeSkillHash(content)
3433
+ };
3434
+ writeLockFile(projectRoot, lock);
3435
+ } catch (err) {
3436
+ throw new SkillInstallError(name, "register", err);
3437
+ }
3438
+ } catch (err) {
3439
+ if (wroteClaudeDir && existsSync11(claudeDir)) {
3440
+ rmSync(claudeDir, { recursive: true, force: true });
3441
+ }
3442
+ if (wroteAgentsDir && existsSync11(agentsDir)) {
3443
+ rmSync(agentsDir, { recursive: true, force: true });
3444
+ }
3445
+ throw err;
3446
+ } finally {
3447
+ if (existsSync11(stagingDir)) {
3448
+ rmSync(stagingDir, { recursive: true, force: true });
3449
+ }
3450
+ }
3371
3451
  }
3372
3452
  async function removeSkill(projectRoot, name) {
3373
- if (!isSkillInstalled(projectRoot, name)) {
3453
+ const claudeDir = join11(projectRoot, CLAUDE_SKILLS_DIR, name);
3454
+ const agentsDir = join11(projectRoot, AGENTS_SKILLS_DIR, name);
3455
+ const lock = readLockFile(projectRoot);
3456
+ const inLockFile = name in lock.skills;
3457
+ const hasClaude = existsSync11(claudeDir);
3458
+ const hasAgents = existsSync11(agentsDir);
3459
+ if (!inLockFile && !hasClaude && !hasAgents) {
3374
3460
  console.warn(`Skill "${name}" is not installed.`);
3375
3461
  return;
3376
3462
  }
3377
- const claudeDir = join11(projectRoot, CLAUDE_SKILLS_DIR, name);
3378
- const agentsDir = join11(projectRoot, AGENTS_SKILLS_DIR, name);
3379
- if (existsSync11(claudeDir)) {
3463
+ if (hasClaude) {
3380
3464
  rmSync(claudeDir, { recursive: true, force: true });
3381
3465
  }
3382
- if (existsSync11(agentsDir)) {
3466
+ if (hasAgents) {
3383
3467
  rmSync(agentsDir, { recursive: true, force: true });
3384
3468
  }
3385
- const lock = readLockFile(projectRoot);
3386
- delete lock.skills[name];
3387
- writeLockFile(projectRoot, lock);
3469
+ if (inLockFile) {
3470
+ delete lock.skills[name];
3471
+ writeLockFile(projectRoot, lock);
3472
+ }
3388
3473
  }
3389
3474
  function isSkillInstalled(projectRoot, name) {
3390
3475
  const lock = readLockFile(projectRoot);
3391
3476
  return name in lock.skills;
3392
3477
  }
3478
+ function hasOrphanedSkillFiles(projectRoot, name) {
3479
+ const claudeDir = join11(projectRoot, CLAUDE_SKILLS_DIR, name);
3480
+ const agentsDir = join11(projectRoot, AGENTS_SKILLS_DIR, name);
3481
+ const inLockFile = isSkillInstalled(projectRoot, name);
3482
+ return !inLockFile && (existsSync11(claudeDir) || existsSync11(agentsDir));
3483
+ }
3484
+ var SkillInstallError;
3393
3485
  var init_installer = __esm(() => {
3394
3486
  init_lock();
3487
+ SkillInstallError = class SkillInstallError extends Error {
3488
+ skillName;
3489
+ step;
3490
+ constructor(skillName, step, cause) {
3491
+ const stepLabel = {
3492
+ stage: "staging files to temp directory",
3493
+ validate: "validating skill content",
3494
+ write: "writing skill files to project",
3495
+ register: "updating skills-lock.json"
3496
+ };
3497
+ const reason = cause instanceof Error ? cause.message : String(cause ?? "");
3498
+ super(`Failed to install '${skillName}' during ${stepLabel[step]}${reason ? `: ${reason}` : ""}`);
3499
+ this.skillName = skillName;
3500
+ this.step = step;
3501
+ this.name = "SkillInstallError";
3502
+ }
3503
+ };
3395
3504
  });
3396
3505
 
3397
3506
  // src/skills/registry.ts
@@ -3565,7 +3674,22 @@ async function installRemoteSkill(name) {
3565
3674
  }
3566
3675
  const cwd = process.cwd();
3567
3676
  const source = `${REGISTRY_REPO}/${entry.path}`;
3568
- await installSkill(cwd, name, content, source);
3677
+ try {
3678
+ await installSkill(cwd, name, content, source);
3679
+ } catch (err) {
3680
+ if (err instanceof SkillInstallError) {
3681
+ process.stderr.write(`${red2("✗")} ${err.message}
3682
+ `);
3683
+ process.stderr.write(` To clean up and retry: ${bold2(`locus skills remove ${name}`)} then ${bold2(`locus skills install ${name}`)}
3684
+ `);
3685
+ } else {
3686
+ process.stderr.write(`${red2("✗")} Unexpected error installing skill '${name}'.
3687
+ `);
3688
+ process.stderr.write(` ${dim2(err.message)}
3689
+ `);
3690
+ }
3691
+ process.exit(1);
3692
+ }
3569
3693
  process.stderr.write(`
3570
3694
  ${green("✓")} Installed skill '${bold2(name)}' from ${REGISTRY_REPO}
3571
3695
  `);
@@ -3584,7 +3708,9 @@ async function removeInstalledSkill(name) {
3584
3708
  process.exit(1);
3585
3709
  }
3586
3710
  const cwd = process.cwd();
3587
- if (!isSkillInstalled(cwd, name)) {
3711
+ const installed = isSkillInstalled(cwd, name);
3712
+ const orphaned = hasOrphanedSkillFiles(cwd, name);
3713
+ if (!installed && !orphaned) {
3588
3714
  process.stderr.write(`${red2("✗")} Skill '${bold2(name)}' is not installed.
3589
3715
  `);
3590
3716
  process.stderr.write(` Run ${bold2("locus skills list --installed")} to see installed skills.
@@ -3592,8 +3718,13 @@ async function removeInstalledSkill(name) {
3592
3718
  process.exit(1);
3593
3719
  }
3594
3720
  await removeSkill(cwd, name);
3595
- process.stderr.write(`${green("✓")} Removed skill '${bold2(name)}'
3721
+ if (orphaned) {
3722
+ process.stderr.write(`${green("✓")} Cleaned up orphaned skill files for '${bold2(name)}'
3723
+ `);
3724
+ } else {
3725
+ process.stderr.write(`${green("✓")} Removed skill '${bold2(name)}'
3596
3726
  `);
3727
+ }
3597
3728
  }
3598
3729
  async function updateSkills(name) {
3599
3730
  const cwd = process.cwd();
@@ -3649,7 +3780,20 @@ async function updateSkills(name) {
3649
3780
  continue;
3650
3781
  }
3651
3782
  const source = `${REGISTRY_REPO}/${entry.path}`;
3652
- await installSkill(cwd, skillName, content, source);
3783
+ try {
3784
+ await installSkill(cwd, skillName, content, source);
3785
+ } catch (err) {
3786
+ if (err instanceof SkillInstallError) {
3787
+ process.stderr.write(`${red2("✗")} ${err.message}
3788
+ `);
3789
+ process.stderr.write(` To clean up: ${bold2(`locus skills remove ${skillName}`)}
3790
+ `);
3791
+ } else {
3792
+ process.stderr.write(`${red2("✗")} Failed to update '${skillName}': ${dim2(err.message)}
3793
+ `);
3794
+ }
3795
+ continue;
3796
+ }
3653
3797
  updatedCount++;
3654
3798
  process.stderr.write(`${green("✓")} Updated '${bold2(skillName)}' (hash changed)
3655
3799
  `);
@@ -3743,7 +3887,7 @@ ${bold2("Subcommands:")}
3743
3887
  ${cyan2("list")} List available skills from the registry
3744
3888
  ${cyan2("list")} ${dim2("--installed")} List locally installed skills
3745
3889
  ${cyan2("install")} ${dim2("<name>")} Install a skill from the registry
3746
- ${cyan2("remove")} ${dim2("<name>")} Remove an installed skill
3890
+ ${cyan2("remove")} ${dim2("<name>")} Remove an installed skill (alias: ${cyan2("uninstall")})
3747
3891
  ${cyan2("update")} ${dim2("[name]")} Update installed skill(s) from registry
3748
3892
  ${cyan2("info")} ${dim2("<name>")} Show skill metadata and install status
3749
3893
 
@@ -3778,7 +3922,8 @@ async function skillsCommand(args, flags) {
3778
3922
  await installRemoteSkill(skillName);
3779
3923
  break;
3780
3924
  }
3781
- case "remove": {
3925
+ case "remove":
3926
+ case "uninstall": {
3782
3927
  const skillName = args[1];
3783
3928
  await removeInstalledSkill(skillName);
3784
3929
  break;
@@ -3796,7 +3941,7 @@ async function skillsCommand(args, flags) {
3796
3941
  default:
3797
3942
  process.stderr.write(`${red2("✗")} Unknown subcommand: ${bold2(subcommand)}
3798
3943
  `);
3799
- process.stderr.write(` Available: ${bold2("list")}, ${bold2("install")}, ${bold2("remove")}, ${bold2("update")}, ${bold2("info")}
3944
+ process.stderr.write(` Available: ${bold2("list")}, ${bold2("install")}, ${bold2("remove")} (${bold2("uninstall")}), ${bold2("update")}, ${bold2("info")}
3800
3945
  `);
3801
3946
  process.exit(1);
3802
3947
  }
@@ -3947,7 +4092,7 @@ __export(exports_logs, {
3947
4092
  import {
3948
4093
  existsSync as existsSync12,
3949
4094
  readdirSync as readdirSync2,
3950
- readFileSync as readFileSync8,
4095
+ readFileSync as readFileSync9,
3951
4096
  statSync as statSync2,
3952
4097
  unlinkSync as unlinkSync2
3953
4098
  } from "node:fs";
@@ -3974,7 +4119,7 @@ async function logsCommand(cwd, options) {
3974
4119
  return viewLog(logFiles[0], options.level, options.lines ?? 50);
3975
4120
  }
3976
4121
  function viewLog(logFile, levelFilter, maxLines) {
3977
- const content = readFileSync8(logFile, "utf-8");
4122
+ const content = readFileSync9(logFile, "utf-8");
3978
4123
  const lines = content.trim().split(`
3979
4124
  `).filter(Boolean);
3980
4125
  process.stderr.write(`
@@ -4011,7 +4156,7 @@ async function tailLog(logFile, levelFilter) {
4011
4156
  `);
4012
4157
  let lastSize = existsSync12(logFile) ? statSync2(logFile).size : 0;
4013
4158
  if (existsSync12(logFile)) {
4014
- const content = readFileSync8(logFile, "utf-8");
4159
+ const content = readFileSync9(logFile, "utf-8");
4015
4160
  const lines = content.trim().split(`
4016
4161
  `).filter(Boolean);
4017
4162
  const recent = lines.slice(-10);
@@ -4034,7 +4179,7 @@ async function tailLog(logFile, levelFilter) {
4034
4179
  const currentSize = statSync2(logFile).size;
4035
4180
  if (currentSize <= lastSize)
4036
4181
  return;
4037
- const content = readFileSync8(logFile, "utf-8");
4182
+ const content = readFileSync9(logFile, "utf-8");
4038
4183
  const allLines = content.trim().split(`
4039
4184
  `).filter(Boolean);
4040
4185
  const oldContent = content.slice(0, lastSize);
@@ -4400,7 +4545,7 @@ var init_stream_renderer = __esm(() => {
4400
4545
  // src/repl/clipboard.ts
4401
4546
  import { execSync as execSync6 } from "node:child_process";
4402
4547
  import { existsSync as existsSync13, mkdirSync as mkdirSync9 } from "node:fs";
4403
- import { tmpdir } from "node:os";
4548
+ import { tmpdir as tmpdir2 } from "node:os";
4404
4549
  import { join as join13 } from "node:path";
4405
4550
  function readClipboardImage() {
4406
4551
  if (process.platform === "darwin") {
@@ -4466,12 +4611,12 @@ function readLinuxClipboardImage() {
4466
4611
  }
4467
4612
  var STABLE_DIR;
4468
4613
  var init_clipboard = __esm(() => {
4469
- STABLE_DIR = join13(tmpdir(), "locus-images");
4614
+ STABLE_DIR = join13(tmpdir2(), "locus-images");
4470
4615
  });
4471
4616
 
4472
4617
  // src/repl/image-detect.ts
4473
4618
  import { copyFileSync, existsSync as existsSync14, mkdirSync as mkdirSync10 } from "node:fs";
4474
- import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
4619
+ import { homedir as homedir3, tmpdir as tmpdir3 } from "node:os";
4475
4620
  import { basename, extname, join as join14, resolve } from "node:path";
4476
4621
  function detectImages(input) {
4477
4622
  const detected = [];
@@ -4676,7 +4821,7 @@ var init_image_detect = __esm(() => {
4676
4821
  ".tif",
4677
4822
  ".tiff"
4678
4823
  ]);
4679
- STABLE_DIR2 = join14(tmpdir2(), "locus-images");
4824
+ STABLE_DIR2 = join14(tmpdir3(), "locus-images");
4680
4825
  PLACEHOLDER_ID_PATTERN = /\(locus:\/\/screenshot-(\d+)\)/g;
4681
4826
  });
4682
4827
 
@@ -5689,17 +5834,17 @@ import {
5689
5834
  mkdirSync as mkdirSync11,
5690
5835
  mkdtempSync,
5691
5836
  readdirSync as readdirSync3,
5692
- readFileSync as readFileSync9,
5837
+ readFileSync as readFileSync10,
5693
5838
  rmSync as rmSync2,
5694
5839
  statSync as statSync3
5695
5840
  } from "node:fs";
5696
- import { tmpdir as tmpdir3 } from "node:os";
5841
+ import { tmpdir as tmpdir4 } from "node:os";
5697
5842
  import { dirname as dirname3, join as join15, relative } from "node:path";
5698
5843
  import { promisify } from "node:util";
5699
5844
  function parseIgnoreFile(filePath) {
5700
5845
  if (!existsSync15(filePath))
5701
5846
  return [];
5702
- const content = readFileSync9(filePath, "utf-8");
5847
+ const content = readFileSync10(filePath, "utf-8");
5703
5848
  const rules = [];
5704
5849
  for (const rawLine of content.split(`
5705
5850
  `)) {
@@ -5810,7 +5955,7 @@ function backupIgnoredFiles(projectRoot) {
5810
5955
  return NOOP_BACKUP;
5811
5956
  let backupDir;
5812
5957
  try {
5813
- backupDir = mkdtempSync(join15(tmpdir3(), "locus-sandbox-backup-"));
5958
+ backupDir = mkdtempSync(join15(tmpdir4(), "locus-sandbox-backup-"));
5814
5959
  } catch (err) {
5815
5960
  log.debug("Failed to create sandbox backup dir", {
5816
5961
  error: err instanceof Error ? err.message : String(err)
@@ -8028,7 +8173,7 @@ var init_sprint = __esm(() => {
8028
8173
 
8029
8174
  // src/core/prompt-builder.ts
8030
8175
  import { execSync as execSync9 } from "node:child_process";
8031
- import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync10 } from "node:fs";
8176
+ import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
8032
8177
  import { join as join16 } from "node:path";
8033
8178
  function buildExecutionPrompt(ctx) {
8034
8179
  const sections = [];
@@ -8256,7 +8401,7 @@ function readFileSafe(path) {
8256
8401
  try {
8257
8402
  if (!existsSync16(path))
8258
8403
  return null;
8259
- return readFileSync10(path, "utf-8");
8404
+ return readFileSync11(path, "utf-8");
8260
8405
  } catch {
8261
8406
  return null;
8262
8407
  }
@@ -8825,7 +8970,7 @@ class CombinedCompletion {
8825
8970
  var init_completions = () => {};
8826
8971
 
8827
8972
  // src/repl/input-history.ts
8828
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "node:fs";
8973
+ import { existsSync as existsSync17, mkdirSync as mkdirSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
8829
8974
  import { dirname as dirname5, join as join18 } from "node:path";
8830
8975
 
8831
8976
  class InputHistory {
@@ -8873,7 +9018,7 @@ class InputHistory {
8873
9018
  try {
8874
9019
  if (!existsSync17(this.filePath))
8875
9020
  return;
8876
- const content = readFileSync11(this.filePath, "utf-8");
9021
+ const content = readFileSync12(this.filePath, "utf-8");
8877
9022
  this.entries = content.split(`
8878
9023
  `).map((line) => this.unescape(line)).filter(Boolean);
8879
9024
  } catch {}
@@ -8915,7 +9060,7 @@ import {
8915
9060
  existsSync as existsSync18,
8916
9061
  mkdirSync as mkdirSync13,
8917
9062
  readdirSync as readdirSync6,
8918
- readFileSync as readFileSync12,
9063
+ readFileSync as readFileSync13,
8919
9064
  unlinkSync as unlinkSync3,
8920
9065
  writeFileSync as writeFileSync10
8921
9066
  } from "node:fs";
@@ -8958,7 +9103,7 @@ class SessionManager {
8958
9103
  const exactPath = this.getSessionPath(idOrPrefix);
8959
9104
  if (existsSync18(exactPath)) {
8960
9105
  try {
8961
- return JSON.parse(readFileSync12(exactPath, "utf-8"));
9106
+ return JSON.parse(readFileSync13(exactPath, "utf-8"));
8962
9107
  } catch {
8963
9108
  return null;
8964
9109
  }
@@ -8966,7 +9111,7 @@ class SessionManager {
8966
9111
  const matches = files.filter((f) => basename3(f, ".json").startsWith(idOrPrefix));
8967
9112
  if (matches.length === 1) {
8968
9113
  try {
8969
- return JSON.parse(readFileSync12(matches[0], "utf-8"));
9114
+ return JSON.parse(readFileSync13(matches[0], "utf-8"));
8970
9115
  } catch {
8971
9116
  return null;
8972
9117
  }
@@ -8991,7 +9136,7 @@ class SessionManager {
8991
9136
  const sessions = [];
8992
9137
  for (const file of files) {
8993
9138
  try {
8994
- const session = JSON.parse(readFileSync12(file, "utf-8"));
9139
+ const session = JSON.parse(readFileSync13(file, "utf-8"));
8995
9140
  sessions.push({
8996
9141
  id: session.id,
8997
9142
  created: session.created,
@@ -9018,7 +9163,7 @@ class SessionManager {
9018
9163
  let pruned = 0;
9019
9164
  const withStats = files.map((f) => {
9020
9165
  try {
9021
- const session = JSON.parse(readFileSync12(f, "utf-8"));
9166
+ const session = JSON.parse(readFileSync13(f, "utf-8"));
9022
9167
  return { path: f, updated: new Date(session.updated).getTime() };
9023
9168
  } catch {
9024
9169
  return { path: f, updated: 0 };
@@ -9072,7 +9217,7 @@ var init_session_manager = __esm(() => {
9072
9217
  // src/repl/voice.ts
9073
9218
  import { execSync as execSync11, spawn as spawn6 } from "node:child_process";
9074
9219
  import { existsSync as existsSync19, mkdirSync as mkdirSync14, unlinkSync as unlinkSync4 } from "node:fs";
9075
- import { cpus, homedir as homedir4, platform, tmpdir as tmpdir4 } from "node:os";
9220
+ import { cpus, homedir as homedir4, platform, tmpdir as tmpdir5 } from "node:os";
9076
9221
  import { join as join20 } from "node:path";
9077
9222
  function getWhisperModelPath() {
9078
9223
  return join20(WHISPER_MODELS_DIR, `ggml-${WHISPER_MODEL}.bin`);
@@ -9284,7 +9429,7 @@ function ensureBuildDeps(pm) {
9284
9429
  }
9285
9430
  function buildWhisperFromSource(pm) {
9286
9431
  const out = process.stderr;
9287
- const buildDir = join20(tmpdir4(), `locus-whisper-build-${process.pid}`);
9432
+ const buildDir = join20(tmpdir5(), `locus-whisper-build-${process.pid}`);
9288
9433
  if (!ensureBuildDeps(pm)) {
9289
9434
  out.write(` ${red2("✗")} Could not install build tools (cmake, g++, git).
9290
9435
  `);
@@ -9442,7 +9587,7 @@ class VoiceController {
9442
9587
  onStateChange;
9443
9588
  constructor(options) {
9444
9589
  this.onStateChange = options.onStateChange;
9445
- this.tempFile = join20(tmpdir4(), `locus-voice-${process.pid}.wav`);
9590
+ this.tempFile = join20(tmpdir5(), `locus-voice-${process.pid}.wav`);
9446
9591
  this.deps = checkDependencies();
9447
9592
  }
9448
9593
  getState() {
@@ -10693,7 +10838,7 @@ var init_conflict = __esm(() => {
10693
10838
  import {
10694
10839
  existsSync as existsSync21,
10695
10840
  mkdirSync as mkdirSync15,
10696
- readFileSync as readFileSync13,
10841
+ readFileSync as readFileSync14,
10697
10842
  unlinkSync as unlinkSync5,
10698
10843
  writeFileSync as writeFileSync11
10699
10844
  } from "node:fs";
@@ -10716,7 +10861,7 @@ function loadRunState(projectRoot, sprintName) {
10716
10861
  if (!existsSync21(path))
10717
10862
  return null;
10718
10863
  try {
10719
- return JSON.parse(readFileSync13(path, "utf-8"));
10864
+ return JSON.parse(readFileSync14(path, "utf-8"));
10720
10865
  } catch {
10721
10866
  getLogger().warn("Corrupted run state file, ignoring");
10722
10867
  return null;
@@ -12302,7 +12447,7 @@ import {
12302
12447
  existsSync as existsSync25,
12303
12448
  mkdirSync as mkdirSync17,
12304
12449
  readdirSync as readdirSync8,
12305
- readFileSync as readFileSync14,
12450
+ readFileSync as readFileSync15,
12306
12451
  writeFileSync as writeFileSync12
12307
12452
  } from "node:fs";
12308
12453
  import { join as join26 } from "node:path";
@@ -12358,7 +12503,7 @@ function loadPlanFile(projectRoot, id) {
12358
12503
  if (!match)
12359
12504
  return null;
12360
12505
  try {
12361
- const content = readFileSync14(join26(dir, match), "utf-8");
12506
+ const content = readFileSync15(join26(dir, match), "utf-8");
12362
12507
  return JSON.parse(content);
12363
12508
  } catch {
12364
12509
  return null;
@@ -12443,7 +12588,7 @@ ${bold2("Saved Plans:")}
12443
12588
  for (const file of files) {
12444
12589
  const id = file.replace(".json", "");
12445
12590
  try {
12446
- const content = readFileSync14(join26(dir, file), "utf-8");
12591
+ const content = readFileSync15(join26(dir, file), "utf-8");
12447
12592
  const plan = JSON.parse(content);
12448
12593
  const date = plan.createdAt ? plan.createdAt.slice(0, 10) : "";
12449
12594
  const issueCount = Array.isArray(plan.issues) ? plan.issues.length : 0;
@@ -12558,7 +12703,7 @@ ${yellow2("⚠")} Plan file was not found at ${bold2(planPathRelative)}.
12558
12703
  }
12559
12704
  let updatedPlan;
12560
12705
  try {
12561
- const content = readFileSync14(planPath, "utf-8");
12706
+ const content = readFileSync15(planPath, "utf-8");
12562
12707
  updatedPlan = JSON.parse(content);
12563
12708
  } catch {
12564
12709
  process.stderr.write(`
@@ -12701,7 +12846,7 @@ ${yellow2("⚠")} Plan file was not created at ${bold2(planPathRelative)}.
12701
12846
  }
12702
12847
  let plan;
12703
12848
  try {
12704
- const content = readFileSync14(planPath, "utf-8");
12849
+ const content = readFileSync15(planPath, "utf-8");
12705
12850
  plan = JSON.parse(content);
12706
12851
  } catch {
12707
12852
  process.stderr.write(`
@@ -12875,14 +13020,14 @@ ${directive}${sprintName ? `
12875
13020
  </directive>`);
12876
13021
  const locusPath = join26(projectRoot, ".locus", "LOCUS.md");
12877
13022
  if (existsSync25(locusPath)) {
12878
- const content = readFileSync14(locusPath, "utf-8");
13023
+ const content = readFileSync15(locusPath, "utf-8");
12879
13024
  parts.push(`<project-context>
12880
13025
  ${content.slice(0, 3000)}
12881
13026
  </project-context>`);
12882
13027
  }
12883
13028
  const learningsPath = join26(projectRoot, ".locus", "LEARNINGS.md");
12884
13029
  if (existsSync25(learningsPath)) {
12885
- const content = readFileSync14(learningsPath, "utf-8");
13030
+ const content = readFileSync15(learningsPath, "utf-8");
12886
13031
  parts.push(`<past-learnings>
12887
13032
  ${content.slice(0, 2000)}
12888
13033
  </past-learnings>`);
@@ -12936,7 +13081,7 @@ ${feedback}
12936
13081
  </feedback>`);
12937
13082
  const locusPath = join26(projectRoot, ".locus", "LOCUS.md");
12938
13083
  if (existsSync25(locusPath)) {
12939
- const content = readFileSync14(locusPath, "utf-8");
13084
+ const content = readFileSync15(locusPath, "utf-8");
12940
13085
  parts.push(`<project-context>
12941
13086
  ${content.slice(0, 3000)}
12942
13087
  </project-context>`);
@@ -13118,7 +13263,7 @@ __export(exports_review, {
13118
13263
  reviewCommand: () => reviewCommand
13119
13264
  });
13120
13265
  import { execFileSync as execFileSync2, execSync as execSync19 } from "node:child_process";
13121
- import { existsSync as existsSync26, readFileSync as readFileSync15 } from "node:fs";
13266
+ import { existsSync as existsSync26, readFileSync as readFileSync16 } from "node:fs";
13122
13267
  import { join as join27 } from "node:path";
13123
13268
  function printHelp3() {
13124
13269
  process.stderr.write(`
@@ -13290,7 +13435,7 @@ You are an expert code reviewer for the ${config.github.owner}/${config.github.r
13290
13435
  </role>`);
13291
13436
  const locusPath = join27(projectRoot, ".locus", "LOCUS.md");
13292
13437
  if (existsSync26(locusPath)) {
13293
- const content = readFileSync15(locusPath, "utf-8");
13438
+ const content = readFileSync16(locusPath, "utf-8");
13294
13439
  parts.push(`<project-context>
13295
13440
  ${content.slice(0, 2000)}
13296
13441
  </project-context>`);
@@ -13608,7 +13753,7 @@ import {
13608
13753
  existsSync as existsSync27,
13609
13754
  mkdirSync as mkdirSync18,
13610
13755
  readdirSync as readdirSync9,
13611
- readFileSync as readFileSync16,
13756
+ readFileSync as readFileSync17,
13612
13757
  unlinkSync as unlinkSync6,
13613
13758
  writeFileSync as writeFileSync13
13614
13759
  } from "node:fs";
@@ -13690,7 +13835,7 @@ ${bold2("Discussions:")}
13690
13835
  `);
13691
13836
  for (const file of files) {
13692
13837
  const id = file.replace(".md", "");
13693
- const content = readFileSync16(join28(dir, file), "utf-8");
13838
+ const content = readFileSync17(join28(dir, file), "utf-8");
13694
13839
  const titleMatch = content.match(/^#\s+(.+)/m);
13695
13840
  const title = titleMatch ? titleMatch[1] : id;
13696
13841
  const dateMatch = content.match(/\*\*Date:\*\*\s*(.+)/);
@@ -13720,7 +13865,7 @@ function showDiscussion(projectRoot, id) {
13720
13865
  `);
13721
13866
  return;
13722
13867
  }
13723
- const content = readFileSync16(join28(dir, match), "utf-8");
13868
+ const content = readFileSync17(join28(dir, match), "utf-8");
13724
13869
  process.stdout.write(`${content}
13725
13870
  `);
13726
13871
  }
@@ -13768,7 +13913,7 @@ async function convertDiscussionToPlan(projectRoot, id) {
13768
13913
  `);
13769
13914
  return;
13770
13915
  }
13771
- const content = readFileSync16(join28(dir, match), "utf-8");
13916
+ const content = readFileSync17(join28(dir, match), "utf-8");
13772
13917
  const titleMatch = content.match(/^#\s+(.+)/m);
13773
13918
  const discussionTitle = titleMatch ? titleMatch[1].trim() : id;
13774
13919
  await planCommand(projectRoot, [
@@ -13911,14 +14056,14 @@ You are a senior software architect and consultant for the ${config.github.owner
13911
14056
  </role>`);
13912
14057
  const locusPath = join28(projectRoot, ".locus", "LOCUS.md");
13913
14058
  if (existsSync27(locusPath)) {
13914
- const content = readFileSync16(locusPath, "utf-8");
14059
+ const content = readFileSync17(locusPath, "utf-8");
13915
14060
  parts.push(`<project-context>
13916
14061
  ${content.slice(0, 3000)}
13917
14062
  </project-context>`);
13918
14063
  }
13919
14064
  const learningsPath = join28(projectRoot, ".locus", "LEARNINGS.md");
13920
14065
  if (existsSync27(learningsPath)) {
13921
- const content = readFileSync16(learningsPath, "utf-8");
14066
+ const content = readFileSync17(learningsPath, "utf-8");
13922
14067
  parts.push(`<past-learnings>
13923
14068
  ${content.slice(0, 2000)}
13924
14069
  </past-learnings>`);
@@ -13989,7 +14134,7 @@ __export(exports_artifacts, {
13989
14134
  formatDate: () => formatDate2,
13990
14135
  artifactsCommand: () => artifactsCommand
13991
14136
  });
13992
- import { existsSync as existsSync28, readdirSync as readdirSync10, readFileSync as readFileSync17, statSync as statSync5 } from "node:fs";
14137
+ import { existsSync as existsSync28, readdirSync as readdirSync10, readFileSync as readFileSync18, statSync as statSync5 } from "node:fs";
13993
14138
  import { join as join29 } from "node:path";
13994
14139
  function printHelp6() {
13995
14140
  process.stderr.write(`
@@ -14035,7 +14180,7 @@ function readArtifact(projectRoot, name) {
14035
14180
  return null;
14036
14181
  const stat = statSync5(filePath);
14037
14182
  return {
14038
- content: readFileSync17(filePath, "utf-8"),
14183
+ content: readFileSync18(filePath, "utf-8"),
14039
14184
  info: {
14040
14185
  name: fileName.replace(/\.md$/, ""),
14041
14186
  fileName,
@@ -14392,7 +14537,7 @@ __export(exports_sandbox2, {
14392
14537
  });
14393
14538
  import { execSync as execSync22, spawn as spawn7 } from "node:child_process";
14394
14539
  import { createHash as createHash2 } from "node:crypto";
14395
- import { existsSync as existsSync29, readFileSync as readFileSync18 } from "node:fs";
14540
+ import { existsSync as existsSync29, readFileSync as readFileSync19 } from "node:fs";
14396
14541
  import { basename as basename4, join as join30 } from "node:path";
14397
14542
  import { createInterface as createInterface3 } from "node:readline";
14398
14543
  function printSandboxHelp() {
@@ -14923,7 +15068,7 @@ async function handleLogs(projectRoot, args) {
14923
15068
  }
14924
15069
  function detectPackageManager2(projectRoot) {
14925
15070
  try {
14926
- const raw = readFileSync18(join30(projectRoot, "package.json"), "utf-8");
15071
+ const raw = readFileSync19(join30(projectRoot, "package.json"), "utf-8");
14927
15072
  const pkgJson = JSON.parse(raw);
14928
15073
  if (typeof pkgJson.packageManager === "string") {
14929
15074
  const name = pkgJson.packageManager.split("@")[0];
@@ -15216,7 +15361,7 @@ init_context();
15216
15361
  init_logger();
15217
15362
  init_rate_limiter();
15218
15363
  init_terminal();
15219
- import { existsSync as existsSync30, readFileSync as readFileSync19 } from "node:fs";
15364
+ import { existsSync as existsSync30, readFileSync as readFileSync20 } from "node:fs";
15220
15365
  import { join as join31 } from "node:path";
15221
15366
  import { fileURLToPath } from "node:url";
15222
15367
  function getCliVersion() {
@@ -15226,7 +15371,7 @@ function getCliVersion() {
15226
15371
  return fallbackVersion;
15227
15372
  }
15228
15373
  try {
15229
- const parsed = JSON.parse(readFileSync19(packageJsonPath, "utf-8"));
15374
+ const parsed = JSON.parse(readFileSync20(packageJsonPath, "utf-8"));
15230
15375
  return parsed.version ?? fallbackVersion;
15231
15376
  } catch {
15232
15377
  return fallbackVersion;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@locusai/cli",
3
- "version": "0.25.2",
3
+ "version": "0.25.3",
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.2",
39
+ "@locusai/sdk": "^0.25.3",
40
40
  "@types/bun": "latest",
41
41
  "typescript": "^5.8.3"
42
42
  },