@locusai/cli 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agent/worker.js +86 -25
- package/bin/locus.js +213 -82
- package/package.json +2 -2
package/bin/agent/worker.js
CHANGED
|
@@ -17065,7 +17065,15 @@ var colors = {
|
|
|
17065
17065
|
brightBlue: `${ESC}94m`,
|
|
17066
17066
|
brightMagenta: `${ESC}95m`,
|
|
17067
17067
|
brightCyan: `${ESC}96m`,
|
|
17068
|
-
brightWhite: `${ESC}97m
|
|
17068
|
+
brightWhite: `${ESC}97m`,
|
|
17069
|
+
bgBlack: `${ESC}40m`,
|
|
17070
|
+
bgRed: `${ESC}41m`,
|
|
17071
|
+
bgGreen: `${ESC}42m`,
|
|
17072
|
+
bgYellow: `${ESC}43m`,
|
|
17073
|
+
bgBlue: `${ESC}44m`,
|
|
17074
|
+
bgMagenta: `${ESC}45m`,
|
|
17075
|
+
bgCyan: `${ESC}46m`,
|
|
17076
|
+
bgWhite: `${ESC}47m`
|
|
17069
17077
|
};
|
|
17070
17078
|
var c = {
|
|
17071
17079
|
text: (text, ...colorNames) => {
|
|
@@ -17081,11 +17089,17 @@ var c = {
|
|
|
17081
17089
|
magenta: (t) => c.text(t, "magenta"),
|
|
17082
17090
|
cyan: (t) => c.text(t, "cyan"),
|
|
17083
17091
|
gray: (t) => c.text(t, "gray"),
|
|
17092
|
+
white: (t) => c.text(t, "white"),
|
|
17093
|
+
brightBlue: (t) => c.text(t, "brightBlue"),
|
|
17094
|
+
bgBlue: (t) => c.text(t, "bgBlue", "white", "bold"),
|
|
17084
17095
|
success: (t) => c.text(t, "green", "bold"),
|
|
17085
17096
|
error: (t) => c.text(t, "red", "bold"),
|
|
17086
17097
|
warning: (t) => c.text(t, "yellow", "bold"),
|
|
17087
17098
|
info: (t) => c.text(t, "cyan", "bold"),
|
|
17088
17099
|
primary: (t) => c.text(t, "blue", "bold"),
|
|
17100
|
+
secondary: (t) => c.text(t, "magenta", "bold"),
|
|
17101
|
+
header: (t) => c.text(` ${t} `, "bgBlue", "white", "bold"),
|
|
17102
|
+
step: (t) => c.text(` ${t} `, "bgCyan", "black", "bold"),
|
|
17089
17103
|
underline: (t) => c.text(t, "underline")
|
|
17090
17104
|
};
|
|
17091
17105
|
|
|
@@ -17143,6 +17157,7 @@ class ClaudeRunner {
|
|
|
17143
17157
|
let finalResult = "";
|
|
17144
17158
|
let errorOutput = "";
|
|
17145
17159
|
let buffer = "";
|
|
17160
|
+
let stderrBuffer = "";
|
|
17146
17161
|
claude.stdout.on("data", (data) => {
|
|
17147
17162
|
buffer += data.toString();
|
|
17148
17163
|
const lines = buffer.split(`
|
|
@@ -17155,14 +17170,27 @@ class ClaudeRunner {
|
|
|
17155
17170
|
}
|
|
17156
17171
|
});
|
|
17157
17172
|
claude.stderr.on("data", (data) => {
|
|
17158
|
-
const
|
|
17159
|
-
errorOutput +=
|
|
17160
|
-
|
|
17173
|
+
const chunk = data.toString();
|
|
17174
|
+
errorOutput += chunk;
|
|
17175
|
+
stderrBuffer += chunk;
|
|
17176
|
+
const lines = stderrBuffer.split(`
|
|
17177
|
+
`);
|
|
17178
|
+
stderrBuffer = lines.pop() || "";
|
|
17179
|
+
for (const line of lines) {
|
|
17180
|
+
if (!this.shouldSuppressLine(line)) {
|
|
17181
|
+
process.stderr.write(`${line}
|
|
17182
|
+
`);
|
|
17183
|
+
}
|
|
17184
|
+
}
|
|
17161
17185
|
});
|
|
17162
17186
|
claude.on("error", (err) => {
|
|
17163
17187
|
reject(new Error(`Failed to start Claude CLI: ${err.message}. Please ensure the 'claude' command is available in your PATH.`));
|
|
17164
17188
|
});
|
|
17165
17189
|
claude.on("close", (code) => {
|
|
17190
|
+
if (stderrBuffer && !this.shouldSuppressLine(stderrBuffer)) {
|
|
17191
|
+
process.stderr.write(`${stderrBuffer}
|
|
17192
|
+
`);
|
|
17193
|
+
}
|
|
17166
17194
|
process.stdout.write(`
|
|
17167
17195
|
`);
|
|
17168
17196
|
if (code === 0) {
|
|
@@ -17205,6 +17233,10 @@ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
|
|
|
17205
17233
|
}
|
|
17206
17234
|
}
|
|
17207
17235
|
}
|
|
17236
|
+
shouldSuppressLine(line) {
|
|
17237
|
+
const infoLogRegex = /^\[\d{2}:\d{2}:\d{2}\]\s\[.*?\]\sℹ\s*$/;
|
|
17238
|
+
return infoLogRegex.test(line.trim());
|
|
17239
|
+
}
|
|
17208
17240
|
createExecutionError(code, detail) {
|
|
17209
17241
|
const errorMsg = detail.trim();
|
|
17210
17242
|
const message = errorMsg ? `Claude CLI error (exit code ${code}): ${errorMsg}` : `Claude CLI exited with code ${code}. Please ensure the Claude CLI is installed and you are logged in.`;
|
|
@@ -20771,7 +20803,15 @@ class BaseModule {
|
|
|
20771
20803
|
// ../sdk/src/modules/ai.ts
|
|
20772
20804
|
class AIModule extends BaseModule {
|
|
20773
20805
|
async chat(workspaceId, body) {
|
|
20774
|
-
const { data } = await this.api.post(`/ai/${workspaceId}/chat`, body);
|
|
20806
|
+
const { data } = await this.api.post(`/ai/${workspaceId}/chat`, body, { timeout: 300000 });
|
|
20807
|
+
return data;
|
|
20808
|
+
}
|
|
20809
|
+
async detectIntent(workspaceId, body) {
|
|
20810
|
+
const { data } = await this.api.post(`/ai/${workspaceId}/chat/intent`, body, { timeout: 300000 });
|
|
20811
|
+
return data;
|
|
20812
|
+
}
|
|
20813
|
+
async executeIntent(workspaceId, body) {
|
|
20814
|
+
const { data } = await this.api.post(`/ai/${workspaceId}/chat/execute`, body, { timeout: 300000 });
|
|
20775
20815
|
return data;
|
|
20776
20816
|
}
|
|
20777
20817
|
async listSessions(workspaceId) {
|
|
@@ -20788,14 +20828,25 @@ class AIModule extends BaseModule {
|
|
|
20788
20828
|
async deleteSession(workspaceId, sessionId) {
|
|
20789
20829
|
await this.api.delete(`/ai/${workspaceId}/session/${sessionId}`);
|
|
20790
20830
|
}
|
|
20831
|
+
async shareSession(workspaceId, sessionId, body) {
|
|
20832
|
+
await this.api.post(`/ai/${workspaceId}/session/${sessionId}/share`, body);
|
|
20833
|
+
}
|
|
20834
|
+
async getSharedSession(sessionId) {
|
|
20835
|
+
const { data } = await this.api.get(`/ai/shared/${sessionId}`);
|
|
20836
|
+
return data;
|
|
20837
|
+
}
|
|
20791
20838
|
}
|
|
20792
20839
|
|
|
20793
20840
|
// ../sdk/src/modules/auth.ts
|
|
20794
20841
|
class AuthModule extends BaseModule {
|
|
20795
|
-
async
|
|
20842
|
+
async getProfile() {
|
|
20796
20843
|
const { data } = await this.api.get("/auth/me");
|
|
20797
20844
|
return data;
|
|
20798
20845
|
}
|
|
20846
|
+
async getApiKeyInfo() {
|
|
20847
|
+
const { data } = await this.api.get("/auth/api-key");
|
|
20848
|
+
return data;
|
|
20849
|
+
}
|
|
20799
20850
|
async requestRegisterOtp(email) {
|
|
20800
20851
|
const { data } = await this.api.post("/auth/register-otp", { email });
|
|
20801
20852
|
return data;
|
|
@@ -34804,6 +34855,9 @@ var ChatResponseSchema = exports_external.object({
|
|
|
34804
34855
|
sessionId: exports_external.string(),
|
|
34805
34856
|
history: exports_external.array(AIMessageSchema).optional()
|
|
34806
34857
|
});
|
|
34858
|
+
var ShareChatRequestSchema = exports_external.object({
|
|
34859
|
+
isShared: exports_external.boolean()
|
|
34860
|
+
});
|
|
34807
34861
|
// ../shared/src/models/user.ts
|
|
34808
34862
|
var UserSchema = BaseEntitySchema.extend({
|
|
34809
34863
|
email: exports_external.string().email(),
|
|
@@ -34841,9 +34895,10 @@ var JwtAuthUserSchema = exports_external.object({
|
|
|
34841
34895
|
});
|
|
34842
34896
|
var ApiKeyAuthUserSchema = exports_external.object({
|
|
34843
34897
|
authType: exports_external.literal("api_key"),
|
|
34844
|
-
apiKeyId: exports_external.
|
|
34898
|
+
apiKeyId: exports_external.string(),
|
|
34845
34899
|
apiKeyName: exports_external.string(),
|
|
34846
|
-
orgId: exports_external.
|
|
34900
|
+
orgId: exports_external.string().optional(),
|
|
34901
|
+
workspaceId: exports_external.string().optional()
|
|
34847
34902
|
});
|
|
34848
34903
|
var AuthenticatedUserSchema = exports_external.discriminatedUnion("authType", [
|
|
34849
34904
|
JwtAuthUserSchema,
|
|
@@ -35344,6 +35399,17 @@ class WorkspacesModule extends BaseModule {
|
|
|
35344
35399
|
const { data } = await this.api.post(`/workspaces/${id}/dispatch`, { workerId, sprintId });
|
|
35345
35400
|
return data.task;
|
|
35346
35401
|
}
|
|
35402
|
+
async listApiKeys(workspaceId) {
|
|
35403
|
+
const { data } = await this.api.get(`/workspaces/${workspaceId}/api-keys`);
|
|
35404
|
+
return data.apiKeys;
|
|
35405
|
+
}
|
|
35406
|
+
async createApiKey(workspaceId, name) {
|
|
35407
|
+
const { data } = await this.api.post(`/workspaces/${workspaceId}/api-keys`, { name });
|
|
35408
|
+
return data.apiKey;
|
|
35409
|
+
}
|
|
35410
|
+
async deleteApiKey(workspaceId, keyId) {
|
|
35411
|
+
await this.api.delete(`/workspaces/${workspaceId}/api-keys/${keyId}`);
|
|
35412
|
+
}
|
|
35347
35413
|
}
|
|
35348
35414
|
|
|
35349
35415
|
// ../sdk/src/index.ts
|
|
@@ -36068,13 +36134,10 @@ class CodebaseIndexer {
|
|
|
36068
36134
|
`);
|
|
36069
36135
|
const newTreeHash = this.hashTree(treeString);
|
|
36070
36136
|
const existingIndex = this.loadIndex();
|
|
36071
|
-
if (!force && existingIndex?.treeHash === newTreeHash) {
|
|
36072
|
-
onProgress?.("No file changes detected, skipping reindex");
|
|
36073
|
-
return null;
|
|
36074
|
-
}
|
|
36075
36137
|
const currentHashes = this.computeFileHashes(currentFiles);
|
|
36076
36138
|
const existingHashes = existingIndex?.fileHashes;
|
|
36077
|
-
const
|
|
36139
|
+
const hasExistingContent = existingIndex && (Object.keys(existingIndex.symbols).length > 0 || Object.keys(existingIndex.responsibilities).length > 0);
|
|
36140
|
+
const canIncremental = !force && existingIndex && existingHashes && hasExistingContent;
|
|
36078
36141
|
if (canIncremental) {
|
|
36079
36142
|
onProgress?.("Performing incremental update");
|
|
36080
36143
|
const { added, deleted, modified } = this.diffFiles(currentHashes, existingHashes);
|
|
@@ -36107,8 +36170,12 @@ class CodebaseIndexer {
|
|
|
36107
36170
|
}
|
|
36108
36171
|
}
|
|
36109
36172
|
onProgress?.("AI is analyzing codebase structure...");
|
|
36110
|
-
|
|
36111
|
-
|
|
36173
|
+
try {
|
|
36174
|
+
const index = await treeSummarizer(treeString);
|
|
36175
|
+
return this.applyIndexMetadata(index, currentHashes, newTreeHash);
|
|
36176
|
+
} catch (error48) {
|
|
36177
|
+
throw new Error(`AI analysis failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
36178
|
+
}
|
|
36112
36179
|
}
|
|
36113
36180
|
async getFileTree() {
|
|
36114
36181
|
const gitmodulesPath = join4(this.projectPath, ".gitmodules");
|
|
@@ -36600,16 +36667,12 @@ class TaskExecutor {
|
|
|
36600
36667
|
});
|
|
36601
36668
|
try {
|
|
36602
36669
|
let plan = null;
|
|
36603
|
-
|
|
36604
|
-
|
|
36605
|
-
} else {
|
|
36606
|
-
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
36607
|
-
const planningPrompt = `${basePrompt}
|
|
36670
|
+
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
36671
|
+
const planningPrompt = `${basePrompt}
|
|
36608
36672
|
|
|
36609
36673
|
## Phase 1: Planning
|
|
36610
36674
|
Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
|
|
36611
|
-
|
|
36612
|
-
}
|
|
36675
|
+
plan = await this.deps.aiRunner.run(planningPrompt, true);
|
|
36613
36676
|
this.deps.log("Starting Execution...", "info");
|
|
36614
36677
|
let executionPrompt = basePrompt;
|
|
36615
36678
|
if (plan != null) {
|
|
@@ -36698,7 +36761,6 @@ class AgentWorker {
|
|
|
36698
36761
|
this.taskExecutor = new TaskExecutor({
|
|
36699
36762
|
aiRunner: this.aiRunner,
|
|
36700
36763
|
projectPath,
|
|
36701
|
-
skipPlanning: config2.skipPlanning,
|
|
36702
36764
|
log
|
|
36703
36765
|
});
|
|
36704
36766
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
@@ -36829,8 +36891,7 @@ if (process.argv[1]?.includes("agent-worker") || process.argv[1]?.includes("work
|
|
|
36829
36891
|
if (value && !value.startsWith("--"))
|
|
36830
36892
|
i++;
|
|
36831
36893
|
config2.provider = resolveProvider(value);
|
|
36832
|
-
}
|
|
36833
|
-
config2.skipPlanning = true;
|
|
36894
|
+
}
|
|
36834
36895
|
}
|
|
36835
36896
|
if (!config2.agentId || !config2.workspaceId || !config2.apiBase || !config2.apiKey || !config2.projectPath) {
|
|
36836
36897
|
console.error("Missing required arguments");
|
package/bin/locus.js
CHANGED
|
@@ -30874,6 +30874,9 @@ var ChatResponseSchema = exports_external.object({
|
|
|
30874
30874
|
sessionId: exports_external.string(),
|
|
30875
30875
|
history: exports_external.array(AIMessageSchema).optional()
|
|
30876
30876
|
});
|
|
30877
|
+
var ShareChatRequestSchema = exports_external.object({
|
|
30878
|
+
isShared: exports_external.boolean()
|
|
30879
|
+
});
|
|
30877
30880
|
// ../shared/src/models/user.ts
|
|
30878
30881
|
var UserSchema = BaseEntitySchema.extend({
|
|
30879
30882
|
email: exports_external.string().email(),
|
|
@@ -30911,9 +30914,10 @@ var JwtAuthUserSchema = exports_external.object({
|
|
|
30911
30914
|
});
|
|
30912
30915
|
var ApiKeyAuthUserSchema = exports_external.object({
|
|
30913
30916
|
authType: exports_external.literal("api_key"),
|
|
30914
|
-
apiKeyId: exports_external.
|
|
30917
|
+
apiKeyId: exports_external.string(),
|
|
30915
30918
|
apiKeyName: exports_external.string(),
|
|
30916
|
-
orgId: exports_external.
|
|
30919
|
+
orgId: exports_external.string().optional(),
|
|
30920
|
+
workspaceId: exports_external.string().optional()
|
|
30917
30921
|
});
|
|
30918
30922
|
var AuthenticatedUserSchema = exports_external.discriminatedUnion("authType", [
|
|
30919
30923
|
JwtAuthUserSchema,
|
|
@@ -31960,13 +31964,10 @@ class CodebaseIndexer {
|
|
|
31960
31964
|
`);
|
|
31961
31965
|
const newTreeHash = this.hashTree(treeString);
|
|
31962
31966
|
const existingIndex = this.loadIndex();
|
|
31963
|
-
if (!force && existingIndex?.treeHash === newTreeHash) {
|
|
31964
|
-
onProgress?.("No file changes detected, skipping reindex");
|
|
31965
|
-
return null;
|
|
31966
|
-
}
|
|
31967
31967
|
const currentHashes = this.computeFileHashes(currentFiles);
|
|
31968
31968
|
const existingHashes = existingIndex?.fileHashes;
|
|
31969
|
-
const
|
|
31969
|
+
const hasExistingContent = existingIndex && (Object.keys(existingIndex.symbols).length > 0 || Object.keys(existingIndex.responsibilities).length > 0);
|
|
31970
|
+
const canIncremental = !force && existingIndex && existingHashes && hasExistingContent;
|
|
31970
31971
|
if (canIncremental) {
|
|
31971
31972
|
onProgress?.("Performing incremental update");
|
|
31972
31973
|
const { added, deleted, modified } = this.diffFiles(currentHashes, existingHashes);
|
|
@@ -31999,8 +32000,12 @@ class CodebaseIndexer {
|
|
|
31999
32000
|
}
|
|
32000
32001
|
}
|
|
32001
32002
|
onProgress?.("AI is analyzing codebase structure...");
|
|
32002
|
-
|
|
32003
|
-
|
|
32003
|
+
try {
|
|
32004
|
+
const index = await treeSummarizer(treeString);
|
|
32005
|
+
return this.applyIndexMetadata(index, currentHashes, newTreeHash);
|
|
32006
|
+
} catch (error48) {
|
|
32007
|
+
throw new Error(`AI analysis failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
32008
|
+
}
|
|
32004
32009
|
}
|
|
32005
32010
|
async getFileTree() {
|
|
32006
32011
|
const gitmodulesPath = join3(this.projectPath, ".gitmodules");
|
|
@@ -32491,16 +32496,12 @@ class TaskExecutor {
|
|
|
32491
32496
|
});
|
|
32492
32497
|
try {
|
|
32493
32498
|
let plan = null;
|
|
32494
|
-
|
|
32495
|
-
|
|
32496
|
-
} else {
|
|
32497
|
-
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
32498
|
-
const planningPrompt = `${basePrompt}
|
|
32499
|
+
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
32500
|
+
const planningPrompt = `${basePrompt}
|
|
32499
32501
|
|
|
32500
32502
|
## Phase 1: Planning
|
|
32501
32503
|
Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
|
|
32502
|
-
|
|
32503
|
-
}
|
|
32504
|
+
plan = await this.deps.aiRunner.run(planningPrompt, true);
|
|
32504
32505
|
this.deps.log("Starting Execution...", "info");
|
|
32505
32506
|
let executionPrompt = basePrompt;
|
|
32506
32507
|
if (plan != null) {
|
|
@@ -32558,7 +32559,15 @@ var colors = {
|
|
|
32558
32559
|
brightBlue: `${ESC}94m`,
|
|
32559
32560
|
brightMagenta: `${ESC}95m`,
|
|
32560
32561
|
brightCyan: `${ESC}96m`,
|
|
32561
|
-
brightWhite: `${ESC}97m
|
|
32562
|
+
brightWhite: `${ESC}97m`,
|
|
32563
|
+
bgBlack: `${ESC}40m`,
|
|
32564
|
+
bgRed: `${ESC}41m`,
|
|
32565
|
+
bgGreen: `${ESC}42m`,
|
|
32566
|
+
bgYellow: `${ESC}43m`,
|
|
32567
|
+
bgBlue: `${ESC}44m`,
|
|
32568
|
+
bgMagenta: `${ESC}45m`,
|
|
32569
|
+
bgCyan: `${ESC}46m`,
|
|
32570
|
+
bgWhite: `${ESC}47m`
|
|
32562
32571
|
};
|
|
32563
32572
|
var c = {
|
|
32564
32573
|
text: (text, ...colorNames) => {
|
|
@@ -32574,11 +32583,17 @@ var c = {
|
|
|
32574
32583
|
magenta: (t) => c.text(t, "magenta"),
|
|
32575
32584
|
cyan: (t) => c.text(t, "cyan"),
|
|
32576
32585
|
gray: (t) => c.text(t, "gray"),
|
|
32586
|
+
white: (t) => c.text(t, "white"),
|
|
32587
|
+
brightBlue: (t) => c.text(t, "brightBlue"),
|
|
32588
|
+
bgBlue: (t) => c.text(t, "bgBlue", "white", "bold"),
|
|
32577
32589
|
success: (t) => c.text(t, "green", "bold"),
|
|
32578
32590
|
error: (t) => c.text(t, "red", "bold"),
|
|
32579
32591
|
warning: (t) => c.text(t, "yellow", "bold"),
|
|
32580
32592
|
info: (t) => c.text(t, "cyan", "bold"),
|
|
32581
32593
|
primary: (t) => c.text(t, "blue", "bold"),
|
|
32594
|
+
secondary: (t) => c.text(t, "magenta", "bold"),
|
|
32595
|
+
header: (t) => c.text(` ${t} `, "bgBlue", "white", "bold"),
|
|
32596
|
+
step: (t) => c.text(` ${t} `, "bgCyan", "black", "bold"),
|
|
32582
32597
|
underline: (t) => c.text(t, "underline")
|
|
32583
32598
|
};
|
|
32584
32599
|
|
|
@@ -32636,6 +32651,7 @@ class ClaudeRunner {
|
|
|
32636
32651
|
let finalResult = "";
|
|
32637
32652
|
let errorOutput = "";
|
|
32638
32653
|
let buffer = "";
|
|
32654
|
+
let stderrBuffer = "";
|
|
32639
32655
|
claude.stdout.on("data", (data) => {
|
|
32640
32656
|
buffer += data.toString();
|
|
32641
32657
|
const lines = buffer.split(`
|
|
@@ -32648,14 +32664,27 @@ class ClaudeRunner {
|
|
|
32648
32664
|
}
|
|
32649
32665
|
});
|
|
32650
32666
|
claude.stderr.on("data", (data) => {
|
|
32651
|
-
const
|
|
32652
|
-
errorOutput +=
|
|
32653
|
-
|
|
32667
|
+
const chunk = data.toString();
|
|
32668
|
+
errorOutput += chunk;
|
|
32669
|
+
stderrBuffer += chunk;
|
|
32670
|
+
const lines = stderrBuffer.split(`
|
|
32671
|
+
`);
|
|
32672
|
+
stderrBuffer = lines.pop() || "";
|
|
32673
|
+
for (const line of lines) {
|
|
32674
|
+
if (!this.shouldSuppressLine(line)) {
|
|
32675
|
+
process.stderr.write(`${line}
|
|
32676
|
+
`);
|
|
32677
|
+
}
|
|
32678
|
+
}
|
|
32654
32679
|
});
|
|
32655
32680
|
claude.on("error", (err) => {
|
|
32656
32681
|
reject(new Error(`Failed to start Claude CLI: ${err.message}. Please ensure the 'claude' command is available in your PATH.`));
|
|
32657
32682
|
});
|
|
32658
32683
|
claude.on("close", (code) => {
|
|
32684
|
+
if (stderrBuffer && !this.shouldSuppressLine(stderrBuffer)) {
|
|
32685
|
+
process.stderr.write(`${stderrBuffer}
|
|
32686
|
+
`);
|
|
32687
|
+
}
|
|
32659
32688
|
process.stdout.write(`
|
|
32660
32689
|
`);
|
|
32661
32690
|
if (code === 0) {
|
|
@@ -32698,6 +32727,10 @@ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
|
|
|
32698
32727
|
}
|
|
32699
32728
|
}
|
|
32700
32729
|
}
|
|
32730
|
+
shouldSuppressLine(line) {
|
|
32731
|
+
const infoLogRegex = /^\[\d{2}:\d{2}:\d{2}\]\s\[.*?\]\sℹ\s*$/;
|
|
32732
|
+
return infoLogRegex.test(line.trim());
|
|
32733
|
+
}
|
|
32701
32734
|
createExecutionError(code, detail) {
|
|
32702
32735
|
const errorMsg = detail.trim();
|
|
32703
32736
|
const message = errorMsg ? `Claude CLI error (exit code ${code}): ${errorMsg}` : `Claude CLI exited with code ${code}. Please ensure the Claude CLI is installed and you are logged in.`;
|
|
@@ -36264,7 +36297,15 @@ class BaseModule {
|
|
|
36264
36297
|
// ../sdk/src/modules/ai.ts
|
|
36265
36298
|
class AIModule extends BaseModule {
|
|
36266
36299
|
async chat(workspaceId, body) {
|
|
36267
|
-
const { data } = await this.api.post(`/ai/${workspaceId}/chat`, body);
|
|
36300
|
+
const { data } = await this.api.post(`/ai/${workspaceId}/chat`, body, { timeout: 300000 });
|
|
36301
|
+
return data;
|
|
36302
|
+
}
|
|
36303
|
+
async detectIntent(workspaceId, body) {
|
|
36304
|
+
const { data } = await this.api.post(`/ai/${workspaceId}/chat/intent`, body, { timeout: 300000 });
|
|
36305
|
+
return data;
|
|
36306
|
+
}
|
|
36307
|
+
async executeIntent(workspaceId, body) {
|
|
36308
|
+
const { data } = await this.api.post(`/ai/${workspaceId}/chat/execute`, body, { timeout: 300000 });
|
|
36268
36309
|
return data;
|
|
36269
36310
|
}
|
|
36270
36311
|
async listSessions(workspaceId) {
|
|
@@ -36281,14 +36322,25 @@ class AIModule extends BaseModule {
|
|
|
36281
36322
|
async deleteSession(workspaceId, sessionId) {
|
|
36282
36323
|
await this.api.delete(`/ai/${workspaceId}/session/${sessionId}`);
|
|
36283
36324
|
}
|
|
36325
|
+
async shareSession(workspaceId, sessionId, body) {
|
|
36326
|
+
await this.api.post(`/ai/${workspaceId}/session/${sessionId}/share`, body);
|
|
36327
|
+
}
|
|
36328
|
+
async getSharedSession(sessionId) {
|
|
36329
|
+
const { data } = await this.api.get(`/ai/shared/${sessionId}`);
|
|
36330
|
+
return data;
|
|
36331
|
+
}
|
|
36284
36332
|
}
|
|
36285
36333
|
|
|
36286
36334
|
// ../sdk/src/modules/auth.ts
|
|
36287
36335
|
class AuthModule extends BaseModule {
|
|
36288
|
-
async
|
|
36336
|
+
async getProfile() {
|
|
36289
36337
|
const { data } = await this.api.get("/auth/me");
|
|
36290
36338
|
return data;
|
|
36291
36339
|
}
|
|
36340
|
+
async getApiKeyInfo() {
|
|
36341
|
+
const { data } = await this.api.get("/auth/api-key");
|
|
36342
|
+
return data;
|
|
36343
|
+
}
|
|
36292
36344
|
async requestRegisterOtp(email3) {
|
|
36293
36345
|
const { data } = await this.api.post("/auth/register-otp", { email: email3 });
|
|
36294
36346
|
return data;
|
|
@@ -36551,6 +36603,17 @@ class WorkspacesModule extends BaseModule {
|
|
|
36551
36603
|
const { data } = await this.api.post(`/workspaces/${id}/dispatch`, { workerId, sprintId });
|
|
36552
36604
|
return data.task;
|
|
36553
36605
|
}
|
|
36606
|
+
async listApiKeys(workspaceId) {
|
|
36607
|
+
const { data } = await this.api.get(`/workspaces/${workspaceId}/api-keys`);
|
|
36608
|
+
return data.apiKeys;
|
|
36609
|
+
}
|
|
36610
|
+
async createApiKey(workspaceId, name) {
|
|
36611
|
+
const { data } = await this.api.post(`/workspaces/${workspaceId}/api-keys`, { name });
|
|
36612
|
+
return data.apiKey;
|
|
36613
|
+
}
|
|
36614
|
+
async deleteApiKey(workspaceId, keyId) {
|
|
36615
|
+
await this.api.delete(`/workspaces/${workspaceId}/api-keys/${keyId}`);
|
|
36616
|
+
}
|
|
36554
36617
|
}
|
|
36555
36618
|
|
|
36556
36619
|
// ../sdk/src/index.ts
|
|
@@ -36704,7 +36767,6 @@ class AgentWorker {
|
|
|
36704
36767
|
this.taskExecutor = new TaskExecutor({
|
|
36705
36768
|
aiRunner: this.aiRunner,
|
|
36706
36769
|
projectPath,
|
|
36707
|
-
skipPlanning: config2.skipPlanning,
|
|
36708
36770
|
log
|
|
36709
36771
|
});
|
|
36710
36772
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
@@ -36835,8 +36897,7 @@ if (process.argv[1]?.includes("agent-worker") || process.argv[1]?.includes("work
|
|
|
36835
36897
|
if (value && !value.startsWith("--"))
|
|
36836
36898
|
i++;
|
|
36837
36899
|
config2.provider = resolveProvider(value);
|
|
36838
|
-
}
|
|
36839
|
-
config2.skipPlanning = true;
|
|
36900
|
+
}
|
|
36840
36901
|
}
|
|
36841
36902
|
if (!config2.agentId || !config2.workspaceId || !config2.apiBase || !config2.apiKey || !config2.projectPath) {
|
|
36842
36903
|
console.error("Missing required arguments");
|
|
@@ -36970,9 +37031,6 @@ ${c.success("✅ Orchestrator finished")}`);
|
|
|
36970
37031
|
if (this.config.provider) {
|
|
36971
37032
|
workerArgs.push("--provider", this.config.provider);
|
|
36972
37033
|
}
|
|
36973
|
-
if (this.config.skipPlanning) {
|
|
36974
|
-
workerArgs.push("--skip-planning");
|
|
36975
|
-
}
|
|
36976
37034
|
if (this.resolvedSprintId) {
|
|
36977
37035
|
workerArgs.push("--sprint-id", this.resolvedSprintId);
|
|
36978
37036
|
}
|
|
@@ -37477,10 +37535,23 @@ class ConfigManager {
|
|
|
37477
37535
|
const config2 = this.loadConfig();
|
|
37478
37536
|
if (config2 && config2.version !== version2) {
|
|
37479
37537
|
config2.version = version2;
|
|
37480
|
-
|
|
37481
|
-
writeFileSync3(path2, JSON.stringify(config2, null, 2));
|
|
37538
|
+
this.saveConfig(config2);
|
|
37482
37539
|
}
|
|
37483
37540
|
}
|
|
37541
|
+
getWorkspaceId() {
|
|
37542
|
+
return this.loadConfig()?.workspaceId;
|
|
37543
|
+
}
|
|
37544
|
+
setWorkspaceId(workspaceId) {
|
|
37545
|
+
const config2 = this.loadConfig();
|
|
37546
|
+
if (config2) {
|
|
37547
|
+
config2.workspaceId = workspaceId;
|
|
37548
|
+
this.saveConfig(config2);
|
|
37549
|
+
}
|
|
37550
|
+
}
|
|
37551
|
+
saveConfig(config2) {
|
|
37552
|
+
const path2 = getLocusPath(this.projectPath, "configFile");
|
|
37553
|
+
writeFileSync3(path2, JSON.stringify(config2, null, 2));
|
|
37554
|
+
}
|
|
37484
37555
|
}
|
|
37485
37556
|
|
|
37486
37557
|
// src/tree-summarizer.ts
|
|
@@ -37507,6 +37578,41 @@ ${tree}`;
|
|
|
37507
37578
|
}
|
|
37508
37579
|
}
|
|
37509
37580
|
|
|
37581
|
+
// src/workspace-resolver.ts
|
|
37582
|
+
class WorkspaceResolver {
|
|
37583
|
+
configManager;
|
|
37584
|
+
options;
|
|
37585
|
+
constructor(configManager, options) {
|
|
37586
|
+
this.configManager = configManager;
|
|
37587
|
+
this.options = options;
|
|
37588
|
+
}
|
|
37589
|
+
async resolve() {
|
|
37590
|
+
if (this.options.workspaceId) {
|
|
37591
|
+
return this.options.workspaceId;
|
|
37592
|
+
}
|
|
37593
|
+
const persistedId = this.configManager.getWorkspaceId();
|
|
37594
|
+
if (persistedId) {
|
|
37595
|
+
return persistedId;
|
|
37596
|
+
}
|
|
37597
|
+
try {
|
|
37598
|
+
console.log(c.dim("ℹ Resolving workspace from API key..."));
|
|
37599
|
+
const client = new LocusClient({
|
|
37600
|
+
baseUrl: this.options.apiBase,
|
|
37601
|
+
token: this.options.apiKey
|
|
37602
|
+
});
|
|
37603
|
+
const info = await client.auth.getApiKeyInfo();
|
|
37604
|
+
if (info.workspaceId) {
|
|
37605
|
+
this.configManager.setWorkspaceId(info.workspaceId);
|
|
37606
|
+
console.log(c.success(`✓ Resolved workspace: ${info.workspaceId}`));
|
|
37607
|
+
return info.workspaceId;
|
|
37608
|
+
}
|
|
37609
|
+
throw new Error("API key is not associated with a workspace. Please specify --workspace.");
|
|
37610
|
+
} catch (error48) {
|
|
37611
|
+
throw new Error(`Error resolving workspace: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
37612
|
+
}
|
|
37613
|
+
}
|
|
37614
|
+
}
|
|
37615
|
+
|
|
37510
37616
|
// src/cli.ts
|
|
37511
37617
|
function getVersion() {
|
|
37512
37618
|
try {
|
|
@@ -37523,12 +37629,12 @@ function getVersion() {
|
|
|
37523
37629
|
var VERSION2 = getVersion();
|
|
37524
37630
|
function printBanner() {
|
|
37525
37631
|
console.log(c.primary(`
|
|
37526
|
-
|
|
37527
|
-
|
|
37528
|
-
|
|
37529
|
-
|
|
37530
|
-
|
|
37531
|
-
`));
|
|
37632
|
+
_ ____ ____ _ _ ____
|
|
37633
|
+
| | / __ \\ / ___| | | |/ ___|
|
|
37634
|
+
| | | | | | | | | | |\\___ \\
|
|
37635
|
+
| |___| |__| | |___| |_| |___) |
|
|
37636
|
+
|_____|\\____/ \\____|\\___/|____/ ${c.dim(`v${VERSION2}`)}
|
|
37637
|
+
`));
|
|
37532
37638
|
}
|
|
37533
37639
|
function isProjectInitialized(projectPath) {
|
|
37534
37640
|
const locusDir = join8(projectPath, LOCUS_CONFIG.dir);
|
|
@@ -37538,14 +37644,14 @@ function isProjectInitialized(projectPath) {
|
|
|
37538
37644
|
function requireInitialization(projectPath, command) {
|
|
37539
37645
|
if (!isProjectInitialized(projectPath)) {
|
|
37540
37646
|
console.error(`
|
|
37541
|
-
${c.error("
|
|
37647
|
+
${c.error("✖ Error")} ${c.red(`Locus is not initialized in this directory.`)}
|
|
37542
37648
|
|
|
37543
|
-
The '${c.bold(command)}' command requires a Locus project to be initialized.
|
|
37649
|
+
The '${c.bold(command)}' command requires a Locus project to be initialized.
|
|
37544
37650
|
|
|
37545
|
-
To initialize Locus in this directory, run:
|
|
37546
|
-
|
|
37651
|
+
To initialize Locus in this directory, run:
|
|
37652
|
+
${c.primary("locus init")}
|
|
37547
37653
|
|
|
37548
|
-
This will create a .locus directory with the necessary configuration.
|
|
37654
|
+
This will create a ${c.dim(".locus")} directory with the necessary configuration.
|
|
37549
37655
|
`);
|
|
37550
37656
|
process.exit(1);
|
|
37551
37657
|
}
|
|
@@ -37575,13 +37681,27 @@ async function runCommand(args) {
|
|
|
37575
37681
|
});
|
|
37576
37682
|
const projectPath = values.dir || process.cwd();
|
|
37577
37683
|
requireInitialization(projectPath, "run");
|
|
37578
|
-
new ConfigManager(projectPath)
|
|
37684
|
+
const configManager = new ConfigManager(projectPath);
|
|
37685
|
+
configManager.updateVersion(VERSION2);
|
|
37579
37686
|
const apiKey = values["api-key"];
|
|
37580
|
-
|
|
37687
|
+
let workspaceId = values.workspace;
|
|
37581
37688
|
const provider = resolveProvider2(values.provider);
|
|
37582
37689
|
const model = values.model || DEFAULT_MODEL[provider];
|
|
37583
|
-
|
|
37584
|
-
|
|
37690
|
+
const apiBase = values["api-url"] || "https://api.locusai.dev/api";
|
|
37691
|
+
if (!apiKey) {
|
|
37692
|
+
console.error(c.error("Error: --api-key is required"));
|
|
37693
|
+
console.error(c.dim("You can create an API key in Workspace Settings > API Keys"));
|
|
37694
|
+
process.exit(1);
|
|
37695
|
+
}
|
|
37696
|
+
try {
|
|
37697
|
+
const resolver = new WorkspaceResolver(configManager, {
|
|
37698
|
+
apiKey,
|
|
37699
|
+
apiBase,
|
|
37700
|
+
workspaceId: values.workspace
|
|
37701
|
+
});
|
|
37702
|
+
workspaceId = await resolver.resolve();
|
|
37703
|
+
} catch (error48) {
|
|
37704
|
+
console.error(c.error(error48 instanceof Error ? error48.message : String(error48)));
|
|
37585
37705
|
process.exit(1);
|
|
37586
37706
|
}
|
|
37587
37707
|
const orchestrator = new AgentOrchestrator({
|
|
@@ -37589,15 +37709,14 @@ async function runCommand(args) {
|
|
|
37589
37709
|
sprintId: values.sprint || "",
|
|
37590
37710
|
model,
|
|
37591
37711
|
provider,
|
|
37592
|
-
apiBase
|
|
37712
|
+
apiBase,
|
|
37593
37713
|
maxIterations: 100,
|
|
37594
37714
|
projectPath,
|
|
37595
|
-
apiKey
|
|
37596
|
-
skipPlanning: Boolean(values["skip-planning"])
|
|
37715
|
+
apiKey
|
|
37597
37716
|
});
|
|
37598
|
-
orchestrator.on("task:assigned", (data) => console.log(
|
|
37599
|
-
orchestrator.on("task:completed", (data) => console.log(
|
|
37600
|
-
orchestrator.on("task:failed", (data) => console.log(
|
|
37717
|
+
orchestrator.on("task:assigned", (data) => console.log(` ${c.info("●")} ${c.bold("Claimed:")} ${data.title}`));
|
|
37718
|
+
orchestrator.on("task:completed", (data) => console.log(` ${c.success("✔")} ${c.success("Completed:")} ${c.dim(data.taskId)}`));
|
|
37719
|
+
orchestrator.on("task:failed", (data) => console.log(` ${c.error("✖")} ${c.error("Failed:")} ${c.bold(data.taskId)}: ${data.error}`));
|
|
37601
37720
|
const handleSignal = async (signal) => {
|
|
37602
37721
|
console.log(`
|
|
37603
37722
|
${c.info(`Received ${signal}. Stopping agents...`)}`);
|
|
@@ -37606,7 +37725,8 @@ ${c.info(`Received ${signal}. Stopping agents...`)}`);
|
|
|
37606
37725
|
};
|
|
37607
37726
|
process.on("SIGINT", () => handleSignal("SIGINT"));
|
|
37608
37727
|
process.on("SIGTERM", () => handleSignal("SIGTERM"));
|
|
37609
|
-
console.log(
|
|
37728
|
+
console.log(`
|
|
37729
|
+
${c.primary("\uD83D\uDE80")} ${c.bold("Starting Locus agent in")} ${c.primary(projectPath)}...`);
|
|
37610
37730
|
await orchestrator.start();
|
|
37611
37731
|
}
|
|
37612
37732
|
async function indexCommand(args) {
|
|
@@ -37630,12 +37750,22 @@ async function indexCommand(args) {
|
|
|
37630
37750
|
});
|
|
37631
37751
|
const summarizer = new TreeSummarizer(aiRunner);
|
|
37632
37752
|
const indexer = new CodebaseIndexer(projectPath);
|
|
37633
|
-
console.log(
|
|
37634
|
-
|
|
37635
|
-
|
|
37636
|
-
indexer.
|
|
37753
|
+
console.log(`
|
|
37754
|
+
${c.step(" INDEX ")} ${c.primary("Analyzing codebase in")} ${c.bold(projectPath)}...`);
|
|
37755
|
+
try {
|
|
37756
|
+
const index = await indexer.index((msg) => console.log(` ${c.dim(msg)}`), (tree) => summarizer.summarize(tree));
|
|
37757
|
+
if (index) {
|
|
37758
|
+
indexer.saveIndex(index);
|
|
37759
|
+
}
|
|
37760
|
+
} catch (error48) {
|
|
37761
|
+
console.error(`
|
|
37762
|
+
${c.error("✖")} ${c.error("Indexing failed:")} ${c.red(error48 instanceof Error ? error48.message : String(error48))}`);
|
|
37763
|
+
console.error(c.dim(` The agent might have limited context until indexing succeeds.
|
|
37764
|
+
`));
|
|
37637
37765
|
}
|
|
37638
|
-
console.log(
|
|
37766
|
+
console.log(`
|
|
37767
|
+
${c.success("✔")} ${c.success("Indexing complete!")}
|
|
37768
|
+
`);
|
|
37639
37769
|
}
|
|
37640
37770
|
async function initCommand() {
|
|
37641
37771
|
const projectPath = process.cwd();
|
|
@@ -37651,19 +37781,19 @@ If you want to reinitialize, please remove the .locus directory first.
|
|
|
37651
37781
|
}
|
|
37652
37782
|
await new ConfigManager(projectPath).init(VERSION2);
|
|
37653
37783
|
console.log(`
|
|
37654
|
-
${c.success("✨ Locus initialized successfully!")}
|
|
37784
|
+
${c.success("✨ Locus initialized successfully!")}
|
|
37655
37785
|
|
|
37656
|
-
Created:
|
|
37657
|
-
|
|
37658
|
-
|
|
37659
|
-
|
|
37660
|
-
|
|
37786
|
+
${c.bold("Created:")}
|
|
37787
|
+
${c.primary("\uD83D\uDCC1")} ${c.bold(".locus/")} ${c.dim("Configuration directory")}
|
|
37788
|
+
${c.primary("\uD83D\uDCC4")} ${c.bold(".locus/config.json")} ${c.dim("Project settings")}
|
|
37789
|
+
${c.primary("\uD83D\uDCDD")} ${c.bold("CLAUDE.md")} ${c.dim("AI instructions & context")}
|
|
37790
|
+
${c.primary("\uD83D\uDCC1")} ${c.bold(".agent/skills/")} ${c.dim("Domain-specific agent skills")}
|
|
37661
37791
|
|
|
37662
|
-
Next steps:
|
|
37663
|
-
|
|
37664
|
-
|
|
37792
|
+
${c.bold("Next steps:")}
|
|
37793
|
+
1. Run '${c.primary("locus index")}' to index your codebase
|
|
37794
|
+
2. Run '${c.primary("locus run")}' to start an agent (requires --api-key)
|
|
37665
37795
|
|
|
37666
|
-
For more information, visit: ${c.underline("https://locusai.dev/docs")}
|
|
37796
|
+
For more information, visit: ${c.underline("https://locusai.dev/docs")}
|
|
37667
37797
|
`);
|
|
37668
37798
|
}
|
|
37669
37799
|
async function main() {
|
|
@@ -37682,28 +37812,29 @@ async function main() {
|
|
|
37682
37812
|
break;
|
|
37683
37813
|
default:
|
|
37684
37814
|
console.log(`
|
|
37685
|
-
|
|
37815
|
+
${c.header(" USAGE ")}
|
|
37816
|
+
${c.primary("locus")} ${c.dim("<command> [options]")}
|
|
37686
37817
|
|
|
37687
|
-
|
|
37688
|
-
|
|
37689
|
-
|
|
37690
|
-
|
|
37818
|
+
${c.header(" COMMANDS ")}
|
|
37819
|
+
${c.success("init")} Initialize Locus in the current directory
|
|
37820
|
+
${c.success("index")} Index the codebase for AI context
|
|
37821
|
+
${c.success("run")} Start an agent to work on tasks
|
|
37691
37822
|
|
|
37692
|
-
|
|
37693
|
-
|
|
37694
|
-
|
|
37695
|
-
--skip-planning Skip the planning phase (CLI planning)
|
|
37823
|
+
${c.header(" OPTIONS ")}
|
|
37824
|
+
${c.secondary("--help")} Show this help message
|
|
37825
|
+
${c.secondary("--provider")} <name> AI provider: ${c.dim("claude")} or ${c.dim("codex")} (default: ${c.dim("claude")})
|
|
37696
37826
|
|
|
37697
|
-
|
|
37698
|
-
|
|
37699
|
-
|
|
37700
|
-
|
|
37827
|
+
${c.header(" EXAMPLES ")}
|
|
37828
|
+
${c.dim("$")} ${c.primary("locus init")}
|
|
37829
|
+
${c.dim("$")} ${c.primary("locus index")}
|
|
37830
|
+
${c.dim("$")} ${c.primary("locus run --api-key YOUR_KEY")}
|
|
37701
37831
|
|
|
37702
|
-
For more information, visit: https://locusai.dev/docs
|
|
37832
|
+
For more information, visit: ${c.underline("https://locusai.dev/docs")}
|
|
37703
37833
|
`);
|
|
37704
37834
|
}
|
|
37705
37835
|
}
|
|
37706
37836
|
main().catch((err) => {
|
|
37707
|
-
console.error(`
|
|
37837
|
+
console.error(`
|
|
37838
|
+
${c.error("✖ Fatal Error")} ${c.red(err.message)}`);
|
|
37708
37839
|
process.exit(1);
|
|
37709
37840
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "CLI for Locus - AI-native project management platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"author": "",
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@locusai/sdk": "^0.
|
|
35
|
+
"@locusai/sdk": "^0.7.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {}
|
|
38
38
|
}
|