@locusai/cli 0.15.5 → 0.16.2

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 (3) hide show
  1. package/bin/agent/worker.js +134 -359
  2. package/bin/locus.js +865 -1658
  3. package/package.json +4 -4
package/bin/locus.js CHANGED
@@ -7055,7 +7055,6 @@ var init_resolve_bin = __esm(() => {
7055
7055
  join4(homedir(), ".local", "bin"),
7056
7056
  join4(homedir(), ".npm", "bin"),
7057
7057
  join4(homedir(), ".npm-global", "bin"),
7058
- join4(homedir(), ".npm-packages", "bin"),
7059
7058
  join4(homedir(), ".yarn", "bin"),
7060
7059
  join4(homedir(), ".bun", "bin"),
7061
7060
  join4(homedir(), "Library", "pnpm"),
@@ -7066,6 +7065,112 @@ var init_resolve_bin = __esm(() => {
7066
7065
  ENV_VARS_TO_STRIP = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"];
7067
7066
  });
7068
7067
 
7068
+ // ../sdk/src/ai/claude-stream-parser.ts
7069
+ class ClaudeStreamParser {
7070
+ activeTools = new Map;
7071
+ parseLineToChunk(line) {
7072
+ if (!line.trim())
7073
+ return null;
7074
+ try {
7075
+ const item = JSON.parse(line);
7076
+ return this.processItemToChunk(item);
7077
+ } catch {
7078
+ return null;
7079
+ }
7080
+ }
7081
+ parseLine(line, log) {
7082
+ if (!line.trim())
7083
+ return null;
7084
+ try {
7085
+ const item = JSON.parse(line);
7086
+ return this.processItem(item, log);
7087
+ } catch {
7088
+ return null;
7089
+ }
7090
+ }
7091
+ processItemToChunk(item) {
7092
+ if (item.type === "result") {
7093
+ return { type: "result", content: item.result || "" };
7094
+ }
7095
+ if (item.type === "stream_event" && item.event) {
7096
+ return this.handleEventToChunk(item.event);
7097
+ }
7098
+ return null;
7099
+ }
7100
+ handleEventToChunk(event) {
7101
+ const { type, delta, content_block, index } = event;
7102
+ if (type === "content_block_delta" && delta?.type === "text_delta") {
7103
+ return { type: "text_delta", content: delta.text || "" };
7104
+ }
7105
+ if (type === "content_block_delta" && delta?.type === "input_json_delta" && delta.partial_json !== undefined && index !== undefined) {
7106
+ const activeTool = this.activeTools.get(index);
7107
+ if (activeTool) {
7108
+ activeTool.parameterJson += delta.partial_json;
7109
+ }
7110
+ return null;
7111
+ }
7112
+ if (type === "content_block_start" && content_block) {
7113
+ if (content_block.type === "tool_use" && content_block.name) {
7114
+ if (index !== undefined) {
7115
+ this.activeTools.set(index, {
7116
+ name: content_block.name,
7117
+ id: content_block.id,
7118
+ index,
7119
+ parameterJson: "",
7120
+ startTime: Date.now()
7121
+ });
7122
+ }
7123
+ return {
7124
+ type: "tool_use",
7125
+ tool: content_block.name,
7126
+ id: content_block.id
7127
+ };
7128
+ }
7129
+ if (content_block.type === "thinking") {
7130
+ return { type: "thinking" };
7131
+ }
7132
+ }
7133
+ if (type === "content_block_stop" && index !== undefined) {
7134
+ const activeTool = this.activeTools.get(index);
7135
+ if (activeTool?.parameterJson) {
7136
+ try {
7137
+ const parameters = JSON.parse(activeTool.parameterJson);
7138
+ return {
7139
+ type: "tool_parameters",
7140
+ tool: activeTool.name,
7141
+ id: activeTool.id,
7142
+ parameters
7143
+ };
7144
+ } catch {}
7145
+ }
7146
+ return null;
7147
+ }
7148
+ return null;
7149
+ }
7150
+ processItem(item, log) {
7151
+ if (item.type === "result") {
7152
+ return item.result || "";
7153
+ }
7154
+ if (item.type === "stream_event" && item.event) {
7155
+ this.handleEvent(item.event, log);
7156
+ }
7157
+ return null;
7158
+ }
7159
+ handleEvent(event, log) {
7160
+ const { type, content_block } = event;
7161
+ if (type === "content_block_start" && content_block) {
7162
+ if (content_block.type === "tool_use" && content_block.name) {
7163
+ log?.(`
7164
+ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
7165
+ `, "info");
7166
+ }
7167
+ }
7168
+ }
7169
+ }
7170
+ var init_claude_stream_parser = __esm(() => {
7171
+ init_colors();
7172
+ });
7173
+
7069
7174
  // ../sdk/src/ai/claude-runner.ts
7070
7175
  import { spawn } from "node:child_process";
7071
7176
  import { resolve } from "node:path";
@@ -7076,7 +7181,6 @@ class ClaudeRunner {
7076
7181
  projectPath;
7077
7182
  eventEmitter;
7078
7183
  currentToolName;
7079
- activeTools = new Map;
7080
7184
  activeProcess = null;
7081
7185
  aborted = false;
7082
7186
  timeoutMs;
@@ -7111,7 +7215,7 @@ class ClaudeRunner {
7111
7215
  }
7112
7216
  if (!isLastAttempt) {
7113
7217
  const delay = Math.pow(2, attempt) * 1000;
7114
- console.warn(`Claude CLI attempt ${attempt} failed: ${err.message}. Retrying in ${delay}ms...`);
7218
+ this.log?.(`Claude CLI attempt ${attempt} failed: ${err.message}. Retrying in ${delay}ms...`, "warn");
7115
7219
  await new Promise((resolve2) => setTimeout(resolve2, delay));
7116
7220
  }
7117
7221
  }
@@ -7151,6 +7255,7 @@ class ClaudeRunner {
7151
7255
  }
7152
7256
  async* runStream(prompt) {
7153
7257
  this.aborted = false;
7258
+ const parser = new ClaudeStreamParser;
7154
7259
  const args = this.buildCliArgs();
7155
7260
  const env = getAugmentedEnv({
7156
7261
  FORCE_COLOR: "1",
@@ -7211,7 +7316,7 @@ class ClaudeRunner {
7211
7316
  `);
7212
7317
  buffer = lines.pop() || "";
7213
7318
  for (const line of lines) {
7214
- const chunk = this.parseStreamLineToChunk(line);
7319
+ const chunk = parser.parseLineToChunk(line);
7215
7320
  if (chunk) {
7216
7321
  if (chunk.type === "result") {
7217
7322
  lastResultContent = chunk.content;
@@ -7320,77 +7425,9 @@ class ClaudeRunner {
7320
7425
  break;
7321
7426
  }
7322
7427
  }
7323
- parseStreamLineToChunk(line) {
7324
- if (!line.trim())
7325
- return null;
7326
- try {
7327
- const item = JSON.parse(line);
7328
- return this.processStreamItemToChunk(item);
7329
- } catch {
7330
- return null;
7331
- }
7332
- }
7333
- processStreamItemToChunk(item) {
7334
- if (item.type === "result") {
7335
- return { type: "result", content: item.result || "" };
7336
- }
7337
- if (item.type === "stream_event" && item.event) {
7338
- return this.handleEventToChunk(item.event);
7339
- }
7340
- return null;
7341
- }
7342
- handleEventToChunk(event) {
7343
- const { type, delta, content_block, index } = event;
7344
- if (type === "content_block_delta" && delta?.type === "text_delta") {
7345
- return { type: "text_delta", content: delta.text || "" };
7346
- }
7347
- if (type === "content_block_delta" && delta?.type === "input_json_delta" && delta.partial_json !== undefined && index !== undefined) {
7348
- const activeTool = this.activeTools.get(index);
7349
- if (activeTool) {
7350
- activeTool.parameterJson += delta.partial_json;
7351
- }
7352
- return null;
7353
- }
7354
- if (type === "content_block_start" && content_block) {
7355
- if (content_block.type === "tool_use" && content_block.name) {
7356
- if (index !== undefined) {
7357
- this.activeTools.set(index, {
7358
- name: content_block.name,
7359
- id: content_block.id,
7360
- index,
7361
- parameterJson: "",
7362
- startTime: Date.now()
7363
- });
7364
- }
7365
- return {
7366
- type: "tool_use",
7367
- tool: content_block.name,
7368
- id: content_block.id
7369
- };
7370
- }
7371
- if (content_block.type === "thinking") {
7372
- return { type: "thinking" };
7373
- }
7374
- }
7375
- if (type === "content_block_stop" && index !== undefined) {
7376
- const activeTool = this.activeTools.get(index);
7377
- if (activeTool?.parameterJson) {
7378
- try {
7379
- const parameters = JSON.parse(activeTool.parameterJson);
7380
- return {
7381
- type: "tool_parameters",
7382
- tool: activeTool.name,
7383
- id: activeTool.id,
7384
- parameters
7385
- };
7386
- } catch {}
7387
- }
7388
- return null;
7389
- }
7390
- return null;
7391
- }
7392
7428
  executeRun(prompt) {
7393
7429
  this.aborted = false;
7430
+ const parser = new ClaudeStreamParser;
7394
7431
  return new Promise((resolve2, reject) => {
7395
7432
  const args = this.buildCliArgs();
7396
7433
  const env = getAugmentedEnv({
@@ -7413,7 +7450,7 @@ class ClaudeRunner {
7413
7450
  `);
7414
7451
  buffer = lines.pop() || "";
7415
7452
  for (const line of lines) {
7416
- const result = this.handleStreamLine(line);
7453
+ const result = parser.parseLine(line, this.log);
7417
7454
  if (result)
7418
7455
  finalResult = result;
7419
7456
  }
@@ -7454,35 +7491,6 @@ class ClaudeRunner {
7454
7491
  claude.stdin.end();
7455
7492
  });
7456
7493
  }
7457
- handleStreamLine(line) {
7458
- if (!line.trim())
7459
- return null;
7460
- try {
7461
- const item = JSON.parse(line);
7462
- return this.processStreamItem(item);
7463
- } catch {
7464
- return null;
7465
- }
7466
- }
7467
- processStreamItem(item) {
7468
- if (item.type === "result") {
7469
- return item.result || "";
7470
- }
7471
- if (item.type === "stream_event" && item.event) {
7472
- this.handleEvent(item.event);
7473
- }
7474
- return null;
7475
- }
7476
- handleEvent(event) {
7477
- const { type, content_block } = event;
7478
- if (type === "content_block_start" && content_block) {
7479
- if (content_block.type === "tool_use" && content_block.name) {
7480
- this.log?.(`
7481
- ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
7482
- `, "info");
7483
- }
7484
- }
7485
- }
7486
7494
  shouldSuppressLine(line) {
7487
7495
  const infoLogRegex = /^\[\d{2}:\d{2}:\d{2}\]\s\[.*?\]\sℹ\s*$/;
7488
7496
  return infoLogRegex.test(line.trim());
@@ -7496,8 +7504,8 @@ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
7496
7504
  var DEFAULT_TIMEOUT_MS;
7497
7505
  var init_claude_runner = __esm(() => {
7498
7506
  init_config();
7499
- init_colors();
7500
7507
  init_resolve_bin();
7508
+ init_claude_stream_parser();
7501
7509
  DEFAULT_TIMEOUT_MS = 60 * 60 * 1000;
7502
7510
  });
7503
7511
 
@@ -7549,7 +7557,7 @@ class CodexRunner {
7549
7557
  }
7550
7558
  if (attempt < maxRetries) {
7551
7559
  const delay = Math.pow(2, attempt) * 1000;
7552
- console.warn(`Codex CLI attempt ${attempt} failed: ${lastError.message}. Retrying in ${delay}ms...`);
7560
+ this.log?.(`Codex CLI attempt ${attempt} failed: ${lastError.message}. Retrying in ${delay}ms...`, "warn");
7553
7561
  await this.sleep(delay);
7554
7562
  }
7555
7563
  }
@@ -7841,6 +7849,20 @@ var init_codex_runner = __esm(() => {
7841
7849
  });
7842
7850
 
7843
7851
  // ../sdk/src/ai/factory.ts
7852
+ function createWorkerLogger(agentId, prefix) {
7853
+ const tag = prefix ? `${prefix}:${agentId.slice(-8)}` : agentId.slice(-8);
7854
+ return (message, level = "info") => {
7855
+ const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
7856
+ const colorFn = {
7857
+ info: c.cyan,
7858
+ success: c.green,
7859
+ warn: c.yellow,
7860
+ error: c.red
7861
+ }[level];
7862
+ const icon = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
7863
+ console.log(`${c.dim(`[${timestamp}]`)} ${c.bold(`[${tag}]`)} ${colorFn(`${icon} ${message}`)}`);
7864
+ };
7865
+ }
7844
7866
  function createAiRunner(provider, config) {
7845
7867
  const resolvedProvider = provider ?? PROVIDER.CLAUDE;
7846
7868
  const model = config.model ?? DEFAULT_MODEL[resolvedProvider];
@@ -7855,8 +7877,10 @@ function createAiRunner(provider, config) {
7855
7877
  return new ClaudeRunner(config.projectPath, model, config.log, config.timeoutMs);
7856
7878
  }
7857
7879
  }
7880
+ var noopLogger = () => {};
7858
7881
  var init_factory = __esm(() => {
7859
7882
  init_config();
7883
+ init_colors();
7860
7884
  init_claude_runner();
7861
7885
  init_codex_runner();
7862
7886
  });
@@ -23202,49 +23226,6 @@ var init_docs = __esm(() => {
23202
23226
  };
23203
23227
  });
23204
23228
 
23205
- // ../sdk/src/modules/instances.ts
23206
- var InstancesModule;
23207
- var init_instances = __esm(() => {
23208
- InstancesModule = class InstancesModule extends BaseModule {
23209
- async list(workspaceId) {
23210
- const { data } = await this.api.get(`/workspaces/${workspaceId}/aws-instances`);
23211
- return data.instances;
23212
- }
23213
- async get(workspaceId, instanceId) {
23214
- const { data } = await this.api.get(`/workspaces/${workspaceId}/aws-instances/${instanceId}`);
23215
- return data.instance;
23216
- }
23217
- async provision(workspaceId, body) {
23218
- const { data } = await this.api.post(`/workspaces/${workspaceId}/aws-instances`, body);
23219
- return data.instance;
23220
- }
23221
- async performAction(workspaceId, instanceId, action) {
23222
- const { data } = await this.api.post(`/workspaces/${workspaceId}/aws-instances/${instanceId}/actions`, { action });
23223
- return data.instance;
23224
- }
23225
- async sync(workspaceId, instanceId) {
23226
- const { data } = await this.api.post(`/workspaces/${workspaceId}/aws-instances/${instanceId}/sync`);
23227
- return data.instance;
23228
- }
23229
- async checkUpdates(workspaceId, instanceId) {
23230
- const { data } = await this.api.get(`/workspaces/${workspaceId}/aws-instances/${instanceId}/updates`);
23231
- return data.update;
23232
- }
23233
- async applyUpdate(workspaceId, instanceId) {
23234
- const { data } = await this.api.post(`/workspaces/${workspaceId}/aws-instances/${instanceId}/updates`);
23235
- return data.update;
23236
- }
23237
- async getSecurity(workspaceId, instanceId) {
23238
- const { data } = await this.api.get(`/workspaces/${workspaceId}/aws-instances/${instanceId}/security`);
23239
- return data.rules;
23240
- }
23241
- async updateSecurity(workspaceId, instanceId, body) {
23242
- const { data } = await this.api.put(`/workspaces/${workspaceId}/aws-instances/${instanceId}/security`, body);
23243
- return data.rules;
23244
- }
23245
- };
23246
- });
23247
-
23248
23229
  // ../sdk/src/modules/invitations.ts
23249
23230
  var InvitationsModule;
23250
23231
  var init_invitations = __esm(() => {
@@ -23353,29 +23334,6 @@ var init_sprints = __esm(() => {
23353
23334
  };
23354
23335
  });
23355
23336
 
23356
- // ../sdk/src/modules/suggestions.ts
23357
- var SuggestionsModule;
23358
- var init_suggestions = __esm(() => {
23359
- SuggestionsModule = class SuggestionsModule extends BaseModule {
23360
- async create(workspaceId, data) {
23361
- const { data: res } = await this.api.post(`/workspaces/${workspaceId}/suggestions`, data);
23362
- return res.suggestion;
23363
- }
23364
- async list(workspaceId, params) {
23365
- const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions`, { params });
23366
- return data.suggestions;
23367
- }
23368
- async get(workspaceId, id) {
23369
- const { data } = await this.api.get(`/workspaces/${workspaceId}/suggestions/${id}`);
23370
- return data.suggestion;
23371
- }
23372
- async updateStatus(workspaceId, id, status) {
23373
- const { data } = await this.api.patch(`/workspaces/${workspaceId}/suggestions/${id}/status`, status);
23374
- return data.suggestion;
23375
- }
23376
- };
23377
- });
23378
-
23379
23337
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/core/core.js
23380
23338
  function $constructor(name, initializer, params) {
23381
23339
  function init(inst, def) {
@@ -37297,7 +37255,7 @@ var init_constants = __esm(() => {
37297
37255
  });
37298
37256
 
37299
37257
  // ../shared/src/enums.ts
37300
- var UserRole, MembershipRole, TaskStatus, TaskPriority, AssigneeRole, SprintStatus, InstanceStatus, AwsRegion, EventType;
37258
+ var UserRole, MembershipRole, TaskStatus, TaskPriority, AssigneeRole, SprintStatus, EventType;
37301
37259
  var init_enums = __esm(() => {
37302
37260
  ((UserRole2) => {
37303
37261
  UserRole2["USER"] = "USER";
@@ -37334,16 +37292,6 @@ var init_enums = __esm(() => {
37334
37292
  SprintStatus2["ACTIVE"] = "ACTIVE";
37335
37293
  SprintStatus2["COMPLETED"] = "COMPLETED";
37336
37294
  })(SprintStatus ||= {});
37337
- ((InstanceStatus2) => {
37338
- InstanceStatus2["PROVISIONING"] = "PROVISIONING";
37339
- InstanceStatus2["RUNNING"] = "RUNNING";
37340
- InstanceStatus2["STOPPED"] = "STOPPED";
37341
- InstanceStatus2["TERMINATED"] = "TERMINATED";
37342
- InstanceStatus2["ERROR"] = "ERROR";
37343
- })(InstanceStatus ||= {});
37344
- ((AwsRegion2) => {
37345
- AwsRegion2["US_EAST_1"] = "us-east-1";
37346
- })(AwsRegion ||= {});
37347
37295
  ((EventType2) => {
37348
37296
  EventType2["TASK_CREATED"] = "TASK_CREATED";
37349
37297
  EventType2["TASK_UPDATED"] = "TASK_UPDATED";
@@ -37691,145 +37639,6 @@ var init_auth2 = __esm(() => {
37691
37639
  });
37692
37640
  });
37693
37641
 
37694
- // ../shared/src/models/autonomy.ts
37695
- var RiskLevel, ChangeCategory, AutonomyRuleSchema, DEFAULT_AUTONOMY_RULES;
37696
- var init_autonomy = __esm(() => {
37697
- init_zod();
37698
- ((RiskLevel2) => {
37699
- RiskLevel2["LOW"] = "LOW";
37700
- RiskLevel2["HIGH"] = "HIGH";
37701
- })(RiskLevel ||= {});
37702
- ((ChangeCategory2) => {
37703
- ChangeCategory2["FIX"] = "FIX";
37704
- ChangeCategory2["REFACTOR"] = "REFACTOR";
37705
- ChangeCategory2["STYLE"] = "STYLE";
37706
- ChangeCategory2["DEPENDENCY"] = "DEPENDENCY";
37707
- ChangeCategory2["FEATURE"] = "FEATURE";
37708
- ChangeCategory2["ARCHITECTURE"] = "ARCHITECTURE";
37709
- ChangeCategory2["DATABASE"] = "DATABASE";
37710
- ChangeCategory2["AUTH"] = "AUTH";
37711
- ChangeCategory2["API"] = "API";
37712
- })(ChangeCategory ||= {});
37713
- AutonomyRuleSchema = exports_external.object({
37714
- category: exports_external.enum(ChangeCategory),
37715
- riskLevel: exports_external.enum(RiskLevel),
37716
- autoExecute: exports_external.boolean()
37717
- });
37718
- DEFAULT_AUTONOMY_RULES = [
37719
- { category: "FIX" /* FIX */, riskLevel: "LOW" /* LOW */, autoExecute: true },
37720
- {
37721
- category: "REFACTOR" /* REFACTOR */,
37722
- riskLevel: "LOW" /* LOW */,
37723
- autoExecute: true
37724
- },
37725
- {
37726
- category: "STYLE" /* STYLE */,
37727
- riskLevel: "LOW" /* LOW */,
37728
- autoExecute: true
37729
- },
37730
- {
37731
- category: "DEPENDENCY" /* DEPENDENCY */,
37732
- riskLevel: "LOW" /* LOW */,
37733
- autoExecute: true
37734
- },
37735
- {
37736
- category: "FEATURE" /* FEATURE */,
37737
- riskLevel: "HIGH" /* HIGH */,
37738
- autoExecute: false
37739
- },
37740
- {
37741
- category: "ARCHITECTURE" /* ARCHITECTURE */,
37742
- riskLevel: "HIGH" /* HIGH */,
37743
- autoExecute: false
37744
- },
37745
- {
37746
- category: "DATABASE" /* DATABASE */,
37747
- riskLevel: "HIGH" /* HIGH */,
37748
- autoExecute: false
37749
- },
37750
- {
37751
- category: "AUTH" /* AUTH */,
37752
- riskLevel: "HIGH" /* HIGH */,
37753
- autoExecute: false
37754
- },
37755
- {
37756
- category: "API" /* API */,
37757
- riskLevel: "HIGH" /* HIGH */,
37758
- autoExecute: false
37759
- }
37760
- ];
37761
- });
37762
-
37763
- // ../shared/src/models/aws-instance.ts
37764
- var InstanceAction, AwsCredentialsSchema, IntegrationSchema, AwsInstanceSchema, CreateAwsInstanceSchema, UpdateAwsInstanceSchema, SaveAwsCredentialsSchema, ProvisionAwsInstanceSchema, InstanceActionBodySchema, InstanceIdParamSchema, CIDR_REGEX, UpdateSecurityRulesSchema;
37765
- var init_aws_instance = __esm(() => {
37766
- init_zod();
37767
- init_common();
37768
- init_enums();
37769
- ((InstanceAction2) => {
37770
- InstanceAction2["START"] = "START";
37771
- InstanceAction2["STOP"] = "STOP";
37772
- InstanceAction2["TERMINATE"] = "TERMINATE";
37773
- })(InstanceAction ||= {});
37774
- AwsCredentialsSchema = exports_external.object({
37775
- accessKeyId: exports_external.string().min(1),
37776
- secretAccessKey: exports_external.string().min(1),
37777
- region: exports_external.enum(AwsRegion).default("us-east-1" /* US_EAST_1 */)
37778
- });
37779
- IntegrationSchema = exports_external.object({
37780
- name: exports_external.string(),
37781
- config: exports_external.record(exports_external.string(), exports_external.string())
37782
- });
37783
- AwsInstanceSchema = BaseEntitySchema.extend({
37784
- workspaceId: exports_external.uuid(),
37785
- instanceId: exports_external.string(),
37786
- status: exports_external.enum(InstanceStatus),
37787
- instanceType: exports_external.enum(["t3.micro", "t3.small", "t3.medium"]),
37788
- region: exports_external.enum(AwsRegion).default("us-east-1" /* US_EAST_1 */),
37789
- publicIp: exports_external.string().nullable().optional(),
37790
- launchTime: exports_external.union([exports_external.date(), exports_external.number()]).nullable().optional(),
37791
- repoUrl: exports_external.string().nullable().optional(),
37792
- integrations: exports_external.array(IntegrationSchema).default([])
37793
- });
37794
- CreateAwsInstanceSchema = exports_external.object({
37795
- workspaceId: exports_external.uuid(),
37796
- instanceType: exports_external.enum(["t3.micro", "t3.small", "t3.medium"]).default("t3.micro"),
37797
- region: exports_external.enum(AwsRegion).default("us-east-1" /* US_EAST_1 */),
37798
- repoUrl: exports_external.string().optional(),
37799
- integrations: exports_external.array(IntegrationSchema).optional().default([])
37800
- });
37801
- UpdateAwsInstanceSchema = exports_external.object({
37802
- status: exports_external.enum(InstanceStatus).optional(),
37803
- instanceType: exports_external.enum(["t3.micro", "t3.small", "t3.medium"]).optional(),
37804
- publicIp: exports_external.string().nullable().optional(),
37805
- launchTime: exports_external.union([exports_external.date(), exports_external.number()]).nullable().optional(),
37806
- repoUrl: exports_external.string().nullable().optional(),
37807
- integrations: exports_external.array(IntegrationSchema).optional()
37808
- });
37809
- SaveAwsCredentialsSchema = exports_external.object({
37810
- accessKeyId: exports_external.string().min(16),
37811
- secretAccessKey: exports_external.string().min(1),
37812
- region: exports_external.string().default("us-east-1")
37813
- });
37814
- ProvisionAwsInstanceSchema = exports_external.object({
37815
- repoUrl: exports_external.string().min(1),
37816
- githubToken: exports_external.string().min(1),
37817
- instanceType: exports_external.enum(["t3.micro", "t3.small", "t3.medium"]).default("t3.small"),
37818
- integrations: exports_external.array(IntegrationSchema).optional().default([])
37819
- });
37820
- InstanceActionBodySchema = exports_external.object({
37821
- action: exports_external.nativeEnum(InstanceAction)
37822
- });
37823
- InstanceIdParamSchema = exports_external.object({
37824
- workspaceId: exports_external.string().uuid("Invalid Workspace ID"),
37825
- instanceId: exports_external.string().uuid("Invalid Instance ID")
37826
- });
37827
- CIDR_REGEX = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/;
37828
- UpdateSecurityRulesSchema = exports_external.object({
37829
- allowedIps: exports_external.array(exports_external.string().regex(CIDR_REGEX, "Invalid CIDR format (e.g. 1.2.3.4/32)"))
37830
- });
37831
- });
37832
-
37833
37642
  // ../shared/src/models/ci.ts
37834
37643
  var RecordCiSchema;
37835
37644
  var init_ci2 = __esm(() => {
@@ -38078,49 +37887,6 @@ var init_sprint = __esm(() => {
38078
37887
  });
38079
37888
  });
38080
37889
 
38081
- // ../shared/src/models/suggestion.ts
38082
- var SuggestionStatus, SuggestionType, SuggestionSchema, CreateSuggestionSchema, UpdateSuggestionStatusSchema;
38083
- var init_suggestion = __esm(() => {
38084
- init_zod();
38085
- ((SuggestionStatus2) => {
38086
- SuggestionStatus2["NEW"] = "NEW";
38087
- SuggestionStatus2["NOTIFIED"] = "NOTIFIED";
38088
- SuggestionStatus2["ACTED_ON"] = "ACTED_ON";
38089
- SuggestionStatus2["SKIPPED"] = "SKIPPED";
38090
- SuggestionStatus2["EXPIRED"] = "EXPIRED";
38091
- })(SuggestionStatus ||= {});
38092
- ((SuggestionType2) => {
38093
- SuggestionType2["CODE_FIX"] = "CODE_FIX";
38094
- SuggestionType2["DEPENDENCY_UPDATE"] = "DEPENDENCY_UPDATE";
38095
- SuggestionType2["NEXT_STEP"] = "NEXT_STEP";
38096
- SuggestionType2["REFACTOR"] = "REFACTOR";
38097
- SuggestionType2["TEST_FIX"] = "TEST_FIX";
38098
- })(SuggestionType ||= {});
38099
- SuggestionSchema = exports_external.object({
38100
- id: exports_external.string(),
38101
- type: exports_external.enum(SuggestionType),
38102
- status: exports_external.enum(SuggestionStatus),
38103
- title: exports_external.string(),
38104
- description: exports_external.string(),
38105
- jobRunId: exports_external.string().optional(),
38106
- workspaceId: exports_external.string(),
38107
- createdAt: exports_external.string(),
38108
- expiresAt: exports_external.string(),
38109
- metadata: exports_external.record(exports_external.string(), exports_external.any()).optional()
38110
- });
38111
- CreateSuggestionSchema = exports_external.object({
38112
- type: exports_external.enum(SuggestionType),
38113
- title: exports_external.string().min(1, "Title is required"),
38114
- description: exports_external.string().min(1, "Description is required"),
38115
- jobRunId: exports_external.string().uuid().optional(),
38116
- metadata: exports_external.record(exports_external.string(), exports_external.any()).optional(),
38117
- expiresAt: exports_external.string().optional()
38118
- });
38119
- UpdateSuggestionStatusSchema = exports_external.object({
38120
- status: exports_external.enum(SuggestionStatus)
38121
- });
38122
- });
38123
-
38124
37890
  // ../shared/src/models/task.ts
38125
37891
  var AcceptanceItemSchema, TaskSchema, CreateTaskSchema, UpdateTaskSchema, AddCommentSchema, DispatchTaskSchema, TaskIdParamSchema, TaskQuerySchema, TaskResponseSchema, TasksResponseSchema;
38126
37892
  var init_task = __esm(() => {
@@ -38260,15 +38026,12 @@ var init_models = __esm(() => {
38260
38026
  init_activity();
38261
38027
  init_agent();
38262
38028
  init_auth2();
38263
- init_autonomy();
38264
- init_aws_instance();
38265
38029
  init_ci2();
38266
38030
  init_doc();
38267
38031
  init_doc_group();
38268
38032
  init_invitation();
38269
38033
  init_organization();
38270
38034
  init_sprint();
38271
- init_suggestion();
38272
38035
  init_task();
38273
38036
  init_user();
38274
38037
  init_workspace();
@@ -38931,17 +38694,6 @@ var init_workspaces = __esm(() => {
38931
38694
  async deleteApiKey(workspaceId, keyId) {
38932
38695
  await this.api.delete(`/workspaces/${workspaceId}/api-keys/${keyId}`);
38933
38696
  }
38934
- async getAwsCredentials(workspaceId) {
38935
- const { data } = await this.api.get(`/workspaces/${workspaceId}/aws-credentials`);
38936
- return data.credential;
38937
- }
38938
- async saveAwsCredentials(workspaceId, body) {
38939
- const { data } = await this.api.put(`/workspaces/${workspaceId}/aws-credentials`, body);
38940
- return data.credential;
38941
- }
38942
- async deleteAwsCredentials(workspaceId) {
38943
- await this.api.delete(`/workspaces/${workspaceId}/aws-credentials`);
38944
- }
38945
38697
  };
38946
38698
  });
38947
38699
 
@@ -38990,8 +38742,6 @@ class LocusClient {
38990
38742
  invitations;
38991
38743
  docs;
38992
38744
  ci;
38993
- instances;
38994
- suggestions;
38995
38745
  constructor(config2) {
38996
38746
  this.emitter = new LocusEmitter;
38997
38747
  this.api = axios_default.create({
@@ -39011,8 +38761,6 @@ class LocusClient {
39011
38761
  this.invitations = new InvitationsModule(this.api, this.emitter);
39012
38762
  this.docs = new DocsModule(this.api, this.emitter);
39013
38763
  this.ci = new CiModule(this.api, this.emitter);
39014
- this.instances = new InstancesModule(this.api, this.emitter);
39015
- this.suggestions = new SuggestionsModule(this.api, this.emitter);
39016
38764
  if (config2.retryOptions) {
39017
38765
  this.setupRetryInterceptor(config2.retryOptions);
39018
38766
  }
@@ -39078,11 +38826,9 @@ var init_src2 = __esm(() => {
39078
38826
  init_auth();
39079
38827
  init_ci();
39080
38828
  init_docs();
39081
- init_instances();
39082
38829
  init_invitations();
39083
38830
  init_organizations();
39084
38831
  init_sprints();
39085
- init_suggestions();
39086
38832
  init_tasks();
39087
38833
  init_workspaces();
39088
38834
  init_discussion_types();
@@ -39090,11 +38836,9 @@ var init_src2 = __esm(() => {
39090
38836
  init_auth();
39091
38837
  init_ci();
39092
38838
  init_docs();
39093
- init_instances();
39094
38839
  init_invitations();
39095
38840
  init_organizations();
39096
38841
  init_sprints();
39097
- init_suggestions();
39098
38842
  init_tasks();
39099
38843
  init_workspaces();
39100
38844
  });
@@ -39117,9 +38861,11 @@ class ReviewerWorker {
39117
38861
  currentTaskId = null;
39118
38862
  maxReviews = 50;
39119
38863
  reviewsCompleted = 0;
38864
+ log;
39120
38865
  constructor(config2) {
39121
38866
  this.config = config2;
39122
38867
  const projectPath = config2.projectPath || process.cwd();
38868
+ this.log = createWorkerLogger(config2.agentId, "R");
39123
38869
  this.client = new LocusClient({
39124
38870
  baseUrl: config2.apiBase,
39125
38871
  token: config2.apiKey,
@@ -39130,28 +38876,16 @@ class ReviewerWorker {
39130
38876
  factor: 2
39131
38877
  }
39132
38878
  });
39133
- const log = this.log.bind(this);
39134
38879
  const provider = config2.provider ?? PROVIDER.CLAUDE;
39135
38880
  this.aiRunner = createAiRunner(provider, {
39136
38881
  projectPath,
39137
38882
  model: config2.model,
39138
- log
38883
+ log: this.log
39139
38884
  });
39140
- this.prService = new PrService(projectPath, log);
38885
+ this.prService = new PrService(projectPath, this.log);
39141
38886
  const providerLabel = provider === "codex" ? "Codex" : "Claude";
39142
38887
  this.log(`Reviewer agent using ${providerLabel} CLI`, "info");
39143
38888
  }
39144
- log(message, level = "info") {
39145
- const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
39146
- const colorFn = {
39147
- info: c.cyan,
39148
- success: c.green,
39149
- warn: c.yellow,
39150
- error: c.red
39151
- }[level];
39152
- const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
39153
- console.log(`${c.dim(`[${timestamp}]`)} ${c.bold(`[R:${this.config.agentId.slice(-8)}]`)} ${colorFn(`${prefix} ${message}`)}`);
39154
- }
39155
38889
  getNextUnreviewedPr() {
39156
38890
  const prs = this.prService.listUnreviewedLocusPrs();
39157
38891
  return prs.length > 0 ? prs[0] : null;
@@ -39271,7 +39005,6 @@ var init_reviewer_worker = __esm(() => {
39271
39005
  init_git_utils();
39272
39006
  init_pr_service();
39273
39007
  init_src2();
39274
- init_colors();
39275
39008
  reviewerEntrypoint = process.argv[1]?.split(/[\\/]/).pop();
39276
39009
  if (reviewerEntrypoint === "reviewer-worker.js" || reviewerEntrypoint === "reviewer-worker.ts") {
39277
39010
  process.title = "locus-reviewer";
@@ -39900,9 +39633,11 @@ class AgentWorker {
39900
39633
  currentTaskId = null;
39901
39634
  completedTaskList = [];
39902
39635
  taskSummaries = [];
39636
+ log;
39903
39637
  constructor(config2) {
39904
39638
  this.config = config2;
39905
39639
  const projectPath = config2.projectPath || process.cwd();
39640
+ this.log = createWorkerLogger(config2.agentId);
39906
39641
  this.client = new LocusClient({
39907
39642
  baseUrl: config2.apiBase,
39908
39643
  token: config2.apiKey,
@@ -39913,7 +39648,6 @@ class AgentWorker {
39913
39648
  factor: 2
39914
39649
  }
39915
39650
  });
39916
- const log = this.log.bind(this);
39917
39651
  if (!isGitAvailable()) {
39918
39652
  this.log("git is not installed — branch management will not work", "error");
39919
39653
  }
@@ -39924,29 +39658,18 @@ class AgentWorker {
39924
39658
  this.aiRunner = createAiRunner(provider, {
39925
39659
  projectPath,
39926
39660
  model: config2.model,
39927
- log,
39661
+ log: this.log,
39928
39662
  reasoningEffort: config2.reasoningEffort
39929
39663
  });
39930
39664
  this.taskExecutor = new TaskExecutor({
39931
39665
  aiRunner: this.aiRunner,
39932
39666
  projectPath,
39933
- log
39667
+ log: this.log
39934
39668
  });
39935
- this.gitWorkflow = new GitWorkflow(config2, log);
39669
+ this.gitWorkflow = new GitWorkflow(config2, this.log);
39936
39670
  const providerLabel = provider === "codex" ? "Codex" : "Claude";
39937
39671
  this.log(`Using ${providerLabel} CLI for all phases`, "info");
39938
39672
  }
39939
- log(message, level = "info") {
39940
- const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
39941
- const colorFn = {
39942
- info: c.cyan,
39943
- success: c.green,
39944
- warn: c.yellow,
39945
- error: c.red
39946
- }[level];
39947
- const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
39948
- console.log(`${c.dim(`[${timestamp}]`)} ${c.bold(`[${this.config.agentId.slice(-8)}]`)} ${colorFn(`${prefix} ${message}`)}`);
39949
- }
39950
39673
  async getActiveSprint() {
39951
39674
  try {
39952
39675
  if (this.config.sprintId) {
@@ -40123,7 +39846,6 @@ var init_worker = __esm(() => {
40123
39846
  init_config();
40124
39847
  init_git_utils();
40125
39848
  init_src2();
40126
- init_colors();
40127
39849
  init_git_workflow();
40128
39850
  init_task_executor();
40129
39851
  workerEntrypoint = process.argv[1]?.split(/[\\/]/).pop();
@@ -40154,6 +39876,7 @@ var init_agent2 = __esm(() => {
40154
39876
  // ../sdk/src/ai/index.ts
40155
39877
  var init_ai = __esm(() => {
40156
39878
  init_claude_runner();
39879
+ init_claude_stream_parser();
40157
39880
  init_codex_runner();
40158
39881
  init_factory();
40159
39882
  });
@@ -40325,9 +40048,7 @@ class DiscussionFacilitator {
40325
40048
  this.projectPath = config2.projectPath;
40326
40049
  this.aiRunner = config2.aiRunner;
40327
40050
  this.discussionManager = config2.discussionManager;
40328
- this.log = config2.log ?? ((_msg) => {
40329
- return;
40330
- });
40051
+ this.log = config2.log ?? noopLogger;
40331
40052
  this.provider = config2.provider;
40332
40053
  this.model = config2.model;
40333
40054
  }
@@ -40498,6 +40219,7 @@ If you need more information about the project strategies, plans, or architectur
40498
40219
  }
40499
40220
  }
40500
40221
  var init_discussion_facilitator = __esm(() => {
40222
+ init_factory();
40501
40223
  init_config();
40502
40224
  });
40503
40225
 
@@ -41985,9 +41707,7 @@ class PlanningMeeting {
41985
41707
  constructor(config2) {
41986
41708
  this.projectPath = config2.projectPath;
41987
41709
  this.aiRunner = config2.aiRunner;
41988
- this.log = config2.log ?? ((_msg) => {
41989
- return;
41990
- });
41710
+ this.log = config2.log ?? noopLogger;
41991
41711
  }
41992
41712
  async run(directive, feedback) {
41993
41713
  this.log("Planning sprint...", "info");
@@ -42027,6 +41747,7 @@ class PlanningMeeting {
42027
41747
  }
42028
41748
  }
42029
41749
  var init_planning_meeting = __esm(() => {
41750
+ init_factory();
42030
41751
  init_config();
42031
41752
  });
42032
41753
 
@@ -42037,22 +41758,6 @@ var init_planning = __esm(() => {
42037
41758
  init_sprint_plan();
42038
41759
  });
42039
41760
 
42040
- // ../sdk/src/proposals/context-gatherer.ts
42041
- var init_context_gatherer = () => {};
42042
-
42043
- // ../sdk/src/proposals/proposal-engine.ts
42044
- var init_proposal_engine = __esm(() => {
42045
- init_src();
42046
- init_factory();
42047
- init_context_gatherer();
42048
- });
42049
-
42050
- // ../sdk/src/proposals/index.ts
42051
- var init_proposals = __esm(() => {
42052
- init_context_gatherer();
42053
- init_proposal_engine();
42054
- });
42055
-
42056
41761
  // ../sdk/src/index-node.ts
42057
41762
  var init_index_node = __esm(() => {
42058
41763
  init_prompt_builder();
@@ -42067,27 +41772,315 @@ var init_index_node = __esm(() => {
42067
41772
  init_git();
42068
41773
  init_src2();
42069
41774
  init_planning();
42070
- init_proposals();
41775
+ });
41776
+
41777
+ // ../commands/src/config.ts
41778
+ import { existsSync as existsSync12 } from "node:fs";
41779
+ import { join as join12 } from "node:path";
41780
+ function isProjectInitialized(projectPath) {
41781
+ const locusDir = join12(projectPath, LOCUS_CONFIG.dir);
41782
+ const configPath = join12(locusDir, LOCUS_CONFIG.configFile);
41783
+ return existsSync12(locusDir) && existsSync12(configPath);
41784
+ }
41785
+ function resolveProvider3(input) {
41786
+ if (!input)
41787
+ return PROVIDER.CLAUDE;
41788
+ if (input === PROVIDER.CLAUDE || input === PROVIDER.CODEX)
41789
+ return input;
41790
+ throw new Error(`Invalid provider '${input}'. Must be 'claude' or 'codex'.`);
41791
+ }
41792
+ function maskSecret(value) {
41793
+ if (value.length <= 8)
41794
+ return "****";
41795
+ return `${value.slice(0, 4)}...${value.slice(-4)}`;
41796
+ }
41797
+ var init_config2 = __esm(() => {
41798
+ init_index_node();
41799
+ });
41800
+
41801
+ // ../commands/src/settings.ts
41802
+ import { existsSync as existsSync13, readFileSync as readFileSync11, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "node:fs";
41803
+ import { join as join13 } from "node:path";
41804
+ function getSettingsPath(projectPath) {
41805
+ return join13(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
41806
+ }
41807
+
41808
+ class SettingsManager {
41809
+ projectPath;
41810
+ constructor(projectPath) {
41811
+ this.projectPath = projectPath;
41812
+ }
41813
+ load() {
41814
+ const settingsPath = getSettingsPath(this.projectPath);
41815
+ if (!existsSync13(settingsPath)) {
41816
+ return {};
41817
+ }
41818
+ return JSON.parse(readFileSync11(settingsPath, "utf-8"));
41819
+ }
41820
+ save(settings) {
41821
+ const { $schema: _2, ...rest } = settings;
41822
+ const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
41823
+ const settingsPath = getSettingsPath(this.projectPath);
41824
+ writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
41825
+ }
41826
+ get(key) {
41827
+ return this.load()[key];
41828
+ }
41829
+ set(key, value) {
41830
+ const settings = this.load();
41831
+ settings[key] = value;
41832
+ this.save(settings);
41833
+ }
41834
+ remove() {
41835
+ const settingsPath = getSettingsPath(this.projectPath);
41836
+ if (existsSync13(settingsPath)) {
41837
+ unlinkSync4(settingsPath);
41838
+ }
41839
+ }
41840
+ exists() {
41841
+ return existsSync13(getSettingsPath(this.projectPath));
41842
+ }
41843
+ }
41844
+ var init_settings = __esm(() => {
41845
+ init_index_node();
41846
+ });
41847
+
41848
+ // ../commands/src/logger.ts
41849
+ function createCliLogger() {
41850
+ return (message, level) => {
41851
+ const icon = level === "success" ? c.success("✔") : level === "error" ? c.error("✖") : level === "warn" ? c.warning("!") : c.info("●");
41852
+ console.log(` ${icon} ${message}`);
41853
+ };
41854
+ }
41855
+ var init_logger = __esm(() => {
41856
+ init_index_node();
41857
+ init_index_node();
41858
+ });
41859
+
41860
+ // ../commands/src/workspace.ts
41861
+ class WorkspaceResolver {
41862
+ options;
41863
+ constructor(options) {
41864
+ this.options = options;
41865
+ }
41866
+ async resolve() {
41867
+ if (this.options.workspaceId) {
41868
+ return this.options.workspaceId;
41869
+ }
41870
+ const log = this.options.log ?? noopLogger;
41871
+ try {
41872
+ log("Resolving workspace from API key...");
41873
+ const client = new LocusClient({
41874
+ baseUrl: this.options.apiBase,
41875
+ token: this.options.apiKey
41876
+ });
41877
+ const info = await client.auth.getApiKeyInfo();
41878
+ if (info.workspaceId) {
41879
+ log(`Resolved workspace: ${info.workspaceId}`);
41880
+ return info.workspaceId;
41881
+ }
41882
+ throw new Error("API key is not associated with a workspace. Please specify a workspace ID.");
41883
+ } catch (error48) {
41884
+ if (error48 instanceof Error && error48.message.includes("API key is not")) {
41885
+ throw error48;
41886
+ }
41887
+ throw new Error(`Error resolving workspace: ${error48 instanceof Error ? error48.message : String(error48)}`);
41888
+ }
41889
+ }
41890
+ }
41891
+ var init_workspace2 = __esm(() => {
41892
+ init_src2();
41893
+ init_logger();
41894
+ });
41895
+
41896
+ // ../commands/src/api-context.ts
41897
+ async function resolveApiContext(options) {
41898
+ const settings = new SettingsManager(options.projectPath).load();
41899
+ const apiKey = options.apiKey || settings.apiKey;
41900
+ if (!apiKey) {
41901
+ throw new Error("API key is required. Configure with: locus config setup --api-key <key>");
41902
+ }
41903
+ const apiBase = options.apiUrl || settings.apiUrl || DEFAULT_API_BASE;
41904
+ const resolver = new WorkspaceResolver({
41905
+ apiKey,
41906
+ apiBase,
41907
+ workspaceId: options.workspaceId
41908
+ });
41909
+ const workspaceId = await resolver.resolve();
41910
+ const client = new LocusClient({
41911
+ baseUrl: apiBase,
41912
+ token: apiKey
41913
+ });
41914
+ return { client, workspaceId, apiKey, apiBase };
41915
+ }
41916
+ function resolveAiSettings(options) {
41917
+ const settings = new SettingsManager(options.projectPath).load();
41918
+ const provider = resolveProvider3(options.provider || settings.provider);
41919
+ const model = options.model || settings.model || DEFAULT_MODEL[provider];
41920
+ return { provider, model };
41921
+ }
41922
+ var DEFAULT_API_BASE = "https://api.locusai.dev/api";
41923
+ var init_api_context = __esm(() => {
41924
+ init_src2();
41925
+ init_index_node();
41926
+ init_config2();
41927
+ init_settings();
41928
+ init_workspace2();
41929
+ });
41930
+
41931
+ // ../commands/src/artifacts.ts
41932
+ import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync12, statSync } from "node:fs";
41933
+ import { join as join14 } from "node:path";
41934
+ function listArtifacts(projectPath) {
41935
+ const artifactsDir = getLocusPath(projectPath, "artifactsDir");
41936
+ if (!existsSync14(artifactsDir)) {
41937
+ return [];
41938
+ }
41939
+ const files = readdirSync5(artifactsDir).filter((f) => f.endsWith(".md"));
41940
+ return files.map((fileName) => {
41941
+ const filePath = join14(artifactsDir, fileName);
41942
+ const stat = statSync(filePath);
41943
+ const name = fileName.replace(/\.md$/, "");
41944
+ return {
41945
+ name,
41946
+ fileName,
41947
+ createdAt: stat.birthtime,
41948
+ size: stat.size
41949
+ };
41950
+ }).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
41951
+ }
41952
+ function readArtifact(projectPath, name) {
41953
+ const artifactsDir = getLocusPath(projectPath, "artifactsDir");
41954
+ const fileName = name.endsWith(".md") ? name : `${name}.md`;
41955
+ const filePath = join14(artifactsDir, fileName);
41956
+ if (!existsSync14(filePath)) {
41957
+ return null;
41958
+ }
41959
+ const stat = statSync(filePath);
41960
+ const content = readFileSync12(filePath, "utf-8");
41961
+ return {
41962
+ content,
41963
+ info: {
41964
+ name: fileName.replace(/\.md$/, ""),
41965
+ fileName,
41966
+ createdAt: stat.birthtime,
41967
+ size: stat.size
41968
+ }
41969
+ };
41970
+ }
41971
+ function findArtifact(projectPath, name) {
41972
+ const exact = readArtifact(projectPath, name);
41973
+ if (exact) {
41974
+ return { match: true, content: exact.content, info: exact.info };
41975
+ }
41976
+ const artifacts = listArtifacts(projectPath);
41977
+ const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
41978
+ if (matches.length === 1) {
41979
+ const result = readArtifact(projectPath, matches[0].name);
41980
+ if (result) {
41981
+ return { match: true, content: result.content, info: result.info };
41982
+ }
41983
+ }
41984
+ if (matches.length > 1) {
41985
+ return { match: false, ambiguous: matches };
41986
+ }
41987
+ return null;
41988
+ }
41989
+ function formatSize(bytes) {
41990
+ if (bytes < 1024)
41991
+ return `${bytes}B`;
41992
+ const kb = bytes / 1024;
41993
+ if (kb < 1024)
41994
+ return `${kb.toFixed(1)}KB`;
41995
+ const mb = kb / 1024;
41996
+ return `${mb.toFixed(1)}MB`;
41997
+ }
41998
+ function formatDate(date5) {
41999
+ return date5.toLocaleDateString("en-US", {
42000
+ year: "numeric",
42001
+ month: "short",
42002
+ day: "numeric",
42003
+ hour: "2-digit",
42004
+ minute: "2-digit"
42005
+ });
42006
+ }
42007
+ var init_artifacts = __esm(() => {
42008
+ init_index_node();
42009
+ });
42010
+
42011
+ // ../commands/src/discussions.ts
42012
+ function listDiscussions(manager) {
42013
+ const discussions = manager.list();
42014
+ return discussions.map((d) => ({
42015
+ id: d.id,
42016
+ title: d.title,
42017
+ status: d.status,
42018
+ messageCount: d.messages.length,
42019
+ insightCount: d.insights.length,
42020
+ createdAt: d.createdAt
42021
+ }));
42022
+ }
42023
+ function showDiscussion(manager, id) {
42024
+ return manager.getMarkdown(id);
42025
+ }
42026
+ function archiveDiscussion(manager, id) {
42027
+ manager.archive(id);
42028
+ }
42029
+ function deleteDiscussion(manager, id) {
42030
+ manager.delete(id);
42031
+ }
42032
+
42033
+ // ../commands/src/plans.ts
42034
+ function listPlans(manager, status) {
42035
+ const plans = manager.list(status);
42036
+ return plans.map((p) => ({
42037
+ id: p.id,
42038
+ name: p.name,
42039
+ status: p.status,
42040
+ taskCount: p.tasks.length,
42041
+ directive: p.directive,
42042
+ createdAt: p.createdAt,
42043
+ feedback: p.feedback
42044
+ }));
42045
+ }
42046
+ function showPlan(manager, idOrSlug) {
42047
+ return manager.getMarkdown(idOrSlug);
42048
+ }
42049
+ function cancelPlan(manager, idOrSlug) {
42050
+ manager.cancel(idOrSlug);
42051
+ }
42052
+ function rejectPlan(manager, idOrSlug, feedback) {
42053
+ return manager.reject(idOrSlug, feedback);
42054
+ }
42055
+
42056
+ // ../commands/src/index.ts
42057
+ var init_src3 = __esm(() => {
42058
+ init_api_context();
42059
+ init_artifacts();
42060
+ init_config2();
42061
+ init_logger();
42062
+ init_settings();
42063
+ init_workspace2();
42071
42064
  });
42072
42065
 
42073
42066
  // src/utils/version.ts
42074
- import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
42075
- import { dirname as dirname3, join as join12 } from "node:path";
42067
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
42068
+ import { dirname as dirname3, join as join15 } from "node:path";
42076
42069
  import { fileURLToPath as fileURLToPath3 } from "node:url";
42077
42070
  function getVersion() {
42078
42071
  try {
42079
42072
  const __filename2 = fileURLToPath3(import.meta.url);
42080
42073
  const __dirname2 = dirname3(__filename2);
42081
- const bundledPath = join12(__dirname2, "..", "package.json");
42082
- const sourcePath = join12(__dirname2, "..", "..", "package.json");
42083
- if (existsSync12(bundledPath)) {
42084
- const pkg = JSON.parse(readFileSync11(bundledPath, "utf-8"));
42074
+ const bundledPath = join15(__dirname2, "..", "package.json");
42075
+ const sourcePath = join15(__dirname2, "..", "..", "package.json");
42076
+ if (existsSync15(bundledPath)) {
42077
+ const pkg = JSON.parse(readFileSync13(bundledPath, "utf-8"));
42085
42078
  if (pkg.name === "@locusai/cli") {
42086
42079
  return pkg.version || "0.0.0";
42087
42080
  }
42088
42081
  }
42089
- if (existsSync12(sourcePath)) {
42090
- const pkg = JSON.parse(readFileSync11(sourcePath, "utf-8"));
42082
+ if (existsSync15(sourcePath)) {
42083
+ const pkg = JSON.parse(readFileSync13(sourcePath, "utf-8"));
42091
42084
  if (pkg.name === "@locusai/cli") {
42092
42085
  return pkg.version || "0.0.0";
42093
42086
  }
@@ -42116,13 +42109,6 @@ var init_banner = __esm(() => {
42116
42109
  });
42117
42110
 
42118
42111
  // src/utils/helpers.ts
42119
- import { existsSync as existsSync13 } from "node:fs";
42120
- import { join as join13 } from "node:path";
42121
- function isProjectInitialized(projectPath) {
42122
- const locusDir = join13(projectPath, LOCUS_CONFIG.dir);
42123
- const configPath = join13(locusDir, LOCUS_CONFIG.configFile);
42124
- return existsSync13(locusDir) && existsSync13(configPath);
42125
- }
42126
42112
  function requireInitialization(projectPath, command) {
42127
42113
  if (!isProjectInitialized(projectPath)) {
42128
42114
  console.error(`
@@ -42138,15 +42124,16 @@ function requireInitialization(projectPath, command) {
42138
42124
  process.exit(1);
42139
42125
  }
42140
42126
  }
42141
- function resolveProvider3(input) {
42142
- if (!input)
42143
- return PROVIDER.CLAUDE;
42144
- if (input === PROVIDER.CLAUDE || input === PROVIDER.CODEX)
42145
- return input;
42146
- console.error(c.error(`Error: invalid provider '${input}'. Use 'claude' or 'codex'.`));
42147
- process.exit(1);
42127
+ function resolveProvider4(input) {
42128
+ try {
42129
+ return resolveProvider3(input);
42130
+ } catch {
42131
+ console.error(c.error(`Error: invalid provider '${input}'. Use 'claude' or 'codex'.`));
42132
+ process.exit(1);
42133
+ }
42148
42134
  }
42149
42135
  var init_helpers2 = __esm(() => {
42136
+ init_src3();
42150
42137
  init_index_node();
42151
42138
  });
42152
42139
 
@@ -42157,17 +42144,107 @@ var init_utils3 = __esm(() => {
42157
42144
  init_version();
42158
42145
  });
42159
42146
 
42160
- // src/config-manager.ts
42147
+ // src/templates.ts
42148
+ var LOCUS_GITIGNORE_MARKER = "# Locus AI", LOCUS_MD_TEMPLATE = `## Planning First
42149
+
42150
+ Complex tasks must be planned before writing code. Create ".locus/plans/<task-name>.md" with:
42151
+ - **Goal**: What we're trying to achieve and why
42152
+ - **Approach**: Step-by-step strategy with technical decisions
42153
+ - **Affected files**: List of files to create/modify/delete
42154
+ - **Acceptance criteria**: Specific, testable conditions for completion
42155
+ - **Dependencies**: Required packages, APIs, or external services
42156
+
42157
+ Delete the planning .md files after successful execution.
42158
+
42159
+ ## Code Quality
42160
+
42161
+ - **Follow existing patterns**: Run formatters and linters before finishing (check "package.json" scripts or project config)
42162
+ - **Minimize changes**: Keep modifications atomic. Separate refactors from behavioral changes into different tasks
42163
+ - **Never commit secrets**: No API keys, passwords, or credentials in code. Use environment variables or secret management
42164
+ - **Test as you go**: If tests exist, run relevant ones. If breaking changes occur, update tests accordingly
42165
+ - **Comment complex logic**: Explain *why*, not *what*. Focus on business logic and non-obvious decisions
42166
+
42167
+ ## Artifacts
42168
+
42169
+ When a task produces knowledge, analysis, or research output rather than (or in addition to) code changes, you **must** save results as Markdown in ".locus/artifacts/<descriptive-name>.md":
42170
+
42171
+ **Always create artifacts for:**
42172
+ - Code quality audits, security reviews, vulnerability assessments
42173
+ - Architecture analyses, system design proposals, or recommendations
42174
+ - Dependency reports, performance profiling, benchmarking results
42175
+ - Research summaries, technology comparisons, or feasibility studies
42176
+ - Migration plans, deployment strategies, or rollback procedures
42177
+ - Post-mortems, incident analysis, or debugging investigations
42178
+
42179
+ **Artifact structure:**
42180
+ - Clear title and date
42181
+ - Executive summary (2-3 sentences)
42182
+ - Detailed findings/analysis
42183
+ - Actionable recommendations (if applicable)
42184
+
42185
+ ## Git Operations
42186
+
42187
+ - **Do NOT run**: git add, git commit, git push, git checkout, git branch, or any git commands
42188
+ - **Why**: The Locus orchestrator handles all version control automatically after execution
42189
+ - **Your role**: Focus solely on making file changes. The system commits, pushes, and creates PRs
42190
+
42191
+ ## Continuous Learning
42192
+
42193
+ Read ".locus/LEARNINGS.md" **before starting any task** to avoid repeating mistakes.
42194
+
42195
+ **When to update:**
42196
+ - User corrects your approach or provides guidance
42197
+ - You discover a better pattern while working
42198
+ - A decision prevents future confusion (e.g., "use X not Y because Z")
42199
+ - You encounter and solve a tricky problem
42200
+
42201
+ **What to record:**
42202
+ - Architectural decisions and their rationale
42203
+ - Preferred libraries, tools, or patterns for this project
42204
+ - Common pitfalls and how to avoid them
42205
+ - Project-specific conventions or user preferences
42206
+ - Solutions to non-obvious problems
42207
+
42208
+ **Format (append-only, never delete):**
42209
+
42210
+ """
42211
+ - **[Category]**: Concise description (1-2 lines max). *Context if needed.*
42212
+ """
42213
+
42214
+ **Categories:** Architecture, Dependencies, Patterns, Debugging, Performance, Security, DevOps, User Preferences
42215
+
42216
+ ## Error Handling
42217
+
42218
+ - **Read error messages carefully**: Don't guess. Parse the actual error before proposing fixes
42219
+ - **Check dependencies first**: Missing packages, wrong versions, and environment issues are common
42220
+ - **Verify assumptions**: If something "should work," confirm it actually does in this environment
42221
+ - **Ask for context**: If you need environment details, configuration, or logs, request them explicitly
42222
+
42223
+ ## Communication
42224
+
42225
+ - **Be precise**: When uncertain, state what you know and what you're assuming
42226
+ - **Show your work**: For complex changes, briefly explain the approach before executing
42227
+ - **Highlight trade-offs**: If multiple approaches exist, note why you chose one over others
42228
+ - **Request feedback**: For ambiguous requirements, propose an approach and ask for confirmation
42229
+ `, DEFAULT_LEARNINGS_MD = `# Learnings
42230
+
42231
+ This file captures important lessons, decisions, and corrections made during development.
42232
+ It is read by AI agents before every task to avoid repeating mistakes and to follow established patterns.
42233
+
42234
+ <!-- Add learnings below this line. Format: - **[Category]**: Description -->
42235
+ `;
42236
+
42237
+ // src/git-helpers.ts
42161
42238
  import { execSync as execSync2 } from "node:child_process";
42162
- import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "node:fs";
42163
- import { join as join14 } from "node:path";
42239
+ import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
42240
+ import { join as join16 } from "node:path";
42164
42241
  function updateGitignore(projectPath) {
42165
- const gitignorePath = join14(projectPath, ".gitignore");
42242
+ const gitignorePath = join16(projectPath, ".gitignore");
42166
42243
  let content = "";
42167
42244
  const locusBlock = LOCUS_GITIGNORE_PATTERNS.join(`
42168
42245
  `);
42169
- if (existsSync14(gitignorePath)) {
42170
- content = readFileSync12(gitignorePath, "utf-8");
42246
+ if (existsSync16(gitignorePath)) {
42247
+ content = readFileSync14(gitignorePath, "utf-8");
42171
42248
  if (content.includes(LOCUS_GITIGNORE_MARKER)) {
42172
42249
  const lines = content.split(`
42173
42250
  `);
@@ -42184,7 +42261,7 @@ function updateGitignore(projectPath) {
42184
42261
  const after = lines.slice(endIdx + 1);
42185
42262
  content = [...before, locusBlock, ...after].join(`
42186
42263
  `);
42187
- writeFileSync6(gitignorePath, content);
42264
+ writeFileSync7(gitignorePath, content);
42188
42265
  return;
42189
42266
  }
42190
42267
  if (content.length > 0 && !content.endsWith(`
@@ -42199,7 +42276,7 @@ function updateGitignore(projectPath) {
42199
42276
  }
42200
42277
  content += `${locusBlock}
42201
42278
  `;
42202
- writeFileSync6(gitignorePath, content);
42279
+ writeFileSync7(gitignorePath, content);
42203
42280
  }
42204
42281
  function ensureGitIdentity(projectPath) {
42205
42282
  const hasName = (() => {
@@ -42239,6 +42316,13 @@ function ensureGitIdentity(projectPath) {
42239
42316
  stdio: "ignore"
42240
42317
  });
42241
42318
  }
42319
+ var init_git_helpers = __esm(() => {
42320
+ init_index_node();
42321
+ });
42322
+
42323
+ // src/config-manager.ts
42324
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
42325
+ import { join as join17 } from "node:path";
42242
42326
 
42243
42327
  class ConfigManager {
42244
42328
  projectPath;
@@ -42246,9 +42330,9 @@ class ConfigManager {
42246
42330
  this.projectPath = projectPath;
42247
42331
  }
42248
42332
  async init(version2) {
42249
- const locusConfigDir = join14(this.projectPath, LOCUS_CONFIG.dir);
42333
+ const locusConfigDir = join17(this.projectPath, LOCUS_CONFIG.dir);
42250
42334
  const locusConfigPath = getLocusPath(this.projectPath, "configFile");
42251
- if (!existsSync14(locusConfigDir)) {
42335
+ if (!existsSync17(locusConfigDir)) {
42252
42336
  mkdirSync7(locusConfigDir, { recursive: true });
42253
42337
  }
42254
42338
  const locusSubdirs = [
@@ -42260,35 +42344,35 @@ class ConfigManager {
42260
42344
  LOCUS_CONFIG.discussionsDir
42261
42345
  ];
42262
42346
  for (const subdir of locusSubdirs) {
42263
- const subdirPath = join14(locusConfigDir, subdir);
42264
- if (!existsSync14(subdirPath)) {
42347
+ const subdirPath = join17(locusConfigDir, subdir);
42348
+ if (!existsSync17(subdirPath)) {
42265
42349
  mkdirSync7(subdirPath, { recursive: true });
42266
42350
  }
42267
42351
  }
42268
42352
  const locusMdPath = getLocusPath(this.projectPath, "contextFile");
42269
- if (!existsSync14(locusMdPath)) {
42270
- writeFileSync6(locusMdPath, LOCUS_MD_TEMPLATE);
42353
+ if (!existsSync17(locusMdPath)) {
42354
+ writeFileSync8(locusMdPath, LOCUS_MD_TEMPLATE);
42271
42355
  }
42272
42356
  const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
42273
- if (!existsSync14(learningsMdPath)) {
42274
- writeFileSync6(learningsMdPath, DEFAULT_LEARNINGS_MD);
42357
+ if (!existsSync17(learningsMdPath)) {
42358
+ writeFileSync8(learningsMdPath, DEFAULT_LEARNINGS_MD);
42275
42359
  }
42276
- if (!existsSync14(locusConfigPath)) {
42360
+ if (!existsSync17(locusConfigPath)) {
42277
42361
  const config2 = {
42278
42362
  $schema: LOCUS_SCHEMAS.config,
42279
42363
  version: version2,
42280
42364
  createdAt: new Date().toISOString(),
42281
42365
  projectPath: "."
42282
42366
  };
42283
- writeFileSync6(locusConfigPath, JSON.stringify(config2, null, 2));
42367
+ writeFileSync8(locusConfigPath, JSON.stringify(config2, null, 2));
42284
42368
  }
42285
42369
  updateGitignore(this.projectPath);
42286
42370
  ensureGitIdentity(this.projectPath);
42287
42371
  }
42288
42372
  loadConfig() {
42289
42373
  const path2 = getLocusPath(this.projectPath, "configFile");
42290
- if (existsSync14(path2)) {
42291
- return JSON.parse(readFileSync12(path2, "utf-8"));
42374
+ if (existsSync17(path2)) {
42375
+ return JSON.parse(readFileSync15(path2, "utf-8"));
42292
42376
  }
42293
42377
  return null;
42294
42378
  }
@@ -42318,19 +42402,19 @@ class ConfigManager {
42318
42402
  this.saveConfig(config2);
42319
42403
  }
42320
42404
  }
42321
- const settingsPath = join14(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
42322
- if (existsSync14(settingsPath)) {
42323
- const raw = readFileSync12(settingsPath, "utf-8");
42405
+ const settingsPath = join17(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
42406
+ if (existsSync17(settingsPath)) {
42407
+ const raw = readFileSync15(settingsPath, "utf-8");
42324
42408
  const settings = JSON.parse(raw);
42325
42409
  if (settings.$schema !== LOCUS_SCHEMAS.settings) {
42326
42410
  const { $schema: _2, ...rest } = settings;
42327
42411
  const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
42328
- writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
42412
+ writeFileSync8(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
42329
42413
  }
42330
42414
  }
42331
42415
  const locusMdPath = getLocusPath(this.projectPath, "contextFile");
42332
- const locusMdExisted = existsSync14(locusMdPath);
42333
- writeFileSync6(locusMdPath, LOCUS_MD_TEMPLATE);
42416
+ const locusMdExisted = existsSync17(locusMdPath);
42417
+ writeFileSync8(locusMdPath, LOCUS_MD_TEMPLATE);
42334
42418
  if (!locusMdExisted) {
42335
42419
  result.directoriesCreated.push(".locus/LOCUS.md");
42336
42420
  }
@@ -42342,23 +42426,23 @@ class ConfigManager {
42342
42426
  LOCUS_CONFIG.plansDir,
42343
42427
  LOCUS_CONFIG.discussionsDir
42344
42428
  ];
42345
- const locusConfigDir = join14(this.projectPath, LOCUS_CONFIG.dir);
42429
+ const locusConfigDir = join17(this.projectPath, LOCUS_CONFIG.dir);
42346
42430
  for (const subdir of locusSubdirs) {
42347
- const subdirPath = join14(locusConfigDir, subdir);
42348
- if (!existsSync14(subdirPath)) {
42431
+ const subdirPath = join17(locusConfigDir, subdir);
42432
+ if (!existsSync17(subdirPath)) {
42349
42433
  mkdirSync7(subdirPath, { recursive: true });
42350
42434
  result.directoriesCreated.push(`.locus/${subdir}`);
42351
42435
  }
42352
42436
  }
42353
42437
  const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
42354
- if (!existsSync14(learningsMdPath)) {
42355
- writeFileSync6(learningsMdPath, DEFAULT_LEARNINGS_MD);
42438
+ if (!existsSync17(learningsMdPath)) {
42439
+ writeFileSync8(learningsMdPath, DEFAULT_LEARNINGS_MD);
42356
42440
  result.directoriesCreated.push(".locus/LEARNINGS.md");
42357
42441
  }
42358
- const gitignorePath = join14(this.projectPath, ".gitignore");
42359
- const gitignoreBefore = existsSync14(gitignorePath) ? readFileSync12(gitignorePath, "utf-8") : "";
42442
+ const gitignorePath = join17(this.projectPath, ".gitignore");
42443
+ const gitignoreBefore = existsSync17(gitignorePath) ? readFileSync15(gitignorePath, "utf-8") : "";
42360
42444
  updateGitignore(this.projectPath);
42361
- const gitignoreAfter = readFileSync12(gitignorePath, "utf-8");
42445
+ const gitignoreAfter = readFileSync15(gitignorePath, "utf-8");
42362
42446
  if (gitignoreBefore !== gitignoreAfter) {
42363
42447
  result.gitignoreUpdated = true;
42364
42448
  }
@@ -42369,190 +42453,12 @@ class ConfigManager {
42369
42453
  const { $schema: _2, ...rest } = config2;
42370
42454
  const ordered = { $schema: LOCUS_SCHEMAS.config, ...rest };
42371
42455
  const path2 = getLocusPath(this.projectPath, "configFile");
42372
- writeFileSync6(path2, JSON.stringify(ordered, null, 2));
42456
+ writeFileSync8(path2, JSON.stringify(ordered, null, 2));
42373
42457
  }
42374
42458
  }
42375
- var LOCUS_GITIGNORE_MARKER = "# Locus AI", LOCUS_MD_TEMPLATE = `## Planning First
42376
-
42377
- Complex tasks must be planned before writing code. Create ".locus/plans/<task-name>.md" with:
42378
- - **Goal**: What we're trying to achieve and why
42379
- - **Approach**: Step-by-step strategy with technical decisions
42380
- - **Affected files**: List of files to create/modify/delete
42381
- - **Acceptance criteria**: Specific, testable conditions for completion
42382
- - **Dependencies**: Required packages, APIs, or external services
42383
-
42384
- Delete the planning .md files after successful execution.
42385
-
42386
- ## Code Quality
42387
-
42388
- - **Follow existing patterns**: Run formatters and linters before finishing (check "package.json" scripts or project config)
42389
- - **Minimize changes**: Keep modifications atomic. Separate refactors from behavioral changes into different tasks
42390
- - **Never commit secrets**: No API keys, passwords, or credentials in code. Use environment variables or secret management
42391
- - **Test as you go**: If tests exist, run relevant ones. If breaking changes occur, update tests accordingly
42392
- - **Comment complex logic**: Explain *why*, not *what*. Focus on business logic and non-obvious decisions
42393
-
42394
- ## Artifacts
42395
-
42396
- When a task produces knowledge, analysis, or research output rather than (or in addition to) code changes, you **must** save results as Markdown in ".locus/artifacts/<descriptive-name>.md":
42397
-
42398
- **Always create artifacts for:**
42399
- - Code quality audits, security reviews, vulnerability assessments
42400
- - Architecture analyses, system design proposals, or recommendations
42401
- - Dependency reports, performance profiling, benchmarking results
42402
- - Research summaries, technology comparisons, or feasibility studies
42403
- - Migration plans, deployment strategies, or rollback procedures
42404
- - Post-mortems, incident analysis, or debugging investigations
42405
-
42406
- **Artifact structure:**
42407
- - Clear title and date
42408
- - Executive summary (2-3 sentences)
42409
- - Detailed findings/analysis
42410
- - Actionable recommendations (if applicable)
42411
-
42412
- ## Git Operations
42413
-
42414
- - **Do NOT run**: git add, git commit, git push, git checkout, git branch, or any git commands
42415
- - **Why**: The Locus orchestrator handles all version control automatically after execution
42416
- - **Your role**: Focus solely on making file changes. The system commits, pushes, and creates PRs
42417
-
42418
- ## Continuous Learning
42419
-
42420
- Read ".locus/LEARNINGS.md" **before starting any task** to avoid repeating mistakes.
42421
-
42422
- **When to update:**
42423
- - User corrects your approach or provides guidance
42424
- - You discover a better pattern while working
42425
- - A decision prevents future confusion (e.g., "use X not Y because Z")
42426
- - You encounter and solve a tricky problem
42427
-
42428
- **What to record:**
42429
- - Architectural decisions and their rationale
42430
- - Preferred libraries, tools, or patterns for this project
42431
- - Common pitfalls and how to avoid them
42432
- - Project-specific conventions or user preferences
42433
- - Solutions to non-obvious problems
42434
-
42435
- **Format (append-only, never delete):**
42436
-
42437
- """
42438
- - **[Category]**: Concise description (1-2 lines max). *Context if needed.*
42439
- """
42440
-
42441
- **Categories:** Architecture, Dependencies, Patterns, Debugging, Performance, Security, DevOps, User Preferences
42442
-
42443
- ## Error Handling
42444
-
42445
- - **Read error messages carefully**: Don't guess. Parse the actual error before proposing fixes
42446
- - **Check dependencies first**: Missing packages, wrong versions, and environment issues are common
42447
- - **Verify assumptions**: If something "should work," confirm it actually does in this environment
42448
- - **Ask for context**: If you need environment details, configuration, or logs, request them explicitly
42449
-
42450
- ## Communication
42451
-
42452
- - **Be precise**: When uncertain, state what you know and what you're assuming
42453
- - **Show your work**: For complex changes, briefly explain the approach before executing
42454
- - **Highlight trade-offs**: If multiple approaches exist, note why you chose one over others
42455
- - **Request feedback**: For ambiguous requirements, propose an approach and ask for confirmation
42456
- `, DEFAULT_LEARNINGS_MD = `# Learnings
42457
-
42458
- This file captures important lessons, decisions, and corrections made during development.
42459
- It is read by AI agents before every task to avoid repeating mistakes and to follow established patterns.
42460
-
42461
- <!-- Add learnings below this line. Format: - **[Category]**: Description -->
42462
- `;
42463
42459
  var init_config_manager = __esm(() => {
42464
42460
  init_index_node();
42465
- });
42466
-
42467
- // src/settings-manager.ts
42468
- import { existsSync as existsSync15, readFileSync as readFileSync13, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "node:fs";
42469
- import { join as join15 } from "node:path";
42470
- function getSettingsPath(projectPath) {
42471
- return join15(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
42472
- }
42473
-
42474
- class SettingsManager {
42475
- projectPath;
42476
- constructor(projectPath) {
42477
- this.projectPath = projectPath;
42478
- }
42479
- load() {
42480
- const settingsPath = getSettingsPath(this.projectPath);
42481
- if (!existsSync15(settingsPath)) {
42482
- return {};
42483
- }
42484
- return JSON.parse(readFileSync13(settingsPath, "utf-8"));
42485
- }
42486
- save(settings) {
42487
- const { $schema: _2, ...rest } = settings;
42488
- const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
42489
- const settingsPath = getSettingsPath(this.projectPath);
42490
- writeFileSync7(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
42491
- }
42492
- get(key) {
42493
- return this.load()[key];
42494
- }
42495
- set(key, value) {
42496
- const settings = this.load();
42497
- settings[key] = value;
42498
- this.save(settings);
42499
- }
42500
- remove() {
42501
- const settingsPath = getSettingsPath(this.projectPath);
42502
- if (existsSync15(settingsPath)) {
42503
- unlinkSync4(settingsPath);
42504
- }
42505
- }
42506
- exists() {
42507
- return existsSync15(getSettingsPath(this.projectPath));
42508
- }
42509
- getAutonomyRules() {
42510
- const settings = this.load();
42511
- const userRules = settings.autonomy?.rules ?? [];
42512
- if (userRules.length === 0) {
42513
- return DEFAULT_AUTONOMY_RULES;
42514
- }
42515
- const ruleMap = new Map(DEFAULT_AUTONOMY_RULES.map((r) => [r.category, r]));
42516
- for (const rule of userRules) {
42517
- ruleMap.set(rule.category, rule);
42518
- }
42519
- return Array.from(ruleMap.values());
42520
- }
42521
- }
42522
- var init_settings_manager = __esm(() => {
42523
- init_index_node();
42524
- init_src();
42525
- });
42526
-
42527
- // src/workspace-resolver.ts
42528
- class WorkspaceResolver {
42529
- options;
42530
- constructor(options) {
42531
- this.options = options;
42532
- }
42533
- async resolve() {
42534
- if (this.options.workspaceId) {
42535
- return this.options.workspaceId;
42536
- }
42537
- try {
42538
- console.log(c.dim("ℹ Resolving workspace from API key..."));
42539
- const client = new LocusClient({
42540
- baseUrl: this.options.apiBase,
42541
- token: this.options.apiKey
42542
- });
42543
- const info = await client.auth.getApiKeyInfo();
42544
- if (info.workspaceId) {
42545
- console.log(c.success(`✓ Resolved workspace: ${info.workspaceId}`));
42546
- return info.workspaceId;
42547
- }
42548
- throw new Error("API key is not associated with a workspace. Please specify --workspace.");
42549
- } catch (error48) {
42550
- throw new Error(`Error resolving workspace: ${error48 instanceof Error ? error48.message : String(error48)}`);
42551
- }
42552
- }
42553
- }
42554
- var init_workspace_resolver = __esm(() => {
42555
- init_index_node();
42461
+ init_git_helpers();
42556
42462
  });
42557
42463
 
42558
42464
  // src/commands/plan.ts
@@ -42560,8 +42466,8 @@ var exports_plan = {};
42560
42466
  __export(exports_plan, {
42561
42467
  planCommand: () => planCommand
42562
42468
  });
42563
- import { existsSync as existsSync16, unlinkSync as unlinkSync5 } from "node:fs";
42564
- import { join as join16 } from "node:path";
42469
+ import { existsSync as existsSync18, unlinkSync as unlinkSync5 } from "node:fs";
42470
+ import { join as join18 } from "node:path";
42565
42471
  import { parseArgs } from "node:util";
42566
42472
  function normalizePlanIdArgs(args) {
42567
42473
  const planIdFlags = new Set(["--approve", "--reject", "--cancel", "--show"]);
@@ -42605,13 +42511,34 @@ async function planCommand(args) {
42605
42511
  requireInitialization(projectPath, "plan");
42606
42512
  const planManager = new PlanManager(projectPath);
42607
42513
  if (values.list) {
42608
- return listPlans(planManager);
42514
+ return renderPlanList(planManager);
42609
42515
  }
42610
42516
  if (values.show) {
42611
- return showPlan(planManager, values.show);
42517
+ const md = showPlan(planManager, values.show);
42518
+ if (!md) {
42519
+ console.error(`
42520
+ ${c.error("✖")} ${c.red(`Plan not found: ${values.show}`)}
42521
+ `);
42522
+ process.exit(1);
42523
+ }
42524
+ console.log(`
42525
+ ${md}
42526
+ `);
42527
+ return;
42612
42528
  }
42613
42529
  if (values.cancel) {
42614
- return cancelPlan(planManager, values.cancel);
42530
+ try {
42531
+ cancelPlan(planManager, values.cancel);
42532
+ console.log(`
42533
+ ${c.success("✔")} ${c.dim("Plan cancelled.")}
42534
+ `);
42535
+ } catch (error48) {
42536
+ console.error(`
42537
+ ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
42538
+ `);
42539
+ process.exit(1);
42540
+ }
42541
+ return;
42615
42542
  }
42616
42543
  if (values.reject) {
42617
42544
  const feedback2 = values.feedback;
@@ -42623,30 +42550,59 @@ async function planCommand(args) {
42623
42550
  `);
42624
42551
  process.exit(1);
42625
42552
  }
42626
- return rejectPlan(planManager, values.reject, feedback2);
42553
+ try {
42554
+ const plan = rejectPlan(planManager, values.reject, feedback2);
42555
+ console.log(`
42556
+ ${c.warning("!")} ${c.bold("Plan rejected:")} ${plan.name}
42557
+ `);
42558
+ console.log(` ${c.dim("Feedback saved:")} ${feedback2}
42559
+ `);
42560
+ console.log(` ${c.dim("Re-run the planning meeting with the same directive to incorporate feedback:")}`);
42561
+ console.log(` ${c.cyan(`locus plan "${plan.directive}"`)}
42562
+ `);
42563
+ } catch (error48) {
42564
+ console.error(`
42565
+ ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
42566
+ `);
42567
+ process.exit(1);
42568
+ }
42569
+ return;
42627
42570
  }
42628
42571
  if (values.approve) {
42629
- const { client, workspaceId } = await resolveApiContext(projectPath, values);
42630
- return approvePlan(planManager, values.approve, client, workspaceId);
42572
+ const configManager = new ConfigManager(projectPath);
42573
+ configManager.updateVersion(VERSION2);
42574
+ try {
42575
+ const { client, workspaceId } = await resolveApiContext({
42576
+ projectPath,
42577
+ apiKey: values["api-key"],
42578
+ apiUrl: values["api-url"],
42579
+ workspaceId: values.workspace
42580
+ });
42581
+ return await renderApprovePlan(planManager, values.approve, client, workspaceId);
42582
+ } catch (error48) {
42583
+ console.error(`
42584
+ ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
42585
+ `);
42586
+ process.exit(1);
42587
+ }
42631
42588
  }
42632
42589
  const directive = positionals.join(" ").trim();
42633
42590
  if (!directive) {
42634
42591
  showPlanHelp();
42635
42592
  return;
42636
42593
  }
42637
- const planSettings = new SettingsManager(projectPath).load();
42638
- const provider = resolveProvider3(values.provider || planSettings.provider);
42639
- const model = values.model || planSettings.model || DEFAULT_MODEL[provider];
42594
+ const { provider, model } = resolveAiSettings({
42595
+ projectPath,
42596
+ provider: values.provider,
42597
+ model: values.model
42598
+ });
42640
42599
  const reasoningEffort = values["reasoning-effort"];
42641
42600
  const aiRunner = createAiRunner(provider, {
42642
42601
  projectPath,
42643
42602
  model,
42644
42603
  reasoningEffort
42645
42604
  });
42646
- const log = (message, level) => {
42647
- const icon = level === "success" ? c.success("✔") : level === "error" ? c.error("✖") : level === "warn" ? c.warning("!") : c.info("●");
42648
- console.log(` ${icon} ${message}`);
42649
- };
42605
+ const log = createCliLogger();
42650
42606
  console.log(`
42651
42607
  ${c.header(" PLANNING MEETING ")} ${c.bold("Starting async planning meeting...")}
42652
42608
  `);
@@ -42668,8 +42624,8 @@ async function planCommand(args) {
42668
42624
  try {
42669
42625
  const result = await meeting.run(directive, feedback);
42670
42626
  planManager.save(result.plan);
42671
- const tempFile = join16(getLocusPath(projectPath, "plansDir"), `${result.plan.id}.json`);
42672
- if (existsSync16(tempFile)) {
42627
+ const tempFile = join18(getLocusPath(projectPath, "plansDir"), `${result.plan.id}.json`);
42628
+ if (existsSync18(tempFile)) {
42673
42629
  unlinkSync5(tempFile);
42674
42630
  }
42675
42631
  console.log(`
@@ -42690,8 +42646,8 @@ async function planCommand(args) {
42690
42646
  process.exit(1);
42691
42647
  }
42692
42648
  }
42693
- function listPlans(planManager) {
42694
- const plans = planManager.list();
42649
+ function renderPlanList(planManager) {
42650
+ const plans = listPlans(planManager);
42695
42651
  if (plans.length === 0) {
42696
42652
  console.log(`
42697
42653
  ${c.dim("No plans found.")}
@@ -42705,7 +42661,7 @@ function listPlans(planManager) {
42705
42661
  `);
42706
42662
  for (const plan of plans) {
42707
42663
  const statusIcon = plan.status === "pending" ? c.warning("◯") : plan.status === "approved" ? c.success("✔") : plan.status === "rejected" ? c.error("✖") : c.dim("⊘");
42708
- console.log(` ${statusIcon} ${c.bold(plan.name)} ${c.dim(`[${plan.status}]`)} ${c.dim(`— ${plan.tasks.length} tasks`)}`);
42664
+ console.log(` ${statusIcon} ${c.bold(plan.name)} ${c.dim(`[${plan.status}]`)} ${c.dim(`— ${plan.taskCount} tasks`)}`);
42709
42665
  console.log(` ${c.dim("ID:")} ${plan.id}`);
42710
42666
  console.log(` ${c.dim("Created:")} ${plan.createdAt}`);
42711
42667
  if (plan.feedback) {
@@ -42714,50 +42670,7 @@ function listPlans(planManager) {
42714
42670
  console.log("");
42715
42671
  }
42716
42672
  }
42717
- function showPlan(planManager, idOrSlug) {
42718
- const md = planManager.getMarkdown(idOrSlug);
42719
- if (!md) {
42720
- console.error(`
42721
- ${c.error("✖")} ${c.red(`Plan not found: ${idOrSlug}`)}
42722
- `);
42723
- process.exit(1);
42724
- }
42725
- console.log(`
42726
- ${md}
42727
- `);
42728
- }
42729
- function cancelPlan(planManager, idOrSlug) {
42730
- try {
42731
- planManager.cancel(idOrSlug);
42732
- console.log(`
42733
- ${c.success("✔")} ${c.dim("Plan cancelled.")}
42734
- `);
42735
- } catch (error48) {
42736
- console.error(`
42737
- ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
42738
- `);
42739
- process.exit(1);
42740
- }
42741
- }
42742
- function rejectPlan(planManager, idOrSlug, feedback) {
42743
- try {
42744
- const plan = planManager.reject(idOrSlug, feedback);
42745
- console.log(`
42746
- ${c.warning("!")} ${c.bold("Plan rejected:")} ${plan.name}
42747
- `);
42748
- console.log(` ${c.dim("Feedback saved:")} ${feedback}
42749
- `);
42750
- console.log(` ${c.dim("Re-run the planning meeting with the same directive to incorporate feedback:")}`);
42751
- console.log(` ${c.cyan(`locus plan "${plan.directive}"`)}
42752
- `);
42753
- } catch (error48) {
42754
- console.error(`
42755
- ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
42756
- `);
42757
- process.exit(1);
42758
- }
42759
- }
42760
- async function approvePlan(planManager, idOrSlug, client, workspaceId) {
42673
+ async function renderApprovePlan(planManager, idOrSlug, client, workspaceId) {
42761
42674
  try {
42762
42675
  console.log(`
42763
42676
  ${c.info("●")} ${c.bold("Approving plan and creating sprint...")}
@@ -42780,34 +42693,6 @@ async function approvePlan(planManager, idOrSlug, client, workspaceId) {
42780
42693
  process.exit(1);
42781
42694
  }
42782
42695
  }
42783
- async function resolveApiContext(projectPath, values) {
42784
- const configManager = new ConfigManager(projectPath);
42785
- configManager.updateVersion(VERSION2);
42786
- const settingsManager = new SettingsManager(projectPath);
42787
- const settings = settingsManager.load();
42788
- const apiKey = values["api-key"] || settings.apiKey;
42789
- if (!apiKey) {
42790
- console.error(`
42791
- ${c.error("✖")} ${c.red("API key is required for this operation")}
42792
- `);
42793
- console.error(` ${c.dim(`Configure with: locus config setup --api-key <key>
42794
- Or pass --api-key flag`)}
42795
- `);
42796
- process.exit(1);
42797
- }
42798
- const apiBase = values["api-url"] || settings.apiUrl || "https://api.locusai.dev/api";
42799
- const resolver = new WorkspaceResolver({
42800
- apiKey,
42801
- apiBase,
42802
- workspaceId: values.workspace
42803
- });
42804
- const workspaceId = await resolver.resolve();
42805
- const client = new LocusClient({
42806
- baseUrl: apiBase,
42807
- token: apiKey
42808
- });
42809
- return { client, workspaceId };
42810
- }
42811
42696
  function printPlanSummary(plan) {
42812
42697
  console.log(` ${c.bold("Sprint:")} ${plan.name}`);
42813
42698
  console.log(` ${c.bold("Goal:")} ${plan.goal}`);
@@ -42865,11 +42750,10 @@ function showPlanHelp() {
42865
42750
  `);
42866
42751
  }
42867
42752
  var init_plan = __esm(() => {
42753
+ init_src3();
42868
42754
  init_index_node();
42869
42755
  init_config_manager();
42870
- init_settings_manager();
42871
42756
  init_utils3();
42872
- init_workspace_resolver();
42873
42757
  });
42874
42758
 
42875
42759
  // src/display/tool-display.ts
@@ -43126,19 +43010,31 @@ class ProgressRenderer {
43126
43010
  toolDisplay = new ToolDisplay;
43127
43011
  toolDisplayShown = false;
43128
43012
  thinkingShown = false;
43013
+ animated;
43129
43014
  spinnerInterval = null;
43130
43015
  spinnerFrameIndex = 0;
43131
43016
  thinkingStartTime = null;
43132
43017
  isInTextBlock = false;
43133
43018
  textBuffer = "";
43019
+ constructor(options = {}) {
43020
+ this.animated = options.animated ?? false;
43021
+ }
43134
43022
  showThinkingStarted() {
43135
43023
  if (this.isThinking)
43136
43024
  return;
43137
43025
  this.isThinking = true;
43138
43026
  this.thinkingStartTime = Date.now();
43139
- if (!this.thinkingShown) {
43140
- this.thinkingShown = true;
43141
- this.startThinkingAnimation();
43027
+ if (this.animated) {
43028
+ if (!this.thinkingShown) {
43029
+ this.thinkingShown = true;
43030
+ this.startThinkingAnimation();
43031
+ }
43032
+ } else {
43033
+ if (!this.thinkingShown) {
43034
+ console.log(c.dim(`\uD83E\uDD14 Thinking...
43035
+ `));
43036
+ this.thinkingShown = true;
43037
+ }
43142
43038
  }
43143
43039
  }
43144
43040
  showThinkingStopped() {
@@ -43169,7 +43065,7 @@ class ProgressRenderer {
43169
43065
  clearInterval(this.spinnerInterval);
43170
43066
  this.spinnerInterval = null;
43171
43067
  }
43172
- if (this.thinkingShown && this.isThinking) {
43068
+ if (this.animated && this.thinkingShown && this.isThinking) {
43173
43069
  process.stdout.write(`${ANSI.MOVE_TO_START}${ANSI.CLEAR_LINE}
43174
43070
  `);
43175
43071
  }
@@ -43451,9 +43347,9 @@ var init_progress_renderer = __esm(() => {
43451
43347
  });
43452
43348
 
43453
43349
  // src/repl/image-detect.ts
43454
- import { copyFileSync, existsSync as existsSync20, mkdirSync as mkdirSync9 } from "node:fs";
43455
- import { homedir as homedir4, tmpdir as tmpdir2 } from "node:os";
43456
- import { basename as basename2, join as join20 } from "node:path";
43350
+ import { copyFileSync, existsSync as existsSync19, mkdirSync as mkdirSync8 } from "node:fs";
43351
+ import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
43352
+ import { basename as basename2, join as join19 } from "node:path";
43457
43353
  function hasImageExtension(p) {
43458
43354
  const dot = p.lastIndexOf(".");
43459
43355
  if (dot === -1)
@@ -43466,16 +43362,16 @@ function resolvePath(raw) {
43466
43362
  p = p.slice(1, -1);
43467
43363
  }
43468
43364
  if (p.startsWith("~/")) {
43469
- p = homedir4() + p.slice(1);
43365
+ p = homedir2() + p.slice(1);
43470
43366
  }
43471
43367
  return p;
43472
43368
  }
43473
43369
  function copyToStable(srcPath) {
43474
43370
  try {
43475
- mkdirSync9(STABLE_IMAGE_DIR, { recursive: true });
43371
+ mkdirSync8(STABLE_IMAGE_DIR, { recursive: true });
43476
43372
  const ts = Date.now();
43477
43373
  const name = `${ts}-${basename2(srcPath)}`;
43478
- const destPath = join20(STABLE_IMAGE_DIR, name);
43374
+ const destPath = join19(STABLE_IMAGE_DIR, name);
43479
43375
  copyFileSync(srcPath, destPath);
43480
43376
  return destPath;
43481
43377
  } catch {
@@ -43495,7 +43391,7 @@ function detectImages(input) {
43495
43391
  let exists = false;
43496
43392
  let stablePath = normalized;
43497
43393
  try {
43498
- exists = existsSync20(normalized);
43394
+ exists = existsSync19(normalized);
43499
43395
  } catch {}
43500
43396
  if (exists) {
43501
43397
  const copied = copyToStable(normalized);
@@ -43569,7 +43465,7 @@ var init_image_detect = __esm(() => {
43569
43465
  ".tif",
43570
43466
  ".tiff"
43571
43467
  ]);
43572
- STABLE_IMAGE_DIR = join20(tmpdir2(), "locus-images");
43468
+ STABLE_IMAGE_DIR = join19(tmpdir2(), "locus-images");
43573
43469
  });
43574
43470
 
43575
43471
  // src/repl/input-handler.ts
@@ -44112,7 +44008,7 @@ class InteractiveSession {
44112
44008
  model: options.model
44113
44009
  });
44114
44010
  this.promptBuilder = new PromptBuilder(options.projectPath);
44115
- this.renderer = new ProgressRenderer;
44011
+ this.renderer = new ProgressRenderer({ animated: true });
44116
44012
  this.historyManager = new HistoryManager(options.projectPath);
44117
44013
  this.projectPath = options.projectPath;
44118
44014
  this.model = options.model;
@@ -44326,66 +44222,10 @@ var init_interactive_session = __esm(() => {
44326
44222
  init_index_node();
44327
44223
 
44328
44224
  // src/commands/artifacts.ts
44225
+ init_src3();
44329
44226
  init_index_node();
44330
44227
  init_utils3();
44331
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync14, statSync } from "node:fs";
44332
- import { join as join17 } from "node:path";
44333
44228
  import { parseArgs as parseArgs2 } from "node:util";
44334
- function listArtifacts(projectPath) {
44335
- const artifactsDir = getLocusPath(projectPath, "artifactsDir");
44336
- if (!existsSync17(artifactsDir)) {
44337
- return [];
44338
- }
44339
- const files = readdirSync5(artifactsDir).filter((f) => f.endsWith(".md"));
44340
- return files.map((fileName) => {
44341
- const filePath = join17(artifactsDir, fileName);
44342
- const stat = statSync(filePath);
44343
- const name = fileName.replace(/\.md$/, "");
44344
- return {
44345
- name,
44346
- fileName,
44347
- createdAt: stat.birthtime,
44348
- size: stat.size
44349
- };
44350
- }).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
44351
- }
44352
- function readArtifact(projectPath, name) {
44353
- const artifactsDir = getLocusPath(projectPath, "artifactsDir");
44354
- const fileName = name.endsWith(".md") ? name : `${name}.md`;
44355
- const filePath = join17(artifactsDir, fileName);
44356
- if (!existsSync17(filePath)) {
44357
- return null;
44358
- }
44359
- const stat = statSync(filePath);
44360
- const content = readFileSync14(filePath, "utf-8");
44361
- return {
44362
- content,
44363
- info: {
44364
- name: fileName.replace(/\.md$/, ""),
44365
- fileName,
44366
- createdAt: stat.birthtime,
44367
- size: stat.size
44368
- }
44369
- };
44370
- }
44371
- function formatSize(bytes) {
44372
- if (bytes < 1024)
44373
- return `${bytes}B`;
44374
- const kb = bytes / 1024;
44375
- if (kb < 1024)
44376
- return `${kb.toFixed(1)}KB`;
44377
- const mb = kb / 1024;
44378
- return `${mb.toFixed(1)}MB`;
44379
- }
44380
- function formatDate(date5) {
44381
- return date5.toLocaleDateString("en-US", {
44382
- year: "numeric",
44383
- month: "short",
44384
- day: "numeric",
44385
- hour: "2-digit",
44386
- minute: "2-digit"
44387
- });
44388
- }
44389
44229
  async function artifactsCommand(args) {
44390
44230
  const { positionals } = parseArgs2({
44391
44231
  args,
@@ -44456,27 +44296,8 @@ async function listArtifactsCommand(projectPath) {
44456
44296
  `);
44457
44297
  }
44458
44298
  async function showArtifact(projectPath, name) {
44459
- const result = readArtifact(projectPath, name);
44299
+ const result = findArtifact(projectPath, name);
44460
44300
  if (!result) {
44461
- const artifacts = listArtifacts(projectPath);
44462
- const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
44463
- if (matches.length === 1) {
44464
- const match = readArtifact(projectPath, matches[0].name);
44465
- if (match) {
44466
- printArtifact(match.info, match.content);
44467
- return;
44468
- }
44469
- }
44470
- if (matches.length > 1) {
44471
- console.error(`
44472
- ${c.error("Error:")} Multiple artifacts match "${name}":
44473
- `);
44474
- for (const m of matches) {
44475
- console.log(` ${c.cyan(m.name)}`);
44476
- }
44477
- console.log();
44478
- return;
44479
- }
44480
44301
  console.error(`
44481
44302
  ${c.error("Error:")} Artifact "${name}" not found
44482
44303
  `);
@@ -44484,6 +44305,16 @@ async function showArtifact(projectPath, name) {
44484
44305
  `);
44485
44306
  return;
44486
44307
  }
44308
+ if (!result.match) {
44309
+ console.error(`
44310
+ ${c.error("Error:")} Multiple artifacts match "${name}":
44311
+ `);
44312
+ for (const m of result.ambiguous) {
44313
+ console.log(` ${c.cyan(m.name)}`);
44314
+ }
44315
+ console.log();
44316
+ return;
44317
+ }
44487
44318
  printArtifact(result.info, result.content);
44488
44319
  }
44489
44320
  function printArtifact(info, content) {
@@ -44497,17 +44328,16 @@ function printArtifact(info, content) {
44497
44328
  console.log(content);
44498
44329
  }
44499
44330
  async function convertToPlan(projectPath, name) {
44500
- const result = readArtifact(projectPath, name);
44331
+ const result = findArtifact(projectPath, name);
44501
44332
  if (!result) {
44502
- const artifacts = listArtifacts(projectPath);
44503
- const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
44504
- if (matches.length === 1) {
44505
- const match = readArtifact(projectPath, matches[0].name);
44506
- if (match) {
44507
- await runPlanConversion(match.info.name);
44508
- return;
44509
- }
44510
- }
44333
+ console.error(`
44334
+ ${c.error("Error:")} Artifact "${name}" not found
44335
+ `);
44336
+ console.log(` ${c.dim("Use 'locus artifacts' to see available artifacts")}
44337
+ `);
44338
+ return;
44339
+ }
44340
+ if (!result.match) {
44511
44341
  console.error(`
44512
44342
  ${c.error("Error:")} Artifact "${name}" not found
44513
44343
  `);
@@ -44526,8 +44356,8 @@ async function runPlanConversion(artifactName) {
44526
44356
  await planCommand2([directive]);
44527
44357
  }
44528
44358
  // src/commands/config.ts
44359
+ init_src3();
44529
44360
  init_index_node();
44530
- init_settings_manager();
44531
44361
  import { createInterface } from "node:readline";
44532
44362
  function ask(question) {
44533
44363
  const rl = createInterface({
@@ -44554,11 +44384,6 @@ var TELEGRAM_KEYS = [
44554
44384
  "telegram.testMode"
44555
44385
  ];
44556
44386
  var ALL_KEYS = [...TOP_LEVEL_KEYS, ...TELEGRAM_KEYS];
44557
- function maskSecret(value) {
44558
- if (value.length <= 8)
44559
- return "****";
44560
- return `${value.slice(0, 4)}...${value.slice(-4)}`;
44561
- }
44562
44387
  function showConfigHelp() {
44563
44388
  console.log(`
44564
44389
  ${c.header(" CONFIG ")}
@@ -44783,539 +44608,12 @@ async function configCommand(args) {
44783
44608
  showConfigHelp();
44784
44609
  }
44785
44610
  }
44786
- // src/commands/daemon.ts
44787
- init_index_node();
44788
- init_settings_manager();
44789
- init_utils3();
44790
- import { existsSync as existsSync19, mkdirSync as mkdirSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync8 } from "node:fs";
44791
- import { homedir as homedir3 } from "node:os";
44792
- import { join as join19 } from "node:path";
44793
-
44794
- // src/utils/process.ts
44795
- import { spawn as spawn4 } from "node:child_process";
44796
- import { existsSync as existsSync18, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "node:fs";
44797
- import { homedir as homedir2 } from "node:os";
44798
- import { dirname as dirname4, join as join18 } from "node:path";
44799
- function runShell(cmd, args) {
44800
- return new Promise((resolve2) => {
44801
- const proc = spawn4(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
44802
- let stdout = "";
44803
- let stderr = "";
44804
- proc.stdout?.on("data", (d) => {
44805
- stdout += d.toString();
44806
- });
44807
- proc.stderr?.on("data", (d) => {
44808
- stderr += d.toString();
44809
- });
44810
- proc.on("close", (exitCode) => resolve2({ exitCode, stdout, stderr }));
44811
- proc.on("error", (err) => resolve2({ exitCode: 1, stdout, stderr: err.message }));
44812
- });
44813
- }
44814
- async function findTelegramBinary() {
44815
- const result = await runShell("which", ["locus-telegram"]);
44816
- const p = result.stdout.trim();
44817
- return p?.startsWith?.("/") ? p : null;
44818
- }
44819
- async function findBinDir(binary) {
44820
- const result = await runShell("which", [binary]);
44821
- const p = result.stdout.trim();
44822
- if (p?.startsWith?.("/"))
44823
- return dirname4(p);
44824
- return null;
44825
- }
44826
- function resolveNvmBinDir() {
44827
- const nvmDir = process.env.NVM_DIR || join18(homedir2(), ".nvm");
44828
- const versionsDir = join18(nvmDir, "versions", "node");
44829
- if (!existsSync18(versionsDir))
44830
- return null;
44831
- let versions2;
44832
- try {
44833
- versions2 = readdirSync6(versionsDir).filter((d) => d.startsWith("v"));
44834
- } catch {
44835
- return null;
44836
- }
44837
- if (versions2.length === 0)
44838
- return null;
44839
- const currentNodeVersion = `v${process.versions.node}`;
44840
- const currentBin = join18(versionsDir, currentNodeVersion, "bin");
44841
- if (versions2.includes(currentNodeVersion) && existsSync18(currentBin)) {
44842
- return currentBin;
44843
- }
44844
- const aliasPath = join18(nvmDir, "alias", "default");
44845
- if (existsSync18(aliasPath)) {
44846
- try {
44847
- const alias = readFileSync15(aliasPath, "utf-8").trim();
44848
- const match = versions2.find((v) => v === `v${alias}` || v.startsWith(`v${alias}.`));
44849
- if (match) {
44850
- const bin2 = join18(versionsDir, match, "bin");
44851
- if (existsSync18(bin2))
44852
- return bin2;
44853
- }
44854
- } catch {}
44855
- }
44856
- const sorted = versions2.sort((a, b) => {
44857
- const pa = a.slice(1).split(".").map(Number);
44858
- const pb = b.slice(1).split(".").map(Number);
44859
- for (let i = 0;i < 3; i++) {
44860
- if ((pa[i] || 0) !== (pb[i] || 0))
44861
- return (pb[i] || 0) - (pa[i] || 0);
44862
- }
44863
- return 0;
44864
- });
44865
- const bin = join18(versionsDir, sorted[0], "bin");
44866
- return existsSync18(bin) ? bin : null;
44867
- }
44868
- async function buildServicePath() {
44869
- const home = homedir2();
44870
- const dirs = new Set;
44871
- dirs.add("/usr/local/bin");
44872
- dirs.add("/usr/bin");
44873
- dirs.add("/bin");
44874
- const candidates = [
44875
- join18(home, ".bun", "bin"),
44876
- join18(home, ".local", "bin"),
44877
- join18(home, ".npm", "bin"),
44878
- join18(home, ".npm-global", "bin"),
44879
- join18(home, ".yarn", "bin")
44880
- ];
44881
- for (const d of candidates) {
44882
- if (existsSync18(d))
44883
- dirs.add(d);
44884
- }
44885
- const nvmBin = resolveNvmBinDir();
44886
- if (nvmBin)
44887
- dirs.add(nvmBin);
44888
- const fnmCurrent = join18(home, ".fnm", "current", "bin");
44889
- if (existsSync18(fnmCurrent))
44890
- dirs.add(fnmCurrent);
44891
- for (const bin of ["claude", "codex"]) {
44892
- const dir = await findBinDir(bin);
44893
- if (dir)
44894
- dirs.add(dir);
44895
- }
44896
- return Array.from(dirs).join(":");
44897
- }
44898
- var SERVICE_NAME = "locus";
44899
- var SYSTEMD_UNIT_PATH = `/etc/systemd/system/${SERVICE_NAME}.service`;
44900
- var PLIST_LABEL = "com.locus.agent";
44901
- function getPlistPath() {
44902
- return join18(homedir2(), "Library/LaunchAgents", `${PLIST_LABEL}.plist`);
44903
- }
44904
- function getPlatform() {
44905
- if (process.platform === "linux")
44906
- return "linux";
44907
- if (process.platform === "darwin")
44908
- return "darwin";
44909
- return null;
44910
- }
44911
- async function killOrphanedProcesses() {
44912
- const result = await runShell("pgrep", ["-f", "locus-telegram"]);
44913
- const pids = result.stdout.trim().split(`
44914
- `).filter((p) => p.length > 0);
44915
- if (pids.length === 0)
44916
- return;
44917
- console.log(` Killing ${pids.length} orphaned locus-telegram process${pids.length > 1 ? "es" : ""}...`);
44918
- await runShell("pkill", ["-f", "locus-telegram"]);
44919
- await new Promise((resolve2) => setTimeout(resolve2, 2000));
44920
- const check2 = await runShell("pgrep", ["-f", "locus-telegram"]);
44921
- if (check2.stdout.trim().length > 0) {
44922
- await runShell("pkill", ["-9", "-f", "locus-telegram"]);
44923
- }
44924
- }
44925
- async function isDaemonRunning() {
44926
- const platform = getPlatform();
44927
- if (platform === "linux") {
44928
- const result = await runShell("systemctl", ["is-active", SERVICE_NAME]);
44929
- return result.stdout.trim() === "active";
44930
- }
44931
- if (platform === "darwin") {
44932
- const plistPath = getPlistPath();
44933
- if (!existsSync18(plistPath))
44934
- return false;
44935
- const result = await runShell("launchctl", ["list"]);
44936
- const match = result.stdout.split(`
44937
- `).find((l) => l.includes(PLIST_LABEL));
44938
- if (!match)
44939
- return false;
44940
- const pid = match.trim().split(/\s+/)[0];
44941
- return pid !== "-";
44942
- }
44943
- return false;
44944
- }
44945
- async function restartDaemonIfRunning() {
44946
- const platform = getPlatform();
44947
- if (!platform)
44948
- return false;
44949
- const running = await isDaemonRunning();
44950
- if (!running)
44951
- return false;
44952
- if (platform === "linux") {
44953
- const result = await runShell("systemctl", ["restart", SERVICE_NAME]);
44954
- return result.exitCode === 0;
44955
- }
44956
- if (platform === "darwin") {
44957
- const plistPath = getPlistPath();
44958
- await runShell("launchctl", ["unload", plistPath]);
44959
- const result = await runShell("launchctl", ["load", plistPath]);
44960
- return result.exitCode === 0;
44961
- }
44962
- return false;
44963
- }
44964
-
44965
- // src/commands/daemon.ts
44966
- function showDaemonHelp() {
44967
- console.log(`
44968
- ${c.header(" DAEMON ")}
44969
- ${c.primary("locus daemon")} ${c.dim("<subcommand>")}
44970
-
44971
- ${c.header(" SUBCOMMANDS ")}
44972
- ${c.success("start")} Install and start Locus as a background service
44973
- ${c.dim("Sets up systemd (Linux) or launchd (macOS)")}
44974
- ${c.success("stop")} Stop and remove the background service
44975
- ${c.success("restart")} Restart the background service
44976
- ${c.success("status")} Check if the service is running
44977
-
44978
- ${c.header(" EXAMPLES ")}
44979
- ${c.dim("$")} ${c.primary("locus daemon start")}
44980
- ${c.dim("$")} ${c.primary("locus daemon status")}
44981
- ${c.dim("$")} ${c.primary("locus daemon restart")}
44982
- ${c.dim("$")} ${c.primary("locus daemon stop")}
44983
- `);
44984
- }
44985
- async function resolveBinaries() {
44986
- const binaryPath = await findTelegramBinary();
44987
- if (!binaryPath) {
44988
- console.error(`
44989
- ${c.error("✖")} ${c.bold("Could not find locus-telegram binary.")}
44990
- ` + ` Install with: ${c.primary("npm install -g @locusai/telegram")}
44991
- `);
44992
- process.exit(1);
44993
- }
44994
- for (const bin of ["claude", "codex"]) {
44995
- if (!await findBinDir(bin)) {
44996
- console.warn(`
44997
- ${c.secondary("⚠")} ${c.bold(`Could not find '${bin}' CLI in PATH.`)}
44998
- ` + ` The service may need it to execute tasks.
44999
- `);
45000
- }
45001
- }
45002
- const servicePath = await buildServicePath();
45003
- return { binaryPath, servicePath };
45004
- }
45005
- function requirePlatform() {
45006
- const platform = getPlatform();
45007
- if (!platform) {
45008
- console.error(`
45009
- ${c.error("✖")} ${c.bold(`Unsupported platform: ${process.platform}`)}
45010
- ` + ` Daemon management is supported on Linux (systemd) and macOS (launchd).
45011
- `);
45012
- process.exit(1);
45013
- }
45014
- return platform;
45015
- }
45016
- function validateConfig(projectPath) {
45017
- const manager = new SettingsManager(projectPath);
45018
- const settings = manager.load();
45019
- if (!settings.telegram?.botToken || !settings.telegram?.chatId) {
45020
- console.error(`
45021
- ${c.error("✖")} ${c.bold("Telegram is not configured.")}
45022
- ` + ` Run ${c.primary("locus telegram setup")} first.
45023
- `);
45024
- process.exit(1);
45025
- }
45026
- if (!settings.apiKey) {
45027
- console.error(`
45028
- ${c.error("✖")} ${c.bold("API key is not configured.")}
45029
- ` + ` Run ${c.primary("locus config setup --api-key <key>")} first.
45030
- `);
45031
- process.exit(1);
45032
- }
45033
- }
45034
- function generateSystemdUnit(projectPath, user2, bins) {
45035
- return `[Unit]
45036
- Description=Locus AI Agent (Telegram bot + proposal scheduler)
45037
- After=network-online.target
45038
- Wants=network-online.target
45039
-
45040
- [Service]
45041
- Type=simple
45042
- User=${user2}
45043
- WorkingDirectory=${projectPath}
45044
- ExecStart=${bins.binaryPath}
45045
- Restart=on-failure
45046
- RestartSec=10
45047
- Environment=PATH=${bins.servicePath}
45048
- Environment=HOME=${homedir3()}
45049
-
45050
- [Install]
45051
- WantedBy=multi-user.target
45052
- `;
45053
- }
45054
- async function startSystemd(projectPath, bins) {
45055
- const user2 = process.env.USER || "root";
45056
- const unit = generateSystemdUnit(projectPath, user2, bins);
45057
- console.log(`
45058
- ${c.info("▶")} Writing systemd unit to ${c.dim(SYSTEMD_UNIT_PATH)}`);
45059
- writeFileSync8(SYSTEMD_UNIT_PATH, unit, "utf-8");
45060
- console.log(` ${c.info("▶")} Reloading systemd daemon...`);
45061
- await runShell("systemctl", ["daemon-reload"]);
45062
- console.log(` ${c.info("▶")} Enabling and starting ${SERVICE_NAME}...`);
45063
- await runShell("systemctl", ["enable", SERVICE_NAME]);
45064
- const result = await runShell("systemctl", ["start", SERVICE_NAME]);
45065
- if (result.exitCode !== 0) {
45066
- console.error(`
45067
- ${c.error("✖")} Failed to start service: ${result.stderr.trim()}`);
45068
- console.error(` ${c.dim("Check logs with:")} ${c.primary(`journalctl -u ${SERVICE_NAME} -f`)}`);
45069
- return;
45070
- }
45071
- console.log(`
45072
- ${c.success("✔")} ${c.bold("Locus daemon started!")}
45073
-
45074
- ${c.bold("Service:")} ${SERVICE_NAME}
45075
- ${c.bold("Unit file:")} ${SYSTEMD_UNIT_PATH}
45076
-
45077
- ${c.bold("Useful commands:")}
45078
- ${c.dim("$")} ${c.primary(`sudo systemctl status ${SERVICE_NAME}`)}
45079
- ${c.dim("$")} ${c.primary(`journalctl -u ${SERVICE_NAME} -f`)}
45080
- `);
45081
- }
45082
- async function stopSystemd() {
45083
- if (!existsSync19(SYSTEMD_UNIT_PATH)) {
45084
- console.log(`
45085
- ${c.dim("No systemd service found. Nothing to stop.")}
45086
- `);
45087
- await killOrphanedProcesses();
45088
- return;
45089
- }
45090
- console.log(` ${c.info("▶")} Stopping and disabling ${SERVICE_NAME}...`);
45091
- await runShell("systemctl", ["stop", SERVICE_NAME]);
45092
- await runShell("systemctl", ["disable", SERVICE_NAME]);
45093
- unlinkSync6(SYSTEMD_UNIT_PATH);
45094
- await runShell("systemctl", ["daemon-reload"]);
45095
- await killOrphanedProcesses();
45096
- console.log(`
45097
- ${c.success("✔")} ${c.bold("Locus daemon stopped.")}
45098
- `);
45099
- }
45100
- async function restartSystemd() {
45101
- if (!existsSync19(SYSTEMD_UNIT_PATH)) {
45102
- console.log(`
45103
- ${c.dim("No systemd service found. Use")} ${c.primary("locus daemon start")} ${c.dim("first.")}
45104
- `);
45105
- return;
45106
- }
45107
- console.log(` ${c.info("▶")} Restarting ${SERVICE_NAME}...`);
45108
- const result = await runShell("systemctl", ["restart", SERVICE_NAME]);
45109
- if (result.exitCode !== 0) {
45110
- console.error(`
45111
- ${c.error("✖")} Failed to restart: ${result.stderr.trim()}
45112
- `);
45113
- return;
45114
- }
45115
- console.log(`
45116
- ${c.success("✔")} ${c.bold("Locus daemon restarted.")}
45117
- `);
45118
- }
45119
- async function statusSystemd() {
45120
- const result = await runShell("systemctl", ["is-active", SERVICE_NAME]);
45121
- const state = result.stdout.trim();
45122
- if (state === "active") {
45123
- console.log(`
45124
- ${c.success("●")} ${c.bold("Locus daemon is running")} ${c.dim("(systemd)")}
45125
- `);
45126
- } else if (existsSync19(SYSTEMD_UNIT_PATH)) {
45127
- console.log(`
45128
- ${c.secondary("●")} ${c.bold(`Locus daemon is ${state}`)} ${c.dim("(systemd)")}
45129
- `);
45130
- console.log(` ${c.dim("Start with:")} ${c.primary("locus daemon start")}
45131
- `);
45132
- } else {
45133
- console.log(`
45134
- ${c.secondary("●")} ${c.bold("Locus daemon is not installed")}
45135
- `);
45136
- console.log(` ${c.dim("Start with:")} ${c.primary("locus daemon start")}
45137
- `);
45138
- }
45139
- }
45140
- function generatePlist(projectPath, bins) {
45141
- const logDir = join19(homedir3(), "Library/Logs/Locus");
45142
- return `<?xml version="1.0" encoding="UTF-8"?>
45143
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
45144
- <plist version="1.0">
45145
- <dict>
45146
- <key>Label</key>
45147
- <string>${PLIST_LABEL}</string>
45148
- <key>ProgramArguments</key>
45149
- <array>
45150
- <string>${bins.binaryPath}</string>
45151
- </array>
45152
- <key>WorkingDirectory</key>
45153
- <string>${projectPath}</string>
45154
- <key>RunAtLoad</key>
45155
- <true/>
45156
- <key>KeepAlive</key>
45157
- <true/>
45158
- <key>StandardOutPath</key>
45159
- <string>${join19(logDir, "locus.log")}</string>
45160
- <key>StandardErrorPath</key>
45161
- <string>${join19(logDir, "locus-error.log")}</string>
45162
- <key>EnvironmentVariables</key>
45163
- <dict>
45164
- <key>PATH</key>
45165
- <string>${bins.servicePath}</string>
45166
- </dict>
45167
- </dict>
45168
- </plist>
45169
- `;
45170
- }
45171
- async function startLaunchd(projectPath, bins) {
45172
- const plistPath = getPlistPath();
45173
- if (existsSync19(plistPath)) {
45174
- await runShell("launchctl", ["unload", plistPath]);
45175
- }
45176
- const logDir = join19(homedir3(), "Library/Logs/Locus");
45177
- mkdirSync8(logDir, { recursive: true });
45178
- mkdirSync8(join19(homedir3(), "Library/LaunchAgents"), { recursive: true });
45179
- const plist = generatePlist(projectPath, bins);
45180
- console.log(`
45181
- ${c.info("▶")} Writing plist to ${c.dim(plistPath)}`);
45182
- writeFileSync8(plistPath, plist, "utf-8");
45183
- console.log(` ${c.info("▶")} Loading service...`);
45184
- const result = await runShell("launchctl", ["load", plistPath]);
45185
- if (result.exitCode !== 0) {
45186
- console.error(`
45187
- ${c.error("✖")} Failed to load service: ${result.stderr.trim()}`);
45188
- return;
45189
- }
45190
- const logPath = join19(logDir, "locus.log");
45191
- console.log(`
45192
- ${c.success("✔")} ${c.bold("Locus daemon started!")}
45193
-
45194
- ${c.bold("Plist:")} ${plistPath}
45195
- ${c.bold("Logs:")} ${logPath}
45196
-
45197
- ${c.bold("Useful commands:")}
45198
- ${c.dim("$")} ${c.primary(`launchctl list | grep ${PLIST_LABEL}`)}
45199
- ${c.dim("$")} ${c.primary(`tail -f ${logPath}`)}
45200
- `);
45201
- }
45202
- async function stopLaunchd() {
45203
- const plistPath = getPlistPath();
45204
- if (!existsSync19(plistPath)) {
45205
- console.log(`
45206
- ${c.dim("No launchd service found. Nothing to stop.")}
45207
- `);
45208
- await killOrphanedProcesses();
45209
- return;
45210
- }
45211
- console.log(` ${c.info("▶")} Unloading service...`);
45212
- await runShell("launchctl", ["unload", plistPath]);
45213
- unlinkSync6(plistPath);
45214
- await killOrphanedProcesses();
45215
- console.log(`
45216
- ${c.success("✔")} ${c.bold("Locus daemon stopped.")}
45217
- `);
45218
- }
45219
- async function restartLaunchd() {
45220
- const plistPath = getPlistPath();
45221
- if (!existsSync19(plistPath)) {
45222
- console.log(`
45223
- ${c.dim("No launchd service found. Use")} ${c.primary("locus daemon start")} ${c.dim("first.")}
45224
- `);
45225
- return;
45226
- }
45227
- console.log(` ${c.info("▶")} Restarting service...`);
45228
- await runShell("launchctl", ["unload", plistPath]);
45229
- const result = await runShell("launchctl", ["load", plistPath]);
45230
- if (result.exitCode !== 0) {
45231
- console.error(`
45232
- ${c.error("✖")} Failed to restart: ${result.stderr.trim()}
45233
- `);
45234
- return;
45235
- }
45236
- console.log(`
45237
- ${c.success("✔")} ${c.bold("Locus daemon restarted.")}
45238
- `);
45239
- }
45240
- async function statusLaunchd() {
45241
- const plistPath = getPlistPath();
45242
- if (!existsSync19(plistPath)) {
45243
- console.log(`
45244
- ${c.secondary("●")} ${c.bold("Locus daemon is not installed")}
45245
- `);
45246
- console.log(` ${c.dim("Start with:")} ${c.primary("locus daemon start")}
45247
- `);
45248
- return;
45249
- }
45250
- const result = await runShell("launchctl", ["list"]);
45251
- const match = result.stdout.split(`
45252
- `).find((l) => l.includes(PLIST_LABEL));
45253
- if (match) {
45254
- const parts = match.trim().split(/\s+/);
45255
- const pid = parts[0] === "-" ? null : parts[0];
45256
- if (pid) {
45257
- console.log(`
45258
- ${c.success("●")} ${c.bold("Locus daemon is running")} ${c.dim(`(PID ${pid}, launchd)`)}
45259
- `);
45260
- } else {
45261
- console.log(`
45262
- ${c.secondary("●")} ${c.bold("Locus daemon is stopped")} ${c.dim("(launchd)")}
45263
- `);
45264
- console.log(` ${c.dim("Start with:")} ${c.primary("locus daemon start")}
45265
- `);
45266
- }
45267
- } else {
45268
- console.log(`
45269
- ${c.secondary("●")} ${c.bold("Locus daemon is not loaded")} ${c.dim("(plist exists but not loaded)")}
45270
- `);
45271
- console.log(` ${c.dim("Start with:")} ${c.primary("locus daemon start")}
45272
- `);
45273
- }
45274
- }
45275
- async function daemonCommand(args) {
45276
- const projectPath = process.cwd();
45277
- requireInitialization(projectPath, "daemon");
45278
- const subcommand = args[0];
45279
- const platform = subcommand ? requirePlatform() : null;
45280
- const isLinux = platform === "linux";
45281
- switch (subcommand) {
45282
- case "start": {
45283
- validateConfig(projectPath);
45284
- const bins = await resolveBinaries();
45285
- if (isLinux)
45286
- await startSystemd(projectPath, bins);
45287
- else
45288
- await startLaunchd(projectPath, bins);
45289
- break;
45290
- }
45291
- case "stop":
45292
- if (isLinux)
45293
- await stopSystemd();
45294
- else
45295
- await stopLaunchd();
45296
- break;
45297
- case "restart":
45298
- if (isLinux)
45299
- await restartSystemd();
45300
- else
45301
- await restartLaunchd();
45302
- break;
45303
- case "status":
45304
- if (isLinux)
45305
- await statusSystemd();
45306
- else
45307
- await statusLaunchd();
45308
- break;
45309
- default:
45310
- showDaemonHelp();
45311
- }
45312
- }
45313
44611
  // src/commands/discuss.ts
44612
+ init_src3();
45314
44613
  init_index_node();
45315
44614
  init_progress_renderer();
45316
44615
  init_image_detect();
45317
44616
  init_input_handler();
45318
- init_settings_manager();
45319
44617
  init_utils3();
45320
44618
  import { parseArgs as parseArgs3 } from "node:util";
45321
44619
  async function discussCommand(args) {
@@ -45338,35 +44636,66 @@ async function discussCommand(args) {
45338
44636
  requireInitialization(projectPath, "discuss");
45339
44637
  const discussionManager = new DiscussionManager(projectPath);
45340
44638
  if (values.list) {
45341
- return listDiscussions(discussionManager);
44639
+ return renderDiscussionList(discussionManager);
45342
44640
  }
45343
44641
  if (values.show) {
45344
- return showDiscussion(discussionManager, values.show);
44642
+ const md = showDiscussion(discussionManager, values.show);
44643
+ if (!md) {
44644
+ console.error(`
44645
+ ${c.error("✖")} ${c.red(`Discussion not found: ${values.show}`)}
44646
+ `);
44647
+ process.exit(1);
44648
+ }
44649
+ console.log(`
44650
+ ${md}
44651
+ `);
44652
+ return;
45345
44653
  }
45346
44654
  if (values.archive) {
45347
- return archiveDiscussion(discussionManager, values.archive);
44655
+ try {
44656
+ archiveDiscussion(discussionManager, values.archive);
44657
+ console.log(`
44658
+ ${c.success("✔")} ${c.dim("Discussion archived.")}
44659
+ `);
44660
+ } catch (error48) {
44661
+ console.error(`
44662
+ ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
44663
+ `);
44664
+ process.exit(1);
44665
+ }
44666
+ return;
45348
44667
  }
45349
44668
  if (values.delete) {
45350
- return deleteDiscussion(discussionManager, values.delete);
44669
+ try {
44670
+ deleteDiscussion(discussionManager, values.delete);
44671
+ console.log(`
44672
+ ${c.success("✔")} ${c.dim("Discussion deleted.")}
44673
+ `);
44674
+ } catch (error48) {
44675
+ console.error(`
44676
+ ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
44677
+ `);
44678
+ process.exit(1);
44679
+ }
44680
+ return;
45351
44681
  }
45352
44682
  const topic = positionals.join(" ").trim();
45353
44683
  if (!topic) {
45354
44684
  showDiscussHelp();
45355
44685
  return;
45356
44686
  }
45357
- const settings = new SettingsManager(projectPath).load();
45358
- const provider = resolveProvider3(values.provider || settings.provider);
45359
- const model = values.model || settings.model || DEFAULT_MODEL[provider];
44687
+ const { provider, model } = resolveAiSettings({
44688
+ projectPath,
44689
+ provider: values.provider,
44690
+ model: values.model
44691
+ });
45360
44692
  const reasoningEffort = values["reasoning-effort"];
45361
44693
  const aiRunner = createAiRunner(provider, {
45362
44694
  projectPath,
45363
44695
  model,
45364
44696
  reasoningEffort
45365
44697
  });
45366
- const log = (message, level) => {
45367
- const icon = level === "success" ? c.success("✔") : level === "error" ? c.error("✖") : level === "warn" ? c.warning("!") : c.info("●");
45368
- console.log(` ${icon} ${message}`);
45369
- };
44698
+ const log = createCliLogger();
45370
44699
  const facilitator = new DiscussionFacilitator({
45371
44700
  projectPath,
45372
44701
  aiRunner,
@@ -45381,7 +44710,7 @@ async function discussCommand(args) {
45381
44710
  console.log(` ${c.dim("Topic:")} ${c.bold(topic)}`);
45382
44711
  console.log(` ${c.dim("Model:")} ${c.dim(`${model} (${provider})`)}
45383
44712
  `);
45384
- const renderer = new ProgressRenderer;
44713
+ const renderer = new ProgressRenderer({ animated: true });
45385
44714
  let discussionId;
45386
44715
  try {
45387
44716
  renderer.showThinkingStarted();
@@ -45445,7 +44774,7 @@ async function discussCommand(args) {
45445
44774
  }
45446
44775
  if (lowerInput === "summary") {
45447
44776
  isProcessing = true;
45448
- const summaryRenderer = new ProgressRenderer;
44777
+ const summaryRenderer = new ProgressRenderer({ animated: true });
45449
44778
  try {
45450
44779
  summaryRenderer.showThinkingStarted();
45451
44780
  const summary = await facilitator.summarizeDiscussion(discussionId);
@@ -45489,7 +44818,7 @@ async function discussCommand(args) {
45489
44818
  const cleanedInput = stripImagePaths(trimmed, images);
45490
44819
  const effectiveInput = cleanedInput + buildImageContext(images);
45491
44820
  isProcessing = true;
45492
- const chunkRenderer = new ProgressRenderer;
44821
+ const chunkRenderer = new ProgressRenderer({ animated: true });
45493
44822
  try {
45494
44823
  chunkRenderer.showThinkingStarted();
45495
44824
  const stream4 = facilitator.continueDiscussionStream(discussionId, effectiveInput);
@@ -45552,8 +44881,8 @@ async function discussCommand(args) {
45552
44881
  inputHandler.start();
45553
44882
  inputHandler.showPrompt();
45554
44883
  }
45555
- function listDiscussions(discussionManager) {
45556
- const discussions = discussionManager.list();
44884
+ function renderDiscussionList(discussionManager) {
44885
+ const discussions = listDiscussions(discussionManager);
45557
44886
  if (discussions.length === 0) {
45558
44887
  console.log(`
45559
44888
  ${c.dim("No discussions found.")}
@@ -45567,50 +44896,12 @@ function listDiscussions(discussionManager) {
45567
44896
  `);
45568
44897
  for (const disc of discussions) {
45569
44898
  const statusIcon = disc.status === "active" ? c.warning("◯") : disc.status === "completed" ? c.success("✔") : c.dim("⊘");
45570
- console.log(` ${statusIcon} ${c.bold(disc.title)} ${c.dim(`[${disc.status}]`)} ${c.dim(`— ${disc.messages.length} messages, ${disc.insights.length} insights`)}`);
44899
+ console.log(` ${statusIcon} ${c.bold(disc.title)} ${c.dim(`[${disc.status}]`)} ${c.dim(`— ${disc.messageCount} messages, ${disc.insightCount} insights`)}`);
45571
44900
  console.log(` ${c.dim("ID:")} ${disc.id}`);
45572
44901
  console.log(` ${c.dim("Created:")} ${disc.createdAt}`);
45573
44902
  console.log("");
45574
44903
  }
45575
44904
  }
45576
- function showDiscussion(discussionManager, id) {
45577
- const md = discussionManager.getMarkdown(id);
45578
- if (!md) {
45579
- console.error(`
45580
- ${c.error("✖")} ${c.red(`Discussion not found: ${id}`)}
45581
- `);
45582
- process.exit(1);
45583
- }
45584
- console.log(`
45585
- ${md}
45586
- `);
45587
- }
45588
- function archiveDiscussion(discussionManager, id) {
45589
- try {
45590
- discussionManager.archive(id);
45591
- console.log(`
45592
- ${c.success("✔")} ${c.dim("Discussion archived.")}
45593
- `);
45594
- } catch (error48) {
45595
- console.error(`
45596
- ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
45597
- `);
45598
- process.exit(1);
45599
- }
45600
- }
45601
- function deleteDiscussion(discussionManager, id) {
45602
- try {
45603
- discussionManager.delete(id);
45604
- console.log(`
45605
- ${c.success("✔")} ${c.dim("Discussion deleted.")}
45606
- `);
45607
- } catch (error48) {
45608
- console.error(`
45609
- ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
45610
- `);
45611
- process.exit(1);
45612
- }
45613
- }
45614
44905
  function showCurrentInsights(discussionManager, discussionId) {
45615
44906
  const discussion2 = discussionManager.load(discussionId);
45616
44907
  if (!discussion2 || discussion2.insights.length === 0) {
@@ -45701,11 +44992,10 @@ function showDiscussHelp() {
45701
44992
  `);
45702
44993
  }
45703
44994
  // src/commands/docs.ts
44995
+ init_src3();
45704
44996
  init_index_node();
45705
44997
  init_config_manager();
45706
- init_settings_manager();
45707
44998
  init_utils3();
45708
- init_workspace_resolver();
45709
44999
  import { parseArgs as parseArgs4 } from "node:util";
45710
45000
  async function docsCommand(args) {
45711
45001
  const subcommand = args[0];
@@ -45739,55 +45029,25 @@ async function docsSyncCommand(args) {
45739
45029
  requireInitialization(projectPath, "docs sync");
45740
45030
  const configManager = new ConfigManager(projectPath);
45741
45031
  configManager.updateVersion(VERSION2);
45742
- const settingsManager = new SettingsManager(projectPath);
45743
- const settings = settingsManager.load();
45744
- const apiKey = values["api-key"] || settings.apiKey;
45745
- if (!apiKey) {
45746
- console.error(`
45747
- ${c.error("✖")} ${c.red("API key is required")}
45748
- ` + ` ${c.dim(`Configure with: locus config setup --api-key <key>
45749
- Or pass --api-key flag`)}
45750
- `);
45751
- process.exit(1);
45752
- }
45753
- const apiBase = values["api-url"] || settings.apiUrl || "https://api.locusai.dev/api";
45754
- const resolver = new WorkspaceResolver({
45755
- apiKey,
45756
- apiBase,
45757
- workspaceId: values.workspace
45758
- });
45759
- let workspaceId;
45032
+ let apiContext;
45760
45033
  try {
45761
- workspaceId = await resolver.resolve();
45034
+ apiContext = await resolveApiContext({
45035
+ projectPath,
45036
+ apiKey: values["api-key"],
45037
+ apiUrl: values["api-url"],
45038
+ workspaceId: values.workspace
45039
+ });
45762
45040
  } catch (error48) {
45763
45041
  console.error(`
45764
45042
  ${c.error("✖")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}
45765
45043
  `);
45766
45044
  process.exit(1);
45767
45045
  }
45768
- const client = new LocusClient({
45769
- baseUrl: apiBase,
45770
- token: apiKey
45771
- });
45772
45046
  const fetcher = new DocumentFetcher({
45773
- client,
45774
- workspaceId,
45047
+ client: apiContext.client,
45048
+ workspaceId: apiContext.workspaceId,
45775
45049
  projectPath,
45776
- log: (message, level) => {
45777
- if (level === "error") {
45778
- console.log(` ${c.error("✖")} ${message}`);
45779
- return;
45780
- }
45781
- if (level === "warn") {
45782
- console.log(` ${c.warning("!")} ${message}`);
45783
- return;
45784
- }
45785
- if (level === "success") {
45786
- console.log(` ${c.success("✔")} ${message}`);
45787
- return;
45788
- }
45789
- console.log(` ${c.info("●")} ${message}`);
45790
- }
45050
+ log: createCliLogger()
45791
45051
  });
45792
45052
  console.log(`
45793
45053
  ${c.info("●")} ${c.bold("Syncing docs from API...")}
@@ -45966,7 +45226,11 @@ class JsonStreamRenderer {
45966
45226
 
45967
45227
  // src/commands/exec.ts
45968
45228
  init_progress_renderer();
45969
- init_settings_manager();
45229
+
45230
+ // src/settings-manager.ts
45231
+ init_src3();
45232
+
45233
+ // src/commands/exec.ts
45970
45234
  init_utils3();
45971
45235
 
45972
45236
  // src/commands/exec-sessions.ts
@@ -46178,7 +45442,7 @@ async function execCommand(args) {
46178
45442
  }
46179
45443
  }
46180
45444
  const execSettings = new SettingsManager(projectPath).load();
46181
- const provider = resolveProvider3(values.provider || execSettings.provider);
45445
+ const provider = resolveProvider4(values.provider || execSettings.provider);
46182
45446
  const model = values.model || execSettings.model || DEFAULT_MODEL[provider];
46183
45447
  const isInteractive = values.interactive;
46184
45448
  const sessionId = values.session;
@@ -46274,7 +45538,7 @@ async function execCommand(args) {
46274
45538
  async function execJsonStream(values, positionals, projectPath) {
46275
45539
  const sessionId = values["session-id"] ?? values.session ?? randomUUID2();
46276
45540
  const execSettings = new SettingsManager(projectPath).load();
46277
- const provider = resolveProvider3(values.provider || execSettings.provider);
45541
+ const provider = resolveProvider4(values.provider || execSettings.provider);
46278
45542
  const model = values.model || execSettings.model || DEFAULT_MODEL[provider];
46279
45543
  const renderer = new JsonStreamRenderer({
46280
45544
  sessionId,
@@ -46358,17 +45622,11 @@ function showHelp2() {
46358
45622
  ${c.dim("sync Sync docs from API to .locus/documents")}
46359
45623
  ${c.success("review")} Review open Locus PRs on GitHub with AI
46360
45624
  ${c.dim("local Review staged changes locally (no GitHub)")}
46361
- ${c.success("telegram")} Manage the Telegram bot
46362
- ${c.dim("start Start the Telegram bot")}
45625
+ ${c.success("telegram")} Configure the Telegram bot
46363
45626
  ${c.dim("setup Interactive bot token and chat ID setup")}
46364
45627
  ${c.dim("config Show current configuration")}
46365
45628
  ${c.dim("set <k> <v> Update a config value")}
46366
45629
  ${c.dim("remove Remove Telegram configuration")}
46367
- ${c.success("daemon")} Manage the Locus background service
46368
- ${c.dim("start Install and start the daemon")}
46369
- ${c.dim("stop Stop and remove the daemon")}
46370
- ${c.dim("restart Restart the daemon")}
46371
- ${c.dim("status Check if the daemon is running")}
46372
45630
  ${c.success("exec")} Run a prompt with repository context
46373
45631
  ${c.dim("--interactive, -i Start interactive REPL mode")}
46374
45632
  ${c.dim("--session, -s <id> Resume a previous session")}
@@ -46391,19 +45649,17 @@ function showHelp2() {
46391
45649
  ${c.header(" GETTING STARTED ")}
46392
45650
  ${c.dim("$")} ${c.primary("locus init")}
46393
45651
  ${c.dim("$")} ${c.primary("locus config setup")}
46394
- ${c.dim("$")} ${c.primary("locus telegram setup")}
46395
- ${c.dim("$")} ${c.primary("locus daemon start")}
45652
+ ${c.dim("$")} ${c.primary("locus run")}
46396
45653
 
46397
45654
  ${c.header(" EXAMPLES ")}
46398
45655
  ${c.dim("$")} ${c.primary("locus run")}
46399
45656
  ${c.dim("$")} ${c.primary("locus docs sync")}
46400
45657
  ${c.dim("$")} ${c.primary("locus review")}
46401
45658
  ${c.dim("$")} ${c.primary("locus review local")}
46402
- ${c.dim("$")} ${c.primary("locus telegram start")}
45659
+ ${c.dim("$")} ${c.primary("locus telegram setup")}
46403
45660
  ${c.dim("$")} ${c.primary('locus discuss "how should we design the auth system?"')}
46404
45661
  ${c.dim("$")} ${c.primary("locus exec sessions list")}
46405
45662
  ${c.dim("$")} ${c.primary("locus artifacts")}
46406
- ${c.dim("$")} ${c.primary("locus daemon start")}
46407
45663
 
46408
45664
  For more information, visit: ${c.underline("https://docs.locusai.dev")}
46409
45665
  `);
@@ -46452,7 +45708,7 @@ async function indexCommand(args) {
46452
45708
  const projectPath = values.dir || process.cwd();
46453
45709
  requireInitialization(projectPath, "index");
46454
45710
  new ConfigManager(projectPath).updateVersion(VERSION2);
46455
- const provider = resolveProvider3(values.provider);
45711
+ const provider = resolveProvider4(values.provider);
46456
45712
  const model = values.model || DEFAULT_MODEL[provider];
46457
45713
  const aiRunner = createAiRunner(provider, {
46458
45714
  projectPath,
@@ -46543,13 +45799,12 @@ async function initCommand() {
46543
45799
  init_plan();
46544
45800
 
46545
45801
  // src/commands/review.ts
45802
+ init_src3();
46546
45803
  init_index_node();
46547
45804
  init_config_manager();
46548
- init_settings_manager();
46549
45805
  init_utils3();
46550
- init_workspace_resolver();
46551
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
46552
- import { join as join21 } from "node:path";
45806
+ import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "node:fs";
45807
+ import { join as join20 } from "node:path";
46553
45808
  import { parseArgs as parseArgs7 } from "node:util";
46554
45809
  async function reviewCommand(args) {
46555
45810
  const subcommand = args[0];
@@ -46575,41 +45830,25 @@ async function reviewPrsCommand(args) {
46575
45830
  requireInitialization(projectPath, "review");
46576
45831
  const configManager = new ConfigManager(projectPath);
46577
45832
  configManager.updateVersion(VERSION2);
46578
- const settingsManager = new SettingsManager(projectPath);
46579
- const settings = settingsManager.load();
46580
- const apiKey = values["api-key"] || settings.apiKey;
46581
- if (!apiKey) {
46582
- console.error(c.error("Error: API key is required for PR review"));
46583
- console.error(c.dim(`Configure with: locus config setup --api-key <key>
46584
- Or pass --api-key flag`));
46585
- console.error(c.dim("For local staged-changes review, use: locus review local"));
46586
- process.exit(1);
46587
- }
46588
- const provider = resolveProvider3(values.provider || settings.provider);
46589
- const model = values.model || settings.model || DEFAULT_MODEL[provider];
46590
- const apiBase = values["api-url"] || settings.apiUrl || "https://api.locusai.dev/api";
46591
- let workspaceId;
45833
+ let apiContext;
46592
45834
  try {
46593
- const resolver = new WorkspaceResolver({
46594
- apiKey,
46595
- apiBase,
45835
+ apiContext = await resolveApiContext({
45836
+ projectPath,
45837
+ apiKey: values["api-key"],
45838
+ apiUrl: values["api-url"],
46596
45839
  workspaceId: values.workspace
46597
45840
  });
46598
- workspaceId = await resolver.resolve();
46599
45841
  } catch (error48) {
46600
45842
  console.error(c.error(error48 instanceof Error ? error48.message : String(error48)));
45843
+ console.error(c.dim("For local staged-changes review, use: locus review local"));
46601
45844
  process.exit(1);
46602
45845
  }
46603
- const log = (msg, level = "info") => {
46604
- const colorFn = {
46605
- info: c.cyan,
46606
- success: c.green,
46607
- warn: c.yellow,
46608
- error: c.red
46609
- }[level];
46610
- const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
46611
- console.log(` ${colorFn(`${prefix} ${msg}`)}`);
46612
- };
45846
+ const { provider, model } = resolveAiSettings({
45847
+ projectPath,
45848
+ provider: values.provider,
45849
+ model: values.model
45850
+ });
45851
+ const log = createCliLogger();
46613
45852
  const prService = new PrService(projectPath, log);
46614
45853
  const unreviewedPrs = prService.listUnreviewedLocusPrs();
46615
45854
  if (unreviewedPrs.length === 0) {
@@ -46624,10 +45863,10 @@ async function reviewPrsCommand(args) {
46624
45863
  const agentId = `reviewer-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
46625
45864
  const reviewer = new ReviewerWorker({
46626
45865
  agentId,
46627
- workspaceId,
46628
- apiBase,
45866
+ workspaceId: apiContext.workspaceId,
45867
+ apiBase: apiContext.apiBase,
46629
45868
  projectPath,
46630
- apiKey,
45869
+ apiKey: apiContext.apiKey,
46631
45870
  model,
46632
45871
  provider
46633
45872
  });
@@ -46656,9 +45895,11 @@ async function reviewLocalCommand(args) {
46656
45895
  });
46657
45896
  const projectPath = values.dir || process.cwd();
46658
45897
  requireInitialization(projectPath, "review local");
46659
- const localSettings = new SettingsManager(projectPath).load();
46660
- const provider = resolveProvider3(values.provider || localSettings.provider);
46661
- const model = values.model || localSettings.model || DEFAULT_MODEL[provider];
45898
+ const { provider, model } = resolveAiSettings({
45899
+ projectPath,
45900
+ provider: values.provider,
45901
+ model: values.model
45902
+ });
46662
45903
  const aiRunner = createAiRunner(provider, {
46663
45904
  projectPath,
46664
45905
  model
@@ -46666,18 +45907,7 @@ async function reviewLocalCommand(args) {
46666
45907
  const reviewService = new ReviewService({
46667
45908
  aiRunner,
46668
45909
  projectPath,
46669
- log: (msg, level) => {
46670
- switch (level) {
46671
- case "error":
46672
- console.log(` ${c.error("✖")} ${msg}`);
46673
- break;
46674
- case "success":
46675
- console.log(` ${c.success("✔")} ${msg}`);
46676
- break;
46677
- default:
46678
- console.log(` ${c.dim(msg)}`);
46679
- }
46680
- }
45910
+ log: createCliLogger()
46681
45911
  });
46682
45912
  console.log(`
46683
45913
  ${c.primary("\uD83D\uDD0D")} ${c.bold("Reviewing staged changes...")}
@@ -46688,12 +45918,12 @@ async function reviewLocalCommand(args) {
46688
45918
  `);
46689
45919
  return;
46690
45920
  }
46691
- const reviewsDir = join21(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.reviewsDir);
46692
- if (!existsSync21(reviewsDir)) {
46693
- mkdirSync10(reviewsDir, { recursive: true });
45921
+ const reviewsDir = join20(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.reviewsDir);
45922
+ if (!existsSync20(reviewsDir)) {
45923
+ mkdirSync9(reviewsDir, { recursive: true });
46694
45924
  }
46695
45925
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
46696
- const reportPath = join21(reviewsDir, `review-${timestamp}.md`);
45926
+ const reportPath = join20(reviewsDir, `review-${timestamp}.md`);
46697
45927
  writeFileSync9(reportPath, report, "utf-8");
46698
45928
  console.log(`
46699
45929
  ${c.success("✔")} ${c.success("Review complete!")}`);
@@ -46703,10 +45933,23 @@ async function reviewLocalCommand(args) {
46703
45933
  // src/commands/run.ts
46704
45934
  init_index_node();
46705
45935
  init_config_manager();
46706
- init_settings_manager();
46707
- init_utils3();
46708
- init_workspace_resolver();
46709
45936
  import { parseArgs as parseArgs8 } from "node:util";
45937
+ init_utils3();
45938
+
45939
+ // src/workspace-resolver.ts
45940
+ init_src3();
45941
+ init_index_node();
45942
+
45943
+ class WorkspaceResolver2 extends WorkspaceResolver {
45944
+ constructor(options) {
45945
+ super({
45946
+ ...options,
45947
+ log: (msg) => console.log(c.dim(`ℹ ${msg}`))
45948
+ });
45949
+ }
45950
+ }
45951
+
45952
+ // src/commands/run.ts
46710
45953
  async function runCommand(args) {
46711
45954
  const { values } = parseArgs8({
46712
45955
  args,
@@ -46737,11 +45980,11 @@ async function runCommand(args) {
46737
45980
  process.exit(1);
46738
45981
  }
46739
45982
  let workspaceId = values.workspace;
46740
- const provider = resolveProvider3(values.provider || settings.provider);
45983
+ const provider = resolveProvider4(values.provider || settings.provider);
46741
45984
  const model = values.model || settings.model || DEFAULT_MODEL[provider];
46742
45985
  const apiBase = values["api-url"] || settings.apiUrl || "https://api.locusai.dev/api";
46743
45986
  try {
46744
- const resolver = new WorkspaceResolver({
45987
+ const resolver = new WorkspaceResolver2({
46745
45988
  apiKey,
46746
45989
  apiBase,
46747
45990
  workspaceId: values.workspace
@@ -46787,30 +46030,11 @@ ${c.info(`Received ${signal}. Stopping agent and cleaning up...`)}`);
46787
46030
  console.log(` ${c.dim("A PR will be opened when all tasks are done")}`);
46788
46031
  await orchestrator.start();
46789
46032
  }
46790
- // src/commands/service.ts
46791
- init_index_node();
46792
- async function serviceCommand(args) {
46793
- const subcommand = args[0];
46794
- const mapping = {
46795
- install: "start",
46796
- uninstall: "stop",
46797
- status: "status"
46798
- };
46799
- const mapped = subcommand ? mapping[subcommand] : undefined;
46800
- if (mapped) {
46801
- console.log(` ${c.dim(`Hint: 'locus service ${subcommand}' is now 'locus daemon ${mapped}'`)}
46802
- `);
46803
- await daemonCommand([mapped, ...args.slice(1)]);
46804
- } else {
46805
- await daemonCommand(args);
46806
- }
46807
- }
46808
46033
  // src/commands/telegram.ts
46809
46034
  init_index_node();
46810
- init_settings_manager();
46811
- import { spawn as spawn5 } from "node:child_process";
46812
- import { existsSync as existsSync22 } from "node:fs";
46813
- import { join as join22 } from "node:path";
46035
+ import { spawn as spawn4 } from "node:child_process";
46036
+ import { existsSync as existsSync21 } from "node:fs";
46037
+ import { join as join21 } from "node:path";
46814
46038
  import { createInterface as createInterface2 } from "node:readline";
46815
46039
  function ask2(question) {
46816
46040
  const rl = createInterface2({
@@ -46836,7 +46060,7 @@ function showTelegramHelp() {
46836
46060
  ${c.primary("locus telegram")} ${c.dim("<subcommand> [options]")}
46837
46061
 
46838
46062
  ${c.header(" SUBCOMMANDS ")}
46839
- ${c.success("start")} Start the Telegram bot
46063
+ ${c.success("run")} Start the Telegram bot
46840
46064
  ${c.success("setup")} Interactive Telegram bot setup (or pass flags below)
46841
46065
  ${c.success("config")} Show current Telegram configuration
46842
46066
  ${c.success("set")} Set a config value
@@ -46845,7 +46069,7 @@ function showTelegramHelp() {
46845
46069
  ${c.success("remove")} Remove Telegram configuration
46846
46070
 
46847
46071
  ${c.header(" EXAMPLES ")}
46848
- ${c.dim("$")} ${c.primary("locus telegram start")}
46072
+ ${c.dim("$")} ${c.primary("locus telegram run")}
46849
46073
  ${c.dim("$")} ${c.primary('locus telegram setup --token "123:ABC" --chat-id 987654')}
46850
46074
  ${c.dim("$")} ${c.primary("locus telegram config")}
46851
46075
  ${c.dim("$")} ${c.primary("locus telegram remove")}
@@ -46856,7 +46080,7 @@ function showTelegramHelp() {
46856
46080
  ${c.primary("locus config set <key> <value>")}
46857
46081
  `);
46858
46082
  }
46859
- async function setup(args, projectPath) {
46083
+ async function setupCommand2(args, projectPath) {
46860
46084
  let token;
46861
46085
  let chatId;
46862
46086
  for (let i = 0;i < args.length; i++) {
@@ -46928,11 +46152,10 @@ async function setup(args, projectPath) {
46928
46152
  ${c.primary("Chat ID:")} ${parsedChatId}
46929
46153
 
46930
46154
  ${c.bold("Next steps:")}
46931
- Start as daemon: ${c.primary("locus daemon start")}
46932
- Or run manually: ${c.primary("locus telegram start")}
46155
+ Start the bot with: ${c.primary("locus telegram run")}
46933
46156
  `);
46934
46157
  }
46935
- function showConfig(projectPath) {
46158
+ function configCommand2(projectPath) {
46936
46159
  const manager = new SettingsManager(projectPath);
46937
46160
  const settings = manager.load();
46938
46161
  const tg = settings.telegram;
@@ -46948,28 +46171,36 @@ function showConfig(projectPath) {
46948
46171
  console.log(` ${c.dim("File: .locus/settings.json (telegram section)")}
46949
46172
  `);
46950
46173
  const entries = [];
46951
- if (tg.botToken)
46174
+ if (tg.botToken) {
46952
46175
  entries.push(["botToken", maskToken(tg.botToken)]);
46953
- if (tg.chatId)
46176
+ }
46177
+ if (tg.chatId) {
46954
46178
  entries.push(["chatId", String(tg.chatId)]);
46955
- if (tg.testMode !== undefined)
46179
+ }
46180
+ if (tg.testMode !== undefined) {
46956
46181
  entries.push(["testMode", String(tg.testMode)]);
46957
- if (settings.apiKey)
46182
+ }
46183
+ if (settings.apiKey) {
46958
46184
  entries.push(["apiKey (shared)", maskToken(settings.apiKey)]);
46959
- if (settings.apiUrl)
46185
+ }
46186
+ if (settings.apiUrl) {
46960
46187
  entries.push(["apiUrl (shared)", settings.apiUrl]);
46961
- if (settings.provider)
46188
+ }
46189
+ if (settings.provider) {
46962
46190
  entries.push(["provider (shared)", settings.provider]);
46963
- if (settings.model)
46191
+ }
46192
+ if (settings.model) {
46964
46193
  entries.push(["model (shared)", settings.model]);
46965
- if (settings.workspaceId)
46194
+ }
46195
+ if (settings.workspaceId) {
46966
46196
  entries.push(["workspaceId (shared)", settings.workspaceId]);
46197
+ }
46967
46198
  for (const [key, value] of entries) {
46968
46199
  console.log(` ${c.primary(`${key}:`)} ${value}`);
46969
46200
  }
46970
46201
  console.log("");
46971
46202
  }
46972
- function setValue(args, projectPath) {
46203
+ function setCommand2(args, projectPath) {
46973
46204
  const key = args[0]?.trim();
46974
46205
  const value = args.slice(1).join(" ").trim();
46975
46206
  if (!key || !value) {
@@ -47012,7 +46243,7 @@ function setValue(args, projectPath) {
47012
46243
  ${c.success("✔")} Set ${c.primary(key)} = ${displayValue}
47013
46244
  `);
47014
46245
  }
47015
- function removeConfig(projectPath) {
46246
+ function removeCommand2(projectPath) {
47016
46247
  const manager = new SettingsManager(projectPath);
47017
46248
  const settings = manager.load();
47018
46249
  if (!settings.telegram) {
@@ -47027,7 +46258,7 @@ function removeConfig(projectPath) {
47027
46258
  ${c.success("✔")} ${c.bold("Telegram configuration removed.")}
47028
46259
  `);
47029
46260
  }
47030
- function startBot(projectPath) {
46261
+ function runBotCommand(projectPath) {
47031
46262
  const manager = new SettingsManager(projectPath);
47032
46263
  const settings = manager.load();
47033
46264
  if (!settings.telegram?.botToken || !settings.telegram?.chatId) {
@@ -47037,14 +46268,22 @@ function startBot(projectPath) {
47037
46268
  `);
47038
46269
  process.exit(1);
47039
46270
  }
47040
- const monorepoEntry = join22(projectPath, "packages/telegram/src/index.ts");
47041
- const isMonorepo = existsSync22(monorepoEntry);
47042
- const cmd = isMonorepo ? "bun" : "locus-telegram";
47043
- const cmdArgs = isMonorepo ? ["run", monorepoEntry] : [];
47044
- const child = spawn5(cmd, cmdArgs, {
46271
+ const monorepoTelegramEntry = join21(projectPath, "packages/telegram/src/index.ts");
46272
+ const isMonorepo = existsSync21(monorepoTelegramEntry);
46273
+ let cmd;
46274
+ let args;
46275
+ if (isMonorepo) {
46276
+ cmd = "bun";
46277
+ args = ["run", monorepoTelegramEntry];
46278
+ } else {
46279
+ cmd = "locus-telegram";
46280
+ args = [];
46281
+ }
46282
+ const env = { ...process.env };
46283
+ const child = spawn4(cmd, args, {
47045
46284
  cwd: projectPath,
47046
46285
  stdio: "inherit",
47047
- env: { ...process.env }
46286
+ env
47048
46287
  });
47049
46288
  child.on("error", (err) => {
47050
46289
  if (err.code === "ENOENT" && !isMonorepo) {
@@ -47065,23 +46304,22 @@ function startBot(projectPath) {
47065
46304
  }
47066
46305
  async function telegramCommand(args) {
47067
46306
  const projectPath = process.cwd();
47068
- const [subcommand, ...subArgs] = args;
46307
+ const subcommand = args[0];
47069
46308
  switch (subcommand) {
47070
- case "start":
47071
46309
  case "run":
47072
- startBot(projectPath);
46310
+ runBotCommand(projectPath);
47073
46311
  break;
47074
46312
  case "setup":
47075
- await setup(subArgs, projectPath);
46313
+ await setupCommand2(args, projectPath);
47076
46314
  break;
47077
46315
  case "config":
47078
- showConfig(projectPath);
46316
+ configCommand2(projectPath);
47079
46317
  break;
47080
46318
  case "set":
47081
- setValue(subArgs, projectPath);
46319
+ setCommand2(args, projectPath);
47082
46320
  break;
47083
46321
  case "remove":
47084
- removeConfig(projectPath);
46322
+ removeCommand2(projectPath);
47085
46323
  break;
47086
46324
  default:
47087
46325
  showTelegramHelp();
@@ -47117,7 +46355,6 @@ async function upgradeCommand() {
47117
46355
  console.log(`
47118
46356
  ${c.header(" UPGRADE ")}
47119
46357
  `);
47120
- const daemonWasRunning = await isDaemonRunning();
47121
46358
  try {
47122
46359
  console.log(` ${c.dim("◌")} Cleaning npm cache...`);
47123
46360
  execSync3("npm cache clean --force", {
@@ -47129,7 +46366,6 @@ async function upgradeCommand() {
47129
46366
  console.log(` ${c.dim("⚠")} Could not clean npm cache, continuing...
47130
46367
  `);
47131
46368
  }
47132
- let anyUpdated = false;
47133
46369
  for (const pkg of PACKAGES) {
47134
46370
  const current = getInstalledVersion(pkg);
47135
46371
  const latest = getLatestVersion(pkg);
@@ -47152,27 +46388,12 @@ async function upgradeCommand() {
47152
46388
  });
47153
46389
  console.log(` ${c.success("✔")} ${c.bold(pkg)} updated to ${c.primary(`v${latest}`)}
47154
46390
  `);
47155
- anyUpdated = true;
47156
46391
  } catch {
47157
46392
  console.error(` ${c.error("✖")} Failed to update ${c.bold(pkg)}. Try manually:
47158
46393
  ` + ` ${c.primary(`npm install -g ${pkg}@latest`)}
47159
46394
  `);
47160
46395
  }
47161
46396
  }
47162
- if (daemonWasRunning && anyUpdated) {
47163
- console.log(` ${c.info("▶")} Restarting locus daemon...`);
47164
- const restarted = await restartDaemonIfRunning();
47165
- if (restarted) {
47166
- console.log(` ${c.success("✔")} Locus daemon restarted
47167
- `);
47168
- } else {
47169
- console.log(` ${c.dim("⚠")} Could not restart daemon (may need sudo)
47170
- `);
47171
- }
47172
- } else if (daemonWasRunning && !anyUpdated) {
47173
- console.log(` ${c.dim("No updates — daemon left running")}
47174
- `);
47175
- }
47176
46397
  console.log("");
47177
46398
  }
47178
46399
  // src/commands/version.ts
@@ -47244,12 +46465,6 @@ async function main() {
47244
46465
  case "config":
47245
46466
  await configCommand(args);
47246
46467
  break;
47247
- case "daemon":
47248
- await daemonCommand(args);
47249
- break;
47250
- case "service":
47251
- await serviceCommand(args);
47252
- break;
47253
46468
  case "docs":
47254
46469
  await docsCommand(args);
47255
46470
  break;
@@ -47278,11 +46493,3 @@ main().catch((err) => {
47278
46493
  ${c.error("✖ Fatal Error")} ${c.red(err.message)}`);
47279
46494
  process.exit(1);
47280
46495
  });
47281
-
47282
- // index.ts
47283
- var _emit = process.emit;
47284
- process.emit = function(name, data, ...args) {
47285
- if (name === "warning" && data?.code === "DEP0040")
47286
- return false;
47287
- return _emit.apply(process, [name, data, ...args]);
47288
- };