@locusai/cli 0.23.4 → 0.24.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/bin/locus.js +312 -75
- package/package.json +2 -2
package/bin/locus.js
CHANGED
|
@@ -11282,6 +11282,9 @@ var exports_status = {};
|
|
|
11282
11282
|
__export(exports_status, {
|
|
11283
11283
|
statusCommand: () => statusCommand
|
|
11284
11284
|
});
|
|
11285
|
+
import { execSync as execSync18 } from "node:child_process";
|
|
11286
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
11287
|
+
import { dirname as dirname7, join as join22 } from "node:path";
|
|
11285
11288
|
async function statusCommand(projectRoot) {
|
|
11286
11289
|
const config = loadConfig(projectRoot);
|
|
11287
11290
|
const spinner = new Spinner;
|
|
@@ -11362,12 +11365,245 @@ async function statusCommand(projectRoot) {
|
|
|
11362
11365
|
}
|
|
11363
11366
|
}
|
|
11364
11367
|
} catch {}
|
|
11368
|
+
const agents = getActiveAgents(config.sandbox);
|
|
11369
|
+
if (agents.length > 0) {
|
|
11370
|
+
lines.push("");
|
|
11371
|
+
lines.push(` ${bold2("Agents:")}`);
|
|
11372
|
+
for (const agent of agents) {
|
|
11373
|
+
const icon = agent.status === "running" ? green("●") : dim2("○");
|
|
11374
|
+
const details = [agent.provider];
|
|
11375
|
+
if (agent.sandbox)
|
|
11376
|
+
details.push(dim2(`sandbox:${agent.sandbox}`));
|
|
11377
|
+
if (agent.pid)
|
|
11378
|
+
details.push(dim2(`pid:${agent.pid}`));
|
|
11379
|
+
if (agent.uptime)
|
|
11380
|
+
details.push(dim2(agent.uptime));
|
|
11381
|
+
if (agent.memory)
|
|
11382
|
+
details.push(dim2(agent.memory));
|
|
11383
|
+
lines.push(` ${icon} ${cyan2(agent.provider)} ${details.slice(1).join(" ")}`);
|
|
11384
|
+
if (agent.sandboxProcesses && agent.sandboxProcesses.length > 0) {
|
|
11385
|
+
for (const proc of agent.sandboxProcesses) {
|
|
11386
|
+
lines.push(` ${dim2("└")} ${yellow2(proc.name)} ${dim2(`pid:${proc.pid}`)}`);
|
|
11387
|
+
}
|
|
11388
|
+
} else if (agent.sandbox && agent.status === "running") {
|
|
11389
|
+
lines.push(` ${dim2("└ no agent processes detected")}`);
|
|
11390
|
+
}
|
|
11391
|
+
}
|
|
11392
|
+
}
|
|
11393
|
+
const registry = loadRegistry();
|
|
11394
|
+
const entries = Object.values(registry.packages);
|
|
11395
|
+
if (entries.length > 0) {
|
|
11396
|
+
const pm2Processes = getPm2Processes();
|
|
11397
|
+
lines.push("");
|
|
11398
|
+
lines.push(` ${bold2("Packages:")}`);
|
|
11399
|
+
for (const entry of entries) {
|
|
11400
|
+
const shortName = extractShortName(entry.name);
|
|
11401
|
+
const processName = `locus-${shortName}`;
|
|
11402
|
+
const proc = pm2Processes.find((p) => p.name === processName);
|
|
11403
|
+
const statusStr = proc ? proc.status === "online" ? green("online") : proc.status === "stopped" ? dim2("stopped") : yellow2(proc.status) : dim2("not running");
|
|
11404
|
+
const details = [`v${entry.version}`, statusStr];
|
|
11405
|
+
if (proc?.pid) {
|
|
11406
|
+
details.push(dim2(`pid:${proc.pid}`));
|
|
11407
|
+
}
|
|
11408
|
+
if (proc?.uptime) {
|
|
11409
|
+
details.push(dim2(formatUptime(proc.uptime)));
|
|
11410
|
+
}
|
|
11411
|
+
if (proc?.memory) {
|
|
11412
|
+
details.push(dim2(`${(proc.memory / (1024 * 1024)).toFixed(0)}MB`));
|
|
11413
|
+
}
|
|
11414
|
+
lines.push(` ${proc?.status === "online" ? green("●") : dim2("○")} ${cyan2(shortName)} ${details.join(" ")}`);
|
|
11415
|
+
}
|
|
11416
|
+
}
|
|
11365
11417
|
spinner.stop();
|
|
11366
11418
|
lines.push("");
|
|
11367
11419
|
process.stderr.write(`
|
|
11368
11420
|
${drawBox(lines, { title: "Locus Status" })}
|
|
11369
11421
|
`);
|
|
11370
11422
|
}
|
|
11423
|
+
function getPm2Bin() {
|
|
11424
|
+
const pkgsBin = join22(getPackagesDir(), "node_modules", ".bin", "pm2");
|
|
11425
|
+
if (existsSync22(pkgsBin))
|
|
11426
|
+
return pkgsBin;
|
|
11427
|
+
let dir = process.cwd();
|
|
11428
|
+
while (dir !== dirname7(dir)) {
|
|
11429
|
+
const candidate = join22(dir, "node_modules", ".bin", "pm2");
|
|
11430
|
+
if (existsSync22(candidate))
|
|
11431
|
+
return candidate;
|
|
11432
|
+
dir = dirname7(dir);
|
|
11433
|
+
}
|
|
11434
|
+
try {
|
|
11435
|
+
const result = execSync18("which pm2", {
|
|
11436
|
+
encoding: "utf-8",
|
|
11437
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
11438
|
+
}).trim();
|
|
11439
|
+
if (result)
|
|
11440
|
+
return result;
|
|
11441
|
+
} catch {}
|
|
11442
|
+
return "npx pm2";
|
|
11443
|
+
}
|
|
11444
|
+
function getPm2Processes() {
|
|
11445
|
+
try {
|
|
11446
|
+
const pm2 = getPm2Bin();
|
|
11447
|
+
const output = execSync18(`${pm2} jlist`, {
|
|
11448
|
+
encoding: "utf-8",
|
|
11449
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
11450
|
+
timeout: 5000
|
|
11451
|
+
});
|
|
11452
|
+
const processes = JSON.parse(output);
|
|
11453
|
+
return processes.map((p) => ({
|
|
11454
|
+
name: p.name,
|
|
11455
|
+
status: p.pm2_env?.status ?? "unknown",
|
|
11456
|
+
pid: p.pid ?? null,
|
|
11457
|
+
uptime: p.pm2_env?.pm_uptime ?? null,
|
|
11458
|
+
memory: p.monit?.memory ?? null
|
|
11459
|
+
}));
|
|
11460
|
+
} catch {
|
|
11461
|
+
return [];
|
|
11462
|
+
}
|
|
11463
|
+
}
|
|
11464
|
+
function getActiveAgents(sandboxConfig) {
|
|
11465
|
+
const agents = [];
|
|
11466
|
+
if (sandboxConfig.enabled) {
|
|
11467
|
+
const sandboxes = getSandboxProcesses();
|
|
11468
|
+
for (const [provider, name] of Object.entries(sandboxConfig.providers)) {
|
|
11469
|
+
if (!name)
|
|
11470
|
+
continue;
|
|
11471
|
+
const sb = sandboxes.find((s) => s.name === name);
|
|
11472
|
+
const isRunning = !!sb && sb.status === "running";
|
|
11473
|
+
let sandboxProcesses;
|
|
11474
|
+
if (isRunning) {
|
|
11475
|
+
sandboxProcesses = getProcessesInsideSandbox(name);
|
|
11476
|
+
}
|
|
11477
|
+
agents.push({
|
|
11478
|
+
provider,
|
|
11479
|
+
status: isRunning ? "running" : "stopped",
|
|
11480
|
+
sandbox: name,
|
|
11481
|
+
sandboxProcesses
|
|
11482
|
+
});
|
|
11483
|
+
}
|
|
11484
|
+
} else {
|
|
11485
|
+
const procs = getAgentProcesses();
|
|
11486
|
+
agents.push(...procs);
|
|
11487
|
+
}
|
|
11488
|
+
return agents;
|
|
11489
|
+
}
|
|
11490
|
+
function getSandboxProcesses() {
|
|
11491
|
+
try {
|
|
11492
|
+
const output = execSync18("docker sandbox ls", {
|
|
11493
|
+
encoding: "utf-8",
|
|
11494
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
11495
|
+
timeout: 5000
|
|
11496
|
+
});
|
|
11497
|
+
const lines = output.trim().split(`
|
|
11498
|
+
`);
|
|
11499
|
+
if (lines.length < 2)
|
|
11500
|
+
return [];
|
|
11501
|
+
const header = lines[0];
|
|
11502
|
+
const statusIdx = header.search(/\bSTATUS\b/i);
|
|
11503
|
+
return lines.slice(1).map((line) => {
|
|
11504
|
+
const parts = line.trim().split(/\s+/);
|
|
11505
|
+
const name = parts[0] ?? "";
|
|
11506
|
+
let status = "";
|
|
11507
|
+
if (statusIdx >= 0 && line.length > statusIdx) {
|
|
11508
|
+
const rest = line.substring(statusIdx).trim();
|
|
11509
|
+
status = (rest.split(/\s+/)[0] ?? "").toLowerCase();
|
|
11510
|
+
} else {
|
|
11511
|
+
status = (parts[parts.length - 2] ?? parts[1] ?? "").toLowerCase();
|
|
11512
|
+
}
|
|
11513
|
+
return { name, status };
|
|
11514
|
+
}).filter((s) => s.name.length > 0);
|
|
11515
|
+
} catch {
|
|
11516
|
+
return [];
|
|
11517
|
+
}
|
|
11518
|
+
}
|
|
11519
|
+
function getProcessesInsideSandbox(sandboxName) {
|
|
11520
|
+
try {
|
|
11521
|
+
const output = execSync18(`docker sandbox exec ${sandboxName} sh -c "ps -eo pid,pcpu,pmem,args"`, {
|
|
11522
|
+
encoding: "utf-8",
|
|
11523
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
11524
|
+
timeout: 1e4
|
|
11525
|
+
});
|
|
11526
|
+
const lines = output.trim().split(`
|
|
11527
|
+
`).slice(1);
|
|
11528
|
+
const processes = [];
|
|
11529
|
+
for (const line of lines) {
|
|
11530
|
+
const lower = line.toLowerCase();
|
|
11531
|
+
let agentName;
|
|
11532
|
+
if (lower.includes("claude"))
|
|
11533
|
+
agentName = "claude";
|
|
11534
|
+
else if (lower.includes("codex"))
|
|
11535
|
+
agentName = "codex";
|
|
11536
|
+
if (!agentName)
|
|
11537
|
+
continue;
|
|
11538
|
+
const pidMatch = line.trim().match(/^(\d+)/);
|
|
11539
|
+
if (!pidMatch)
|
|
11540
|
+
continue;
|
|
11541
|
+
processes.push({
|
|
11542
|
+
name: agentName,
|
|
11543
|
+
pid: pidMatch[1],
|
|
11544
|
+
command: ""
|
|
11545
|
+
});
|
|
11546
|
+
}
|
|
11547
|
+
return processes;
|
|
11548
|
+
} catch {
|
|
11549
|
+
return [];
|
|
11550
|
+
}
|
|
11551
|
+
}
|
|
11552
|
+
function getAgentProcesses() {
|
|
11553
|
+
const agents = [];
|
|
11554
|
+
try {
|
|
11555
|
+
const output = execSync18("ps -eo pid,pcpu,pmem,args", {
|
|
11556
|
+
encoding: "utf-8",
|
|
11557
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
11558
|
+
timeout: 5000
|
|
11559
|
+
});
|
|
11560
|
+
const lines = output.trim().split(`
|
|
11561
|
+
`).slice(1);
|
|
11562
|
+
const providerMatches = {};
|
|
11563
|
+
for (const line of lines) {
|
|
11564
|
+
const trimmed = line.trim();
|
|
11565
|
+
if (!trimmed)
|
|
11566
|
+
continue;
|
|
11567
|
+
const match = trimmed.match(/^(\d+)\s+([\d.]+)\s+([\d.]+)\s+(.+)$/);
|
|
11568
|
+
if (!match)
|
|
11569
|
+
continue;
|
|
11570
|
+
const [, pid, cpu, mem, command] = match;
|
|
11571
|
+
const cmdLower = command.toLowerCase();
|
|
11572
|
+
if (cmdLower.includes("ps -eo") || cmdLower.includes("grep") || cmdLower.includes("locus status") || cmdLower.includes("locus-cli"))
|
|
11573
|
+
continue;
|
|
11574
|
+
for (const provider of ["claude", "codex"]) {
|
|
11575
|
+
if (providerMatches[provider])
|
|
11576
|
+
continue;
|
|
11577
|
+
const binPattern = new RegExp(`(^|/)${provider}(\\s|$|-)`);
|
|
11578
|
+
if (binPattern.test(command)) {
|
|
11579
|
+
providerMatches[provider] = { pid, cpu, mem, command };
|
|
11580
|
+
}
|
|
11581
|
+
}
|
|
11582
|
+
}
|
|
11583
|
+
for (const [provider, info] of Object.entries(providerMatches)) {
|
|
11584
|
+
agents.push({
|
|
11585
|
+
provider,
|
|
11586
|
+
status: "running",
|
|
11587
|
+
pid: info.pid,
|
|
11588
|
+
memory: info.mem !== "0.0" ? `${info.mem}%` : undefined
|
|
11589
|
+
});
|
|
11590
|
+
}
|
|
11591
|
+
} catch {}
|
|
11592
|
+
return agents;
|
|
11593
|
+
}
|
|
11594
|
+
function formatUptime(pmUptime) {
|
|
11595
|
+
const seconds = Math.floor((Date.now() - pmUptime) / 1000);
|
|
11596
|
+
if (seconds < 60)
|
|
11597
|
+
return `${seconds}s`;
|
|
11598
|
+
const minutes = Math.floor(seconds / 60);
|
|
11599
|
+
if (minutes < 60)
|
|
11600
|
+
return `${minutes}m`;
|
|
11601
|
+
const hours = Math.floor(minutes / 60);
|
|
11602
|
+
if (hours < 24)
|
|
11603
|
+
return `${hours}h ${minutes % 60}m`;
|
|
11604
|
+
const days = Math.floor(hours / 24);
|
|
11605
|
+
return `${days}d ${hours % 24}h`;
|
|
11606
|
+
}
|
|
11371
11607
|
var init_status = __esm(() => {
|
|
11372
11608
|
init_config();
|
|
11373
11609
|
init_github();
|
|
@@ -11375,6 +11611,7 @@ var init_status = __esm(() => {
|
|
|
11375
11611
|
init_worktree();
|
|
11376
11612
|
init_progress();
|
|
11377
11613
|
init_terminal();
|
|
11614
|
+
init_registry();
|
|
11378
11615
|
});
|
|
11379
11616
|
|
|
11380
11617
|
// src/commands/plan.ts
|
|
@@ -11386,13 +11623,13 @@ __export(exports_plan, {
|
|
|
11386
11623
|
parsePlanArgs: () => parsePlanArgs
|
|
11387
11624
|
});
|
|
11388
11625
|
import {
|
|
11389
|
-
existsSync as
|
|
11626
|
+
existsSync as existsSync23,
|
|
11390
11627
|
mkdirSync as mkdirSync15,
|
|
11391
11628
|
readdirSync as readdirSync8,
|
|
11392
11629
|
readFileSync as readFileSync13,
|
|
11393
11630
|
writeFileSync as writeFileSync10
|
|
11394
11631
|
} from "node:fs";
|
|
11395
|
-
import { join as
|
|
11632
|
+
import { join as join23 } from "node:path";
|
|
11396
11633
|
function printHelp2() {
|
|
11397
11634
|
process.stderr.write(`
|
|
11398
11635
|
${bold2("locus plan")} — AI-powered sprint planning
|
|
@@ -11422,11 +11659,11 @@ function normalizeSprintName(name) {
|
|
|
11422
11659
|
return name.trim().toLowerCase();
|
|
11423
11660
|
}
|
|
11424
11661
|
function getPlansDir(projectRoot) {
|
|
11425
|
-
return
|
|
11662
|
+
return join23(projectRoot, ".locus", "plans");
|
|
11426
11663
|
}
|
|
11427
11664
|
function ensurePlansDir(projectRoot) {
|
|
11428
11665
|
const dir = getPlansDir(projectRoot);
|
|
11429
|
-
if (!
|
|
11666
|
+
if (!existsSync23(dir)) {
|
|
11430
11667
|
mkdirSync15(dir, { recursive: true });
|
|
11431
11668
|
}
|
|
11432
11669
|
return dir;
|
|
@@ -11436,14 +11673,14 @@ function generateId() {
|
|
|
11436
11673
|
}
|
|
11437
11674
|
function loadPlanFile(projectRoot, id) {
|
|
11438
11675
|
const dir = getPlansDir(projectRoot);
|
|
11439
|
-
if (!
|
|
11676
|
+
if (!existsSync23(dir))
|
|
11440
11677
|
return null;
|
|
11441
11678
|
const files = readdirSync8(dir).filter((f) => f.endsWith(".json"));
|
|
11442
11679
|
const match = files.find((f) => f.startsWith(id));
|
|
11443
11680
|
if (!match)
|
|
11444
11681
|
return null;
|
|
11445
11682
|
try {
|
|
11446
|
-
const content = readFileSync13(
|
|
11683
|
+
const content = readFileSync13(join23(dir, match), "utf-8");
|
|
11447
11684
|
return JSON.parse(content);
|
|
11448
11685
|
} catch {
|
|
11449
11686
|
return null;
|
|
@@ -11497,7 +11734,7 @@ async function planCommand(projectRoot, args, flags = {}) {
|
|
|
11497
11734
|
}
|
|
11498
11735
|
function handleListPlans(projectRoot) {
|
|
11499
11736
|
const dir = getPlansDir(projectRoot);
|
|
11500
|
-
if (!
|
|
11737
|
+
if (!existsSync23(dir)) {
|
|
11501
11738
|
process.stderr.write(`${dim2("No saved plans yet.")}
|
|
11502
11739
|
`);
|
|
11503
11740
|
return;
|
|
@@ -11515,7 +11752,7 @@ ${bold2("Saved Plans:")}
|
|
|
11515
11752
|
for (const file of files) {
|
|
11516
11753
|
const id = file.replace(".json", "");
|
|
11517
11754
|
try {
|
|
11518
|
-
const content = readFileSync13(
|
|
11755
|
+
const content = readFileSync13(join23(dir, file), "utf-8");
|
|
11519
11756
|
const plan = JSON.parse(content);
|
|
11520
11757
|
const date = plan.createdAt ? plan.createdAt.slice(0, 10) : "";
|
|
11521
11758
|
const issueCount = Array.isArray(plan.issues) ? plan.issues.length : 0;
|
|
@@ -11626,7 +11863,7 @@ ${bold2("Approving plan:")}
|
|
|
11626
11863
|
async function handleAIPlan(projectRoot, config, directive, sprintName, flags) {
|
|
11627
11864
|
const id = generateId();
|
|
11628
11865
|
const plansDir = ensurePlansDir(projectRoot);
|
|
11629
|
-
const planPath =
|
|
11866
|
+
const planPath = join23(plansDir, `${id}.json`);
|
|
11630
11867
|
const planPathRelative = `.locus/plans/${id}.json`;
|
|
11631
11868
|
const displayDirective = directive;
|
|
11632
11869
|
process.stderr.write(`
|
|
@@ -11669,7 +11906,7 @@ ${red2("✗")} Planning failed: ${aiResult.error}
|
|
|
11669
11906
|
`);
|
|
11670
11907
|
return;
|
|
11671
11908
|
}
|
|
11672
|
-
if (!
|
|
11909
|
+
if (!existsSync23(planPath)) {
|
|
11673
11910
|
process.stderr.write(`
|
|
11674
11911
|
${yellow2("⚠")} Plan file was not created at ${bold2(planPathRelative)}.
|
|
11675
11912
|
`);
|
|
@@ -11851,15 +12088,15 @@ ${directive}${sprintName ? `
|
|
|
11851
12088
|
|
|
11852
12089
|
**Sprint:** ${sprintName}` : ""}
|
|
11853
12090
|
</directive>`);
|
|
11854
|
-
const locusPath =
|
|
11855
|
-
if (
|
|
12091
|
+
const locusPath = join23(projectRoot, ".locus", "LOCUS.md");
|
|
12092
|
+
if (existsSync23(locusPath)) {
|
|
11856
12093
|
const content = readFileSync13(locusPath, "utf-8");
|
|
11857
12094
|
parts.push(`<project-context>
|
|
11858
12095
|
${content.slice(0, 3000)}
|
|
11859
12096
|
</project-context>`);
|
|
11860
12097
|
}
|
|
11861
|
-
const learningsPath =
|
|
11862
|
-
if (
|
|
12098
|
+
const learningsPath = join23(projectRoot, ".locus", "LEARNINGS.md");
|
|
12099
|
+
if (existsSync23(learningsPath)) {
|
|
11863
12100
|
const content = readFileSync13(learningsPath, "utf-8");
|
|
11864
12101
|
parts.push(`<past-learnings>
|
|
11865
12102
|
${content.slice(0, 2000)}
|
|
@@ -12039,9 +12276,9 @@ var exports_review = {};
|
|
|
12039
12276
|
__export(exports_review, {
|
|
12040
12277
|
reviewCommand: () => reviewCommand
|
|
12041
12278
|
});
|
|
12042
|
-
import { execFileSync as execFileSync2, execSync as
|
|
12043
|
-
import { existsSync as
|
|
12044
|
-
import { join as
|
|
12279
|
+
import { execFileSync as execFileSync2, execSync as execSync19 } from "node:child_process";
|
|
12280
|
+
import { existsSync as existsSync24, readFileSync as readFileSync14 } from "node:fs";
|
|
12281
|
+
import { join as join24 } from "node:path";
|
|
12045
12282
|
function printHelp3() {
|
|
12046
12283
|
process.stderr.write(`
|
|
12047
12284
|
${bold2("locus review")} — AI-powered code review
|
|
@@ -12125,7 +12362,7 @@ ${bold2("Review complete:")} ${green(`✓ ${reviewed}`)}${failed > 0 ? ` ${red2(
|
|
|
12125
12362
|
async function reviewSinglePR(projectRoot, config, prNumber, focus, flags) {
|
|
12126
12363
|
let prInfo;
|
|
12127
12364
|
try {
|
|
12128
|
-
const result =
|
|
12365
|
+
const result = execSync19(`gh pr view ${prNumber} --json number,title,body,state,headRefName,baseRefName,labels,url,createdAt`, { cwd: projectRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
12129
12366
|
const raw = JSON.parse(result);
|
|
12130
12367
|
prInfo = {
|
|
12131
12368
|
number: raw.number,
|
|
@@ -12210,8 +12447,8 @@ function buildReviewPrompt(projectRoot, config, pr, diff, focus) {
|
|
|
12210
12447
|
parts.push(`<role>
|
|
12211
12448
|
You are an expert code reviewer for the ${config.github.owner}/${config.github.repo} repository.
|
|
12212
12449
|
</role>`);
|
|
12213
|
-
const locusPath =
|
|
12214
|
-
if (
|
|
12450
|
+
const locusPath = join24(projectRoot, ".locus", "LOCUS.md");
|
|
12451
|
+
if (existsSync24(locusPath)) {
|
|
12215
12452
|
const content = readFileSync14(locusPath, "utf-8");
|
|
12216
12453
|
parts.push(`<project-context>
|
|
12217
12454
|
${content.slice(0, 2000)}
|
|
@@ -12273,7 +12510,7 @@ var exports_iterate = {};
|
|
|
12273
12510
|
__export(exports_iterate, {
|
|
12274
12511
|
iterateCommand: () => iterateCommand
|
|
12275
12512
|
});
|
|
12276
|
-
import { execSync as
|
|
12513
|
+
import { execSync as execSync20 } from "node:child_process";
|
|
12277
12514
|
function printHelp4() {
|
|
12278
12515
|
process.stderr.write(`
|
|
12279
12516
|
${bold2("locus iterate")} — Re-execute tasks with PR feedback
|
|
@@ -12491,12 +12728,12 @@ ${bold2("Summary:")} ${green(`✓ ${succeeded}`)}${failed > 0 ? ` ${red2(`✗ ${
|
|
|
12491
12728
|
}
|
|
12492
12729
|
function findPRForIssue(projectRoot, issueNumber) {
|
|
12493
12730
|
try {
|
|
12494
|
-
const result =
|
|
12731
|
+
const result = execSync20(`gh pr list --search "Closes #${issueNumber}" --json number --state open`, { cwd: projectRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
12495
12732
|
const parsed = JSON.parse(result);
|
|
12496
12733
|
if (parsed.length > 0) {
|
|
12497
12734
|
return parsed[0].number;
|
|
12498
12735
|
}
|
|
12499
|
-
const branchResult =
|
|
12736
|
+
const branchResult = execSync20(`gh pr list --head "locus/issue-${issueNumber}" --json number --state open`, { cwd: projectRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
12500
12737
|
const branchParsed = JSON.parse(branchResult);
|
|
12501
12738
|
if (branchParsed.length > 0) {
|
|
12502
12739
|
return branchParsed[0].number;
|
|
@@ -12532,14 +12769,14 @@ __export(exports_discuss, {
|
|
|
12532
12769
|
discussCommand: () => discussCommand
|
|
12533
12770
|
});
|
|
12534
12771
|
import {
|
|
12535
|
-
existsSync as
|
|
12772
|
+
existsSync as existsSync25,
|
|
12536
12773
|
mkdirSync as mkdirSync16,
|
|
12537
12774
|
readdirSync as readdirSync9,
|
|
12538
12775
|
readFileSync as readFileSync15,
|
|
12539
12776
|
unlinkSync as unlinkSync6,
|
|
12540
12777
|
writeFileSync as writeFileSync11
|
|
12541
12778
|
} from "node:fs";
|
|
12542
|
-
import { join as
|
|
12779
|
+
import { join as join25 } from "node:path";
|
|
12543
12780
|
function printHelp5() {
|
|
12544
12781
|
process.stderr.write(`
|
|
12545
12782
|
${bold2("locus discuss")} — AI-powered architectural discussions
|
|
@@ -12561,11 +12798,11 @@ ${bold2("Examples:")}
|
|
|
12561
12798
|
`);
|
|
12562
12799
|
}
|
|
12563
12800
|
function getDiscussionsDir(projectRoot) {
|
|
12564
|
-
return
|
|
12801
|
+
return join25(projectRoot, ".locus", "discussions");
|
|
12565
12802
|
}
|
|
12566
12803
|
function ensureDiscussionsDir(projectRoot) {
|
|
12567
12804
|
const dir = getDiscussionsDir(projectRoot);
|
|
12568
|
-
if (!
|
|
12805
|
+
if (!existsSync25(dir)) {
|
|
12569
12806
|
mkdirSync16(dir, { recursive: true });
|
|
12570
12807
|
}
|
|
12571
12808
|
return dir;
|
|
@@ -12600,7 +12837,7 @@ async function discussCommand(projectRoot, args, flags = {}) {
|
|
|
12600
12837
|
}
|
|
12601
12838
|
function listDiscussions(projectRoot) {
|
|
12602
12839
|
const dir = getDiscussionsDir(projectRoot);
|
|
12603
|
-
if (!
|
|
12840
|
+
if (!existsSync25(dir)) {
|
|
12604
12841
|
process.stderr.write(`${dim2("No discussions yet.")}
|
|
12605
12842
|
`);
|
|
12606
12843
|
return;
|
|
@@ -12617,7 +12854,7 @@ ${bold2("Discussions:")}
|
|
|
12617
12854
|
`);
|
|
12618
12855
|
for (const file of files) {
|
|
12619
12856
|
const id = file.replace(".md", "");
|
|
12620
|
-
const content = readFileSync15(
|
|
12857
|
+
const content = readFileSync15(join25(dir, file), "utf-8");
|
|
12621
12858
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
12622
12859
|
const title = titleMatch ? titleMatch[1] : id;
|
|
12623
12860
|
const dateMatch = content.match(/\*\*Date:\*\*\s*(.+)/);
|
|
@@ -12635,7 +12872,7 @@ function showDiscussion(projectRoot, id) {
|
|
|
12635
12872
|
return;
|
|
12636
12873
|
}
|
|
12637
12874
|
const dir = getDiscussionsDir(projectRoot);
|
|
12638
|
-
if (!
|
|
12875
|
+
if (!existsSync25(dir)) {
|
|
12639
12876
|
process.stderr.write(`${red2("✗")} No discussions found.
|
|
12640
12877
|
`);
|
|
12641
12878
|
return;
|
|
@@ -12647,7 +12884,7 @@ function showDiscussion(projectRoot, id) {
|
|
|
12647
12884
|
`);
|
|
12648
12885
|
return;
|
|
12649
12886
|
}
|
|
12650
|
-
const content = readFileSync15(
|
|
12887
|
+
const content = readFileSync15(join25(dir, match), "utf-8");
|
|
12651
12888
|
process.stdout.write(`${content}
|
|
12652
12889
|
`);
|
|
12653
12890
|
}
|
|
@@ -12658,7 +12895,7 @@ function deleteDiscussion(projectRoot, id) {
|
|
|
12658
12895
|
return;
|
|
12659
12896
|
}
|
|
12660
12897
|
const dir = getDiscussionsDir(projectRoot);
|
|
12661
|
-
if (!
|
|
12898
|
+
if (!existsSync25(dir)) {
|
|
12662
12899
|
process.stderr.write(`${red2("✗")} No discussions found.
|
|
12663
12900
|
`);
|
|
12664
12901
|
return;
|
|
@@ -12670,7 +12907,7 @@ function deleteDiscussion(projectRoot, id) {
|
|
|
12670
12907
|
`);
|
|
12671
12908
|
return;
|
|
12672
12909
|
}
|
|
12673
|
-
unlinkSync6(
|
|
12910
|
+
unlinkSync6(join25(dir, match));
|
|
12674
12911
|
process.stderr.write(`${green("✓")} Deleted discussion: ${match.replace(".md", "")}
|
|
12675
12912
|
`);
|
|
12676
12913
|
}
|
|
@@ -12683,7 +12920,7 @@ async function convertDiscussionToPlan(projectRoot, id) {
|
|
|
12683
12920
|
return;
|
|
12684
12921
|
}
|
|
12685
12922
|
const dir = getDiscussionsDir(projectRoot);
|
|
12686
|
-
if (!
|
|
12923
|
+
if (!existsSync25(dir)) {
|
|
12687
12924
|
process.stderr.write(`${red2("✗")} No discussions found.
|
|
12688
12925
|
`);
|
|
12689
12926
|
return;
|
|
@@ -12695,7 +12932,7 @@ async function convertDiscussionToPlan(projectRoot, id) {
|
|
|
12695
12932
|
`);
|
|
12696
12933
|
return;
|
|
12697
12934
|
}
|
|
12698
|
-
const content = readFileSync15(
|
|
12935
|
+
const content = readFileSync15(join25(dir, match), "utf-8");
|
|
12699
12936
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
12700
12937
|
const discussionTitle = titleMatch ? titleMatch[1].trim() : id;
|
|
12701
12938
|
await planCommand(projectRoot, [
|
|
@@ -12821,7 +13058,7 @@ ${turn.content}`;
|
|
|
12821
13058
|
...conversation.length > 1 ? [`---`, ``, `## Discussion Transcript`, ``, transcript, ``] : []
|
|
12822
13059
|
].join(`
|
|
12823
13060
|
`);
|
|
12824
|
-
writeFileSync11(
|
|
13061
|
+
writeFileSync11(join25(dir, `${id}.md`), markdown, "utf-8");
|
|
12825
13062
|
process.stderr.write(`
|
|
12826
13063
|
${green("✓")} Discussion saved: ${cyan2(id)} ${dim2(`(${timer.formatted()})`)}
|
|
12827
13064
|
`);
|
|
@@ -12836,15 +13073,15 @@ function buildDiscussionPrompt(projectRoot, config, topic, conversation, forceFi
|
|
|
12836
13073
|
parts.push(`<role>
|
|
12837
13074
|
You are a senior software architect and consultant for the ${config.github.owner}/${config.github.repo} project.
|
|
12838
13075
|
</role>`);
|
|
12839
|
-
const locusPath =
|
|
12840
|
-
if (
|
|
13076
|
+
const locusPath = join25(projectRoot, ".locus", "LOCUS.md");
|
|
13077
|
+
if (existsSync25(locusPath)) {
|
|
12841
13078
|
const content = readFileSync15(locusPath, "utf-8");
|
|
12842
13079
|
parts.push(`<project-context>
|
|
12843
13080
|
${content.slice(0, 3000)}
|
|
12844
13081
|
</project-context>`);
|
|
12845
13082
|
}
|
|
12846
|
-
const learningsPath =
|
|
12847
|
-
if (
|
|
13083
|
+
const learningsPath = join25(projectRoot, ".locus", "LEARNINGS.md");
|
|
13084
|
+
if (existsSync25(learningsPath)) {
|
|
12848
13085
|
const content = readFileSync15(learningsPath, "utf-8");
|
|
12849
13086
|
parts.push(`<past-learnings>
|
|
12850
13087
|
${content.slice(0, 2000)}
|
|
@@ -12916,8 +13153,8 @@ __export(exports_artifacts, {
|
|
|
12916
13153
|
formatDate: () => formatDate2,
|
|
12917
13154
|
artifactsCommand: () => artifactsCommand
|
|
12918
13155
|
});
|
|
12919
|
-
import { existsSync as
|
|
12920
|
-
import { join as
|
|
13156
|
+
import { existsSync as existsSync26, readdirSync as readdirSync10, readFileSync as readFileSync16, statSync as statSync5 } from "node:fs";
|
|
13157
|
+
import { join as join26 } from "node:path";
|
|
12921
13158
|
function printHelp6() {
|
|
12922
13159
|
process.stderr.write(`
|
|
12923
13160
|
${bold2("locus artifacts")} — View and manage AI-generated artifacts
|
|
@@ -12937,14 +13174,14 @@ ${dim2("Artifact names support partial matching.")}
|
|
|
12937
13174
|
`);
|
|
12938
13175
|
}
|
|
12939
13176
|
function getArtifactsDir(projectRoot) {
|
|
12940
|
-
return
|
|
13177
|
+
return join26(projectRoot, ".locus", "artifacts");
|
|
12941
13178
|
}
|
|
12942
13179
|
function listArtifacts(projectRoot) {
|
|
12943
13180
|
const dir = getArtifactsDir(projectRoot);
|
|
12944
|
-
if (!
|
|
13181
|
+
if (!existsSync26(dir))
|
|
12945
13182
|
return [];
|
|
12946
13183
|
return readdirSync10(dir).filter((f) => f.endsWith(".md")).map((fileName) => {
|
|
12947
|
-
const filePath =
|
|
13184
|
+
const filePath = join26(dir, fileName);
|
|
12948
13185
|
const stat = statSync5(filePath);
|
|
12949
13186
|
return {
|
|
12950
13187
|
name: fileName.replace(/\.md$/, ""),
|
|
@@ -12957,8 +13194,8 @@ function listArtifacts(projectRoot) {
|
|
|
12957
13194
|
function readArtifact(projectRoot, name) {
|
|
12958
13195
|
const dir = getArtifactsDir(projectRoot);
|
|
12959
13196
|
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
12960
|
-
const filePath =
|
|
12961
|
-
if (!
|
|
13197
|
+
const filePath = join26(dir, fileName);
|
|
13198
|
+
if (!existsSync26(filePath))
|
|
12962
13199
|
return null;
|
|
12963
13200
|
const stat = statSync5(filePath);
|
|
12964
13201
|
return {
|
|
@@ -13125,7 +13362,7 @@ var exports_commit = {};
|
|
|
13125
13362
|
__export(exports_commit, {
|
|
13126
13363
|
commitCommand: () => commitCommand
|
|
13127
13364
|
});
|
|
13128
|
-
import { execSync as
|
|
13365
|
+
import { execSync as execSync21 } from "node:child_process";
|
|
13129
13366
|
function printCommitHelp() {
|
|
13130
13367
|
process.stderr.write(`
|
|
13131
13368
|
${bold2("locus commit")} — AI-powered commit message generation
|
|
@@ -13159,7 +13396,7 @@ async function commitCommand(projectRoot, args, flags = {}) {
|
|
|
13159
13396
|
const config = loadConfig(projectRoot);
|
|
13160
13397
|
let stagedDiff;
|
|
13161
13398
|
try {
|
|
13162
|
-
stagedDiff =
|
|
13399
|
+
stagedDiff = execSync21("git diff --cached", {
|
|
13163
13400
|
cwd: projectRoot,
|
|
13164
13401
|
encoding: "utf-8",
|
|
13165
13402
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13176,7 +13413,7 @@ async function commitCommand(projectRoot, args, flags = {}) {
|
|
|
13176
13413
|
}
|
|
13177
13414
|
let stagedStat;
|
|
13178
13415
|
try {
|
|
13179
|
-
stagedStat =
|
|
13416
|
+
stagedStat = execSync21("git diff --cached --stat", {
|
|
13180
13417
|
cwd: projectRoot,
|
|
13181
13418
|
encoding: "utf-8",
|
|
13182
13419
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13186,7 +13423,7 @@ async function commitCommand(projectRoot, args, flags = {}) {
|
|
|
13186
13423
|
}
|
|
13187
13424
|
let recentCommits;
|
|
13188
13425
|
try {
|
|
13189
|
-
recentCommits =
|
|
13426
|
+
recentCommits = execSync21("git log --oneline -10", {
|
|
13190
13427
|
cwd: projectRoot,
|
|
13191
13428
|
encoding: "utf-8",
|
|
13192
13429
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13246,7 +13483,7 @@ Co-Authored-By: LocusAgent <agent@locusai.team>`;
|
|
|
13246
13483
|
return;
|
|
13247
13484
|
}
|
|
13248
13485
|
try {
|
|
13249
|
-
|
|
13486
|
+
execSync21("git commit -F -", {
|
|
13250
13487
|
input: fullMessage,
|
|
13251
13488
|
cwd: projectRoot,
|
|
13252
13489
|
encoding: "utf-8",
|
|
@@ -13317,10 +13554,10 @@ __export(exports_sandbox2, {
|
|
|
13317
13554
|
parseSandboxLogsArgs: () => parseSandboxLogsArgs,
|
|
13318
13555
|
parseSandboxInstallArgs: () => parseSandboxInstallArgs
|
|
13319
13556
|
});
|
|
13320
|
-
import { execSync as
|
|
13557
|
+
import { execSync as execSync22, spawn as spawn7 } from "node:child_process";
|
|
13321
13558
|
import { createHash } from "node:crypto";
|
|
13322
|
-
import { existsSync as
|
|
13323
|
-
import { basename as basename4, join as
|
|
13559
|
+
import { existsSync as existsSync27, readFileSync as readFileSync17 } from "node:fs";
|
|
13560
|
+
import { basename as basename4, join as join27 } from "node:path";
|
|
13324
13561
|
import { createInterface as createInterface3 } from "node:readline";
|
|
13325
13562
|
function printSandboxHelp() {
|
|
13326
13563
|
process.stderr.write(`
|
|
@@ -13562,7 +13799,7 @@ function handleRemove(projectRoot) {
|
|
|
13562
13799
|
process.stderr.write(`Removing sandbox ${bold2(sandboxName)}...
|
|
13563
13800
|
`);
|
|
13564
13801
|
try {
|
|
13565
|
-
|
|
13802
|
+
execSync22(`docker sandbox rm ${sandboxName}`, {
|
|
13566
13803
|
encoding: "utf-8",
|
|
13567
13804
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13568
13805
|
timeout: 15000
|
|
@@ -13850,7 +14087,7 @@ async function handleLogs(projectRoot, args) {
|
|
|
13850
14087
|
}
|
|
13851
14088
|
function detectPackageManager2(projectRoot) {
|
|
13852
14089
|
try {
|
|
13853
|
-
const raw = readFileSync17(
|
|
14090
|
+
const raw = readFileSync17(join27(projectRoot, "package.json"), "utf-8");
|
|
13854
14091
|
const pkgJson = JSON.parse(raw);
|
|
13855
14092
|
if (typeof pkgJson.packageManager === "string") {
|
|
13856
14093
|
const name = pkgJson.packageManager.split("@")[0];
|
|
@@ -13859,13 +14096,13 @@ function detectPackageManager2(projectRoot) {
|
|
|
13859
14096
|
}
|
|
13860
14097
|
}
|
|
13861
14098
|
} catch {}
|
|
13862
|
-
if (
|
|
14099
|
+
if (existsSync27(join27(projectRoot, "bun.lock")) || existsSync27(join27(projectRoot, "bun.lockb"))) {
|
|
13863
14100
|
return "bun";
|
|
13864
14101
|
}
|
|
13865
|
-
if (
|
|
14102
|
+
if (existsSync27(join27(projectRoot, "yarn.lock"))) {
|
|
13866
14103
|
return "yarn";
|
|
13867
14104
|
}
|
|
13868
|
-
if (
|
|
14105
|
+
if (existsSync27(join27(projectRoot, "pnpm-lock.yaml"))) {
|
|
13869
14106
|
return "pnpm";
|
|
13870
14107
|
}
|
|
13871
14108
|
return "npm";
|
|
@@ -13885,7 +14122,7 @@ function getInstallCommand(pm) {
|
|
|
13885
14122
|
function verifyBinEntries(sandboxName, workdir) {
|
|
13886
14123
|
try {
|
|
13887
14124
|
const binDir = `${workdir}/node_modules/.bin/`;
|
|
13888
|
-
const result =
|
|
14125
|
+
const result = execSync22(`docker sandbox exec --privileged ${sandboxName} sh -c ${JSON.stringify(`ls ${JSON.stringify(binDir)} 2>/dev/null | head -20`)}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 }).trim();
|
|
13889
14126
|
if (!result) {
|
|
13890
14127
|
process.stderr.write(`${yellow2("⚠")} node_modules/.bin is empty — binaries like biome may not be available.
|
|
13891
14128
|
`);
|
|
@@ -13968,9 +14205,9 @@ Installing sandbox dependencies (${bold2(installCmd.join(" "))}) to container fi
|
|
|
13968
14205
|
${dim2(`Detected ${ecosystem} project — skipping JS package install.`)}
|
|
13969
14206
|
`);
|
|
13970
14207
|
}
|
|
13971
|
-
const setupScript =
|
|
13972
|
-
const containerSetupScript = containerWorkdir ?
|
|
13973
|
-
if (
|
|
14208
|
+
const setupScript = join27(projectRoot, ".locus", "sandbox-setup.sh");
|
|
14209
|
+
const containerSetupScript = containerWorkdir ? join27(containerWorkdir, ".locus", "sandbox-setup.sh") : setupScript;
|
|
14210
|
+
if (existsSync27(setupScript)) {
|
|
13974
14211
|
process.stderr.write(`Running ${bold2(".locus/sandbox-setup.sh")} in sandbox ${dim2(sandboxName)}...
|
|
13975
14212
|
`);
|
|
13976
14213
|
const hookOk = await runInteractiveCommand("docker", [
|
|
@@ -14059,7 +14296,7 @@ function runInteractiveCommand(command, args) {
|
|
|
14059
14296
|
}
|
|
14060
14297
|
async function createProviderSandbox(provider, sandboxName, projectRoot) {
|
|
14061
14298
|
try {
|
|
14062
|
-
|
|
14299
|
+
execSync22(`docker sandbox create --name ${sandboxName} claude ${projectRoot}`, {
|
|
14063
14300
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14064
14301
|
timeout: 120000
|
|
14065
14302
|
});
|
|
@@ -14074,7 +14311,7 @@ async function createProviderSandbox(provider, sandboxName, projectRoot) {
|
|
|
14074
14311
|
}
|
|
14075
14312
|
async function ensurePackageManagerInSandbox(sandboxName, pm) {
|
|
14076
14313
|
try {
|
|
14077
|
-
|
|
14314
|
+
execSync22(`docker sandbox exec --privileged ${sandboxName} which ${pm}`, {
|
|
14078
14315
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14079
14316
|
timeout: 5000
|
|
14080
14317
|
});
|
|
@@ -14083,7 +14320,7 @@ async function ensurePackageManagerInSandbox(sandboxName, pm) {
|
|
|
14083
14320
|
process.stderr.write(`Installing ${bold2(pm)} in sandbox...
|
|
14084
14321
|
`);
|
|
14085
14322
|
try {
|
|
14086
|
-
|
|
14323
|
+
execSync22(`docker sandbox exec --privileged ${sandboxName} npm install -g ${npmPkg}`, {
|
|
14087
14324
|
stdio: "inherit",
|
|
14088
14325
|
timeout: 120000
|
|
14089
14326
|
});
|
|
@@ -14095,7 +14332,7 @@ async function ensurePackageManagerInSandbox(sandboxName, pm) {
|
|
|
14095
14332
|
}
|
|
14096
14333
|
async function ensureCodexInSandbox(sandboxName) {
|
|
14097
14334
|
try {
|
|
14098
|
-
|
|
14335
|
+
execSync22(`docker sandbox exec --privileged ${sandboxName} which codex`, {
|
|
14099
14336
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14100
14337
|
timeout: 5000
|
|
14101
14338
|
});
|
|
@@ -14103,7 +14340,7 @@ async function ensureCodexInSandbox(sandboxName) {
|
|
|
14103
14340
|
process.stderr.write(`Installing codex in sandbox...
|
|
14104
14341
|
`);
|
|
14105
14342
|
try {
|
|
14106
|
-
|
|
14343
|
+
execSync22(`docker sandbox exec --privileged ${sandboxName} npm install -g @openai/codex`, { stdio: "inherit", timeout: 120000 });
|
|
14107
14344
|
} catch {
|
|
14108
14345
|
process.stderr.write(`${red2("✗")} Failed to install codex in sandbox.
|
|
14109
14346
|
`);
|
|
@@ -14112,7 +14349,7 @@ async function ensureCodexInSandbox(sandboxName) {
|
|
|
14112
14349
|
}
|
|
14113
14350
|
function isSandboxAlive(name) {
|
|
14114
14351
|
try {
|
|
14115
|
-
const output =
|
|
14352
|
+
const output = execSync22("docker sandbox ls", {
|
|
14116
14353
|
encoding: "utf-8",
|
|
14117
14354
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14118
14355
|
timeout: 5000
|
|
@@ -14138,13 +14375,13 @@ init_context();
|
|
|
14138
14375
|
init_logger();
|
|
14139
14376
|
init_rate_limiter();
|
|
14140
14377
|
init_terminal();
|
|
14141
|
-
import { existsSync as
|
|
14142
|
-
import { join as
|
|
14378
|
+
import { existsSync as existsSync28, readFileSync as readFileSync18 } from "node:fs";
|
|
14379
|
+
import { join as join28 } from "node:path";
|
|
14143
14380
|
import { fileURLToPath } from "node:url";
|
|
14144
14381
|
function getCliVersion() {
|
|
14145
14382
|
const fallbackVersion = "0.0.0";
|
|
14146
|
-
const packageJsonPath =
|
|
14147
|
-
if (!
|
|
14383
|
+
const packageJsonPath = join28(fileURLToPath(new URL(".", import.meta.url)), "..", "package.json");
|
|
14384
|
+
if (!existsSync28(packageJsonPath)) {
|
|
14148
14385
|
return fallbackVersion;
|
|
14149
14386
|
}
|
|
14150
14387
|
try {
|
|
@@ -14421,7 +14658,7 @@ async function main() {
|
|
|
14421
14658
|
try {
|
|
14422
14659
|
const root = getGitRoot(cwd);
|
|
14423
14660
|
if (isInitialized(root)) {
|
|
14424
|
-
logDir =
|
|
14661
|
+
logDir = join28(root, ".locus", "logs");
|
|
14425
14662
|
getRateLimiter(root);
|
|
14426
14663
|
}
|
|
14427
14664
|
} catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
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.
|
|
39
|
+
"@locusai/sdk": "^0.24.0",
|
|
40
40
|
"@types/bun": "latest",
|
|
41
41
|
"typescript": "^5.8.3"
|
|
42
42
|
},
|