@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.
Files changed (2) hide show
  1. package/bin/locus.js +133 -43
  2. 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
- reject(new Error(`Claude exited with code ${code}: ${errorOutput}`));
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(`Workspace: ${this.config.workspaceId}`);
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(`Sprint: ${this.resolvedSprintId}`);
30690
+ console.log(`${c.bold("Sprint:")} ${this.resolvedSprintId}`);
30607
30691
  }
30608
- console.log(`API Base: ${this.config.apiBase}`);
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(`\uD83D\uDE80 Agent started: ${agentId}
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", "ignore"],
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
- return reject(new Error(`Claude exited with code ${code}`));
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
- ███████ ██████ ██████ ██████ ███████ v${VERSION3}
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(`\uD83D\uDE80 Starting agent in ${projectPath}...`);
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(`\uD83D\uDD0D Indexing codebase in ${projectPath}...`);
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.0",
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.0"
34
+ "@locusai/sdk": "^0.4.1"
35
35
  },
36
36
  "devDependencies": {}
37
37
  }