@locusai/cli 0.4.0 → 0.4.1
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 +133 -43
- package/package.json +2 -2
package/bin/locus.js
CHANGED
|
@@ -21929,8 +21929,11 @@ class ClaudeRunner {
|
|
|
21929
21929
|
claude.on("close", (code) => {
|
|
21930
21930
|
if (code === 0)
|
|
21931
21931
|
resolve(output);
|
|
21932
|
-
else
|
|
21933
|
-
|
|
21932
|
+
else {
|
|
21933
|
+
const detail = errorOutput.trim();
|
|
21934
|
+
const message = detail ? `Claude CLI error: ${detail}` : `Claude CLI exited with code ${code}. Please ensure the Claude CLI is installed and you are logged in (run 'claude' manually to check).`;
|
|
21935
|
+
reject(new Error(message));
|
|
21936
|
+
}
|
|
21934
21937
|
});
|
|
21935
21938
|
claude.stdin.write(prompt);
|
|
21936
21939
|
claude.stdin.end();
|
|
@@ -30312,6 +30315,27 @@ class LocusClient {
|
|
|
30312
30315
|
this.invitations = new InvitationsModule(this.api, this.emitter);
|
|
30313
30316
|
this.docs = new DocsModule(this.api, this.emitter);
|
|
30314
30317
|
this.ci = new CiModule(this.api, this.emitter);
|
|
30318
|
+
if (config.retryOptions) {
|
|
30319
|
+
this.setupRetryInterceptor(config.retryOptions);
|
|
30320
|
+
}
|
|
30321
|
+
}
|
|
30322
|
+
setupRetryInterceptor(retryOptions) {
|
|
30323
|
+
this.api.interceptors.response.use(undefined, async (error2) => {
|
|
30324
|
+
const config = error2.config;
|
|
30325
|
+
if (!config || !retryOptions) {
|
|
30326
|
+
return Promise.reject(error2);
|
|
30327
|
+
}
|
|
30328
|
+
config._retryCount = config._retryCount || 0;
|
|
30329
|
+
const maxRetries = retryOptions.maxRetries ?? 3;
|
|
30330
|
+
const shouldRetry = config._retryCount < maxRetries && (retryOptions.retryCondition ? retryOptions.retryCondition(error2) : !error2.response || error2.response.status >= 500);
|
|
30331
|
+
if (shouldRetry) {
|
|
30332
|
+
config._retryCount++;
|
|
30333
|
+
const delay = Math.min((retryOptions.initialDelay ?? 1000) * Math.pow(retryOptions.factor ?? 2, config._retryCount - 1), retryOptions.maxDelay ?? 5000);
|
|
30334
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
30335
|
+
return this.api(config);
|
|
30336
|
+
}
|
|
30337
|
+
return Promise.reject(error2);
|
|
30338
|
+
});
|
|
30315
30339
|
}
|
|
30316
30340
|
setupInterceptors() {
|
|
30317
30341
|
this.api.interceptors.response.use((response) => {
|
|
@@ -30351,6 +30375,54 @@ class LocusClient {
|
|
|
30351
30375
|
}
|
|
30352
30376
|
}
|
|
30353
30377
|
|
|
30378
|
+
// ../sdk/src/utils/colors.ts
|
|
30379
|
+
var ESC = "\x1B[";
|
|
30380
|
+
var RESET = `${ESC}0m`;
|
|
30381
|
+
var colors = {
|
|
30382
|
+
reset: RESET,
|
|
30383
|
+
bold: `${ESC}1m`,
|
|
30384
|
+
dim: `${ESC}2m`,
|
|
30385
|
+
italic: `${ESC}3m`,
|
|
30386
|
+
underline: `${ESC}4m`,
|
|
30387
|
+
black: `${ESC}30m`,
|
|
30388
|
+
red: `${ESC}31m`,
|
|
30389
|
+
green: `${ESC}32m`,
|
|
30390
|
+
yellow: `${ESC}33m`,
|
|
30391
|
+
blue: `${ESC}34m`,
|
|
30392
|
+
magenta: `${ESC}35m`,
|
|
30393
|
+
cyan: `${ESC}36m`,
|
|
30394
|
+
white: `${ESC}37m`,
|
|
30395
|
+
gray: `${ESC}90m`,
|
|
30396
|
+
brightRed: `${ESC}91m`,
|
|
30397
|
+
brightGreen: `${ESC}92m`,
|
|
30398
|
+
brightYellow: `${ESC}93m`,
|
|
30399
|
+
brightBlue: `${ESC}94m`,
|
|
30400
|
+
brightMagenta: `${ESC}95m`,
|
|
30401
|
+
brightCyan: `${ESC}96m`,
|
|
30402
|
+
brightWhite: `${ESC}97m`
|
|
30403
|
+
};
|
|
30404
|
+
var c = {
|
|
30405
|
+
text: (text, ...colorNames) => {
|
|
30406
|
+
const codes = colorNames.map((name) => colors[name]).join("");
|
|
30407
|
+
return `${codes}${text}${RESET}`;
|
|
30408
|
+
},
|
|
30409
|
+
bold: (t) => c.text(t, "bold"),
|
|
30410
|
+
dim: (t) => c.text(t, "dim"),
|
|
30411
|
+
red: (t) => c.text(t, "red"),
|
|
30412
|
+
green: (t) => c.text(t, "green"),
|
|
30413
|
+
yellow: (t) => c.text(t, "yellow"),
|
|
30414
|
+
blue: (t) => c.text(t, "blue"),
|
|
30415
|
+
magenta: (t) => c.text(t, "magenta"),
|
|
30416
|
+
cyan: (t) => c.text(t, "cyan"),
|
|
30417
|
+
gray: (t) => c.text(t, "gray"),
|
|
30418
|
+
success: (t) => c.text(t, "green", "bold"),
|
|
30419
|
+
error: (t) => c.text(t, "red", "bold"),
|
|
30420
|
+
warning: (t) => c.text(t, "yellow", "bold"),
|
|
30421
|
+
info: (t) => c.text(t, "cyan", "bold"),
|
|
30422
|
+
primary: (t) => c.text(t, "blue", "bold"),
|
|
30423
|
+
underline: (t) => c.text(t, "underline")
|
|
30424
|
+
};
|
|
30425
|
+
|
|
30354
30426
|
// ../sdk/src/agent/worker.ts
|
|
30355
30427
|
class AgentWorker {
|
|
30356
30428
|
config;
|
|
@@ -30372,7 +30444,13 @@ class AgentWorker {
|
|
|
30372
30444
|
const projectPath = config.projectPath || process.cwd();
|
|
30373
30445
|
this.client = new LocusClient({
|
|
30374
30446
|
baseUrl: config.apiBase,
|
|
30375
|
-
token: config.apiKey
|
|
30447
|
+
token: config.apiKey,
|
|
30448
|
+
retryOptions: {
|
|
30449
|
+
maxRetries: 3,
|
|
30450
|
+
initialDelay: 1000,
|
|
30451
|
+
maxDelay: 5000,
|
|
30452
|
+
factor: 2
|
|
30453
|
+
}
|
|
30376
30454
|
});
|
|
30377
30455
|
this.claudeRunner = new ClaudeRunner(projectPath, config.model);
|
|
30378
30456
|
this.anthropicClient = config.anthropicApiKey ? new AnthropicClient({
|
|
@@ -30412,8 +30490,14 @@ class AgentWorker {
|
|
|
30412
30490
|
}
|
|
30413
30491
|
log(message, level = "info") {
|
|
30414
30492
|
const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
|
|
30493
|
+
const colorFn = {
|
|
30494
|
+
info: c.cyan,
|
|
30495
|
+
success: c.green,
|
|
30496
|
+
warn: c.yellow,
|
|
30497
|
+
error: c.red
|
|
30498
|
+
}[level];
|
|
30415
30499
|
const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
|
|
30416
|
-
console.log(`[${timestamp}] [${this.config.agentId.slice(-8)}] ${prefix} ${message}`);
|
|
30500
|
+
console.log(`${c.dim(`[${timestamp}]`)} ${c.bold(`[${this.config.agentId.slice(-8)}]`)} ${colorFn(`${prefix} ${message}`)}`);
|
|
30417
30501
|
}
|
|
30418
30502
|
async getActiveSprint() {
|
|
30419
30503
|
try {
|
|
@@ -30569,11 +30653,11 @@ class AgentOrchestrator extends EventEmitter3 {
|
|
|
30569
30653
|
try {
|
|
30570
30654
|
const sprint2 = await this.client.sprints.getActive(this.config.workspaceId);
|
|
30571
30655
|
if (sprint2?.id) {
|
|
30572
|
-
console.log(`\uD83D\uDCCB Using active sprint: ${sprint2.name}`);
|
|
30656
|
+
console.log(c.info(`\uD83D\uDCCB Using active sprint: ${sprint2.name}`));
|
|
30573
30657
|
return sprint2.id;
|
|
30574
30658
|
}
|
|
30575
30659
|
} catch {}
|
|
30576
|
-
console.log("ℹ No sprint specified, working with all workspace tasks");
|
|
30660
|
+
console.log(c.dim("ℹ No sprint specified, working with all workspace tasks"));
|
|
30577
30661
|
return "";
|
|
30578
30662
|
}
|
|
30579
30663
|
async start() {
|
|
@@ -30599,18 +30683,18 @@ class AgentOrchestrator extends EventEmitter3 {
|
|
|
30599
30683
|
sprintId: this.resolvedSprintId
|
|
30600
30684
|
});
|
|
30601
30685
|
console.log(`
|
|
30602
|
-
\uD83E\uDD16 Locus Agent Orchestrator`);
|
|
30603
|
-
console.log("
|
|
30604
|
-
console.log(
|
|
30686
|
+
${c.primary("\uD83E\uDD16 Locus Agent Orchestrator")}`);
|
|
30687
|
+
console.log(c.dim("----------------------------------------------"));
|
|
30688
|
+
console.log(`${c.bold("Workspace:")} ${this.config.workspaceId}`);
|
|
30605
30689
|
if (this.resolvedSprintId) {
|
|
30606
|
-
console.log(
|
|
30690
|
+
console.log(`${c.bold("Sprint:")} ${this.resolvedSprintId}`);
|
|
30607
30691
|
}
|
|
30608
|
-
console.log(
|
|
30609
|
-
console.log(
|
|
30610
|
-
`);
|
|
30692
|
+
console.log(`${c.bold("API Base:")} ${this.config.apiBase}`);
|
|
30693
|
+
console.log(c.dim(`----------------------------------------------
|
|
30694
|
+
`));
|
|
30611
30695
|
const tasks2 = await this.getAvailableTasks();
|
|
30612
30696
|
if (tasks2.length === 0) {
|
|
30613
|
-
console.log("ℹ No available tasks found in the backlog.");
|
|
30697
|
+
console.log(c.dim("ℹ No available tasks found in the backlog."));
|
|
30614
30698
|
return;
|
|
30615
30699
|
}
|
|
30616
30700
|
await this.spawnAgent();
|
|
@@ -30622,7 +30706,7 @@ class AgentOrchestrator extends EventEmitter3 {
|
|
|
30622
30706
|
await this.sleep(2000);
|
|
30623
30707
|
}
|
|
30624
30708
|
console.log(`
|
|
30625
|
-
✅ Orchestrator finished`);
|
|
30709
|
+
${c.success("✅ Orchestrator finished")}`);
|
|
30626
30710
|
}
|
|
30627
30711
|
findPackageRoot(startPath) {
|
|
30628
30712
|
let currentDir = startPath;
|
|
@@ -30645,7 +30729,7 @@ class AgentOrchestrator extends EventEmitter3 {
|
|
|
30645
30729
|
lastHeartbeat: new Date
|
|
30646
30730
|
};
|
|
30647
30731
|
this.agents.set(agentId, agentState);
|
|
30648
|
-
console.log(
|
|
30732
|
+
console.log(`${c.primary("\uD83D\uDE80 Agent started:")} ${c.bold(agentId)}
|
|
30649
30733
|
`);
|
|
30650
30734
|
const potentialPaths = [];
|
|
30651
30735
|
try {
|
|
@@ -30886,16 +30970,22 @@ Return ONLY a JSON object with this structure:
|
|
|
30886
30970
|
File Tree:
|
|
30887
30971
|
${tree}`;
|
|
30888
30972
|
const claude = spawn3("claude", ["--print"], {
|
|
30889
|
-
stdio: ["pipe", "pipe", "
|
|
30973
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
30890
30974
|
cwd: this.projectPath
|
|
30891
30975
|
});
|
|
30892
30976
|
let output = "";
|
|
30977
|
+
let errorOutput = "";
|
|
30893
30978
|
claude.stdout.on("data", (data) => {
|
|
30894
30979
|
output += data.toString();
|
|
30895
30980
|
});
|
|
30981
|
+
claude.stderr.on("data", (data) => {
|
|
30982
|
+
errorOutput += data.toString();
|
|
30983
|
+
});
|
|
30896
30984
|
claude.on("close", (code) => {
|
|
30897
|
-
if (code !== 0)
|
|
30898
|
-
|
|
30985
|
+
if (code !== 0) {
|
|
30986
|
+
const errMsg = errorOutput.trim() || `Claude exited with code ${code}`;
|
|
30987
|
+
return reject(new Error(errMsg));
|
|
30988
|
+
}
|
|
30899
30989
|
try {
|
|
30900
30990
|
const jsonMatch = output.match(/\{[\s\S]*\}/);
|
|
30901
30991
|
if (jsonMatch)
|
|
@@ -30927,13 +31017,13 @@ function getVersion() {
|
|
|
30927
31017
|
}
|
|
30928
31018
|
var VERSION3 = getVersion();
|
|
30929
31019
|
function printBanner() {
|
|
30930
|
-
console.log(`
|
|
30931
|
-
|
|
30932
|
-
|
|
30933
|
-
|
|
30934
|
-
|
|
30935
|
-
|
|
30936
|
-
`);
|
|
31020
|
+
console.log(c.primary(`
|
|
31021
|
+
## ###### ###### ## ## ######
|
|
31022
|
+
## ## ## ## ## ## ##
|
|
31023
|
+
## ## ## ## ## ## ######
|
|
31024
|
+
## ## ## ## ## ## ##
|
|
31025
|
+
####### ###### ###### ###### ###### v${VERSION3}
|
|
31026
|
+
`));
|
|
30937
31027
|
}
|
|
30938
31028
|
function isProjectInitialized(projectPath) {
|
|
30939
31029
|
const locusDir = join6(projectPath, LOCUS_CONFIG.dir);
|
|
@@ -30943,12 +31033,12 @@ function isProjectInitialized(projectPath) {
|
|
|
30943
31033
|
function requireInitialization(projectPath, command) {
|
|
30944
31034
|
if (!isProjectInitialized(projectPath)) {
|
|
30945
31035
|
console.error(`
|
|
30946
|
-
❌ Error: Locus is not initialized in this directory.
|
|
31036
|
+
${c.error("❌ Error: Locus is not initialized in this directory.")}
|
|
30947
31037
|
|
|
30948
|
-
The '${command}' command requires a Locus project to be initialized.
|
|
31038
|
+
The '${c.bold(command)}' command requires a Locus project to be initialized.
|
|
30949
31039
|
|
|
30950
31040
|
To initialize Locus in this directory, run:
|
|
30951
|
-
locus init
|
|
31041
|
+
${c.primary("locus init")}
|
|
30952
31042
|
|
|
30953
31043
|
This will create a .locus directory with the necessary configuration.
|
|
30954
31044
|
`);
|
|
@@ -30975,7 +31065,7 @@ async function runCommand(args) {
|
|
|
30975
31065
|
const anthropicApiKey = values["anthropic-api-key"] || process.env.ANTHROPIC_API_KEY;
|
|
30976
31066
|
const workspaceId = values.workspace || process.env.LOCUS_WORKSPACE_ID;
|
|
30977
31067
|
if (!apiKey || !workspaceId) {
|
|
30978
|
-
console.error("Error: --api-key and --workspace are required");
|
|
31068
|
+
console.error(c.error("Error: --api-key and --workspace are required"));
|
|
30979
31069
|
process.exit(1);
|
|
30980
31070
|
}
|
|
30981
31071
|
const orchestrator = new AgentOrchestrator({
|
|
@@ -30991,7 +31081,7 @@ async function runCommand(args) {
|
|
|
30991
31081
|
orchestrator.on("task:assigned", (data) => console.log(`ℹ [CLAIMED] ${data.title}`));
|
|
30992
31082
|
orchestrator.on("task:completed", (data) => console.log(`✓ [COMPLETED] ${data.taskId}`));
|
|
30993
31083
|
orchestrator.on("task:failed", (data) => console.log(`✗ [FAILED] ${data.taskId}: ${data.error}`));
|
|
30994
|
-
console.log(
|
|
31084
|
+
console.log(`${c.primary("\uD83D\uDE80 Starting agent in")} ${c.bold(projectPath)}...`);
|
|
30995
31085
|
await orchestrator.start();
|
|
30996
31086
|
}
|
|
30997
31087
|
async function indexCommand(args) {
|
|
@@ -31004,18 +31094,18 @@ async function indexCommand(args) {
|
|
|
31004
31094
|
requireInitialization(projectPath, "index");
|
|
31005
31095
|
const summarizer = new TreeSummarizer(projectPath);
|
|
31006
31096
|
const indexer = new CodebaseIndexer(projectPath);
|
|
31007
|
-
console.log(
|
|
31008
|
-
const index = await indexer.index((msg) => console.log(` ${msg}`), (tree) => summarizer.summarize(tree));
|
|
31097
|
+
console.log(`${c.primary("\uD83D\uDD0D Indexing codebase in")} ${c.bold(projectPath)}...`);
|
|
31098
|
+
const index = await indexer.index((msg) => console.log(` ${c.dim(msg)}`), (tree) => summarizer.summarize(tree));
|
|
31009
31099
|
indexer.saveIndex(index);
|
|
31010
|
-
console.log("✅ Indexing complete!");
|
|
31100
|
+
console.log(c.success("✅ Indexing complete!"));
|
|
31011
31101
|
}
|
|
31012
31102
|
async function initCommand() {
|
|
31013
31103
|
const projectPath = process.cwd();
|
|
31014
31104
|
if (isProjectInitialized(projectPath)) {
|
|
31015
31105
|
console.log(`
|
|
31016
|
-
ℹ️ Locus is already initialized in this directory.
|
|
31106
|
+
${c.info("ℹ️ Locus is already initialized in this directory.")}
|
|
31017
31107
|
|
|
31018
|
-
Configuration found at: ${join6(projectPath, LOCUS_CONFIG.dir)}
|
|
31108
|
+
Configuration found at: ${c.bold(join6(projectPath, LOCUS_CONFIG.dir))}
|
|
31019
31109
|
|
|
31020
31110
|
If you want to reinitialize, please remove the .locus directory first.
|
|
31021
31111
|
`);
|
|
@@ -31023,18 +31113,18 @@ If you want to reinitialize, please remove the .locus directory first.
|
|
|
31023
31113
|
}
|
|
31024
31114
|
await new ConfigManager(projectPath).init(VERSION3);
|
|
31025
31115
|
console.log(`
|
|
31026
|
-
✨ Locus initialized successfully!
|
|
31116
|
+
${c.success("✨ Locus initialized successfully!")}
|
|
31027
31117
|
|
|
31028
31118
|
Created:
|
|
31029
|
-
\uD83D\uDCC1 .locus/ - Locus configuration directory
|
|
31030
|
-
\uD83D\uDCC4 .locus/config.json - Project configuration
|
|
31031
|
-
\uD83D\uDCDD CLAUDE.md - AI context file
|
|
31119
|
+
\uD83D\uDCC1 ${c.dim(".locus/")} - Locus configuration directory
|
|
31120
|
+
\uD83D\uDCC4 ${c.dim(".locus/config.json")} - Project configuration
|
|
31121
|
+
\uD83D\uDCDD ${c.dim("CLAUDE.md")} - AI context file
|
|
31032
31122
|
|
|
31033
31123
|
Next steps:
|
|
31034
|
-
1. Run 'locus index' to index your codebase
|
|
31035
|
-
2. Run 'locus run' to start an agent (requires --api-key and --workspace)
|
|
31124
|
+
1. Run '${c.primary("locus index")}' to index your codebase
|
|
31125
|
+
2. Run '${c.primary("locus run")}' to start an agent (requires --api-key and --workspace)
|
|
31036
31126
|
|
|
31037
|
-
For more information, visit: https://locus.dev/docs
|
|
31127
|
+
For more information, visit: ${c.underline("https://locus.dev/docs")}
|
|
31038
31128
|
`);
|
|
31039
31129
|
}
|
|
31040
31130
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Local-first AI development platform & engineering workspace",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"author": "",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@locusai/sdk": "^0.4.
|
|
34
|
+
"@locusai/sdk": "^0.4.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {}
|
|
37
37
|
}
|