@locusai/cli 0.7.3 → 0.7.6

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 +413 -130
  2. package/bin/locus.js +32401 -29085
  3. package/package.json +2 -2
@@ -14880,11 +14880,11 @@ var require_out = __commonJS((exports) => {
14880
14880
  async.read(path, getSettings(optionsOrSettingsOrCallback), callback);
14881
14881
  }
14882
14882
  exports.stat = stat;
14883
- function statSync2(path, optionsOrSettings) {
14883
+ function statSync(path, optionsOrSettings) {
14884
14884
  const settings = getSettings(optionsOrSettings);
14885
14885
  return sync.read(path, settings);
14886
14886
  }
14887
- exports.statSync = statSync2;
14887
+ exports.statSync = statSync;
14888
14888
  function getSettings(settingsOrOptions = {}) {
14889
14889
  if (settingsOrOptions instanceof settings_1.default) {
14890
14890
  return settingsOrOptions;
@@ -17018,8 +17018,8 @@ var PROVIDER = {
17018
17018
  CODEX: "codex"
17019
17019
  };
17020
17020
  var DEFAULT_MODEL = {
17021
- [PROVIDER.CLAUDE]: "sonnet",
17022
- [PROVIDER.CODEX]: "gpt-5.1-codex-mini"
17021
+ [PROVIDER.CLAUDE]: "opus",
17022
+ [PROVIDER.CODEX]: "gpt-5.2-codex"
17023
17023
  };
17024
17024
  var LOCUS_CONFIG = {
17025
17025
  dir: ".locus",
@@ -17028,7 +17028,8 @@ var LOCUS_CONFIG = {
17028
17028
  contextFile: "CLAUDE.md",
17029
17029
  artifactsDir: "artifacts",
17030
17030
  documentsDir: "documents",
17031
- agentSkillsDir: ".agent/skills"
17031
+ agentSkillsDir: ".agent/skills",
17032
+ sessionsDir: "sessions"
17032
17033
  };
17033
17034
  function getLocusPath(projectPath, fileName) {
17034
17035
  if (fileName === "contextFile") {
@@ -17108,11 +17109,17 @@ class ClaudeRunner {
17108
17109
  model;
17109
17110
  log;
17110
17111
  projectPath;
17112
+ eventEmitter;
17113
+ currentToolName;
17114
+ activeTools = new Map;
17111
17115
  constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CLAUDE], log) {
17112
17116
  this.model = model;
17113
17117
  this.log = log;
17114
17118
  this.projectPath = resolve(projectPath);
17115
17119
  }
17120
+ setEventEmitter(emitter) {
17121
+ this.eventEmitter = emitter;
17122
+ }
17116
17123
  async run(prompt, _isPlanning = false) {
17117
17124
  const maxRetries = 3;
17118
17125
  let lastError = null;
@@ -17132,6 +17139,246 @@ class ClaudeRunner {
17132
17139
  }
17133
17140
  throw lastError || new Error("Claude CLI failed after multiple attempts");
17134
17141
  }
17142
+ async* runStream(prompt) {
17143
+ const args = [
17144
+ "--dangerously-skip-permissions",
17145
+ "--print",
17146
+ "--verbose",
17147
+ "--output-format",
17148
+ "stream-json",
17149
+ "--include-partial-messages",
17150
+ "--model",
17151
+ this.model
17152
+ ];
17153
+ const env = {
17154
+ ...process.env,
17155
+ FORCE_COLOR: "1",
17156
+ TERM: "xterm-256color"
17157
+ };
17158
+ this.eventEmitter?.emitSessionStarted({
17159
+ model: this.model,
17160
+ provider: "claude"
17161
+ });
17162
+ this.eventEmitter?.emitPromptSubmitted(prompt, prompt.length > 500);
17163
+ const claude = spawn("claude", args, {
17164
+ cwd: this.projectPath,
17165
+ stdio: ["pipe", "pipe", "pipe"],
17166
+ env
17167
+ });
17168
+ let buffer = "";
17169
+ let stderrBuffer = "";
17170
+ let resolveChunk = null;
17171
+ const chunkQueue = [];
17172
+ let processEnded = false;
17173
+ let errorMessage = "";
17174
+ let finalContent = "";
17175
+ let isThinking = false;
17176
+ const enqueueChunk = (chunk) => {
17177
+ this.emitEventForChunk(chunk, isThinking);
17178
+ if (chunk.type === "thinking") {
17179
+ isThinking = true;
17180
+ } else if (chunk.type === "text_delta" || chunk.type === "tool_use") {
17181
+ if (isThinking) {
17182
+ this.eventEmitter?.emitThinkingStoped();
17183
+ isThinking = false;
17184
+ }
17185
+ }
17186
+ if (chunk.type === "text_delta") {
17187
+ finalContent += chunk.content;
17188
+ }
17189
+ if (resolveChunk) {
17190
+ const resolve2 = resolveChunk;
17191
+ resolveChunk = null;
17192
+ resolve2(chunk);
17193
+ } else {
17194
+ chunkQueue.push(chunk);
17195
+ }
17196
+ };
17197
+ const signalEnd = () => {
17198
+ processEnded = true;
17199
+ if (resolveChunk) {
17200
+ resolveChunk(null);
17201
+ resolveChunk = null;
17202
+ }
17203
+ };
17204
+ claude.stdout.on("data", (data) => {
17205
+ buffer += data.toString();
17206
+ const lines = buffer.split(`
17207
+ `);
17208
+ buffer = lines.pop() || "";
17209
+ for (const line of lines) {
17210
+ const chunk = this.parseStreamLineToChunk(line);
17211
+ if (chunk) {
17212
+ enqueueChunk(chunk);
17213
+ }
17214
+ }
17215
+ });
17216
+ claude.stderr.on("data", (data) => {
17217
+ const chunk = data.toString();
17218
+ stderrBuffer += chunk;
17219
+ const lines = stderrBuffer.split(`
17220
+ `);
17221
+ stderrBuffer = lines.pop() || "";
17222
+ for (const line of lines) {
17223
+ if (!this.shouldSuppressLine(line)) {
17224
+ process.stderr.write(`${line}
17225
+ `);
17226
+ }
17227
+ }
17228
+ });
17229
+ claude.on("error", (err) => {
17230
+ errorMessage = `Failed to start Claude CLI: ${err.message}. Please ensure the 'claude' command is available in your PATH.`;
17231
+ this.eventEmitter?.emitErrorOccurred(errorMessage, "SPAWN_ERROR");
17232
+ signalEnd();
17233
+ });
17234
+ claude.on("close", (code) => {
17235
+ if (stderrBuffer && !this.shouldSuppressLine(stderrBuffer)) {
17236
+ process.stderr.write(`${stderrBuffer}
17237
+ `);
17238
+ }
17239
+ if (code !== 0 && !errorMessage) {
17240
+ errorMessage = this.createExecutionError(code, stderrBuffer).message;
17241
+ this.eventEmitter?.emitErrorOccurred(errorMessage, `EXIT_${code}`);
17242
+ }
17243
+ signalEnd();
17244
+ });
17245
+ claude.stdin.write(prompt);
17246
+ claude.stdin.end();
17247
+ while (true) {
17248
+ if (chunkQueue.length > 0) {
17249
+ const chunk = chunkQueue.shift();
17250
+ if (chunk)
17251
+ yield chunk;
17252
+ } else if (processEnded) {
17253
+ if (errorMessage) {
17254
+ yield { type: "error", error: errorMessage };
17255
+ this.eventEmitter?.emitSessionEnded(false);
17256
+ } else {
17257
+ if (finalContent) {
17258
+ this.eventEmitter?.emitResponseCompleted(finalContent);
17259
+ }
17260
+ this.eventEmitter?.emitSessionEnded(true);
17261
+ }
17262
+ break;
17263
+ } else {
17264
+ const chunk = await new Promise((resolve2) => {
17265
+ resolveChunk = resolve2;
17266
+ });
17267
+ if (chunk === null) {
17268
+ if (errorMessage) {
17269
+ yield { type: "error", error: errorMessage };
17270
+ this.eventEmitter?.emitSessionEnded(false);
17271
+ } else {
17272
+ if (finalContent) {
17273
+ this.eventEmitter?.emitResponseCompleted(finalContent);
17274
+ }
17275
+ this.eventEmitter?.emitSessionEnded(true);
17276
+ }
17277
+ break;
17278
+ }
17279
+ yield chunk;
17280
+ }
17281
+ }
17282
+ }
17283
+ emitEventForChunk(chunk, isThinking) {
17284
+ if (!this.eventEmitter)
17285
+ return;
17286
+ switch (chunk.type) {
17287
+ case "text_delta":
17288
+ this.eventEmitter.emitTextDelta(chunk.content);
17289
+ break;
17290
+ case "tool_use":
17291
+ if (this.currentToolName) {
17292
+ this.eventEmitter.emitToolCompleted(this.currentToolName);
17293
+ }
17294
+ this.currentToolName = chunk.tool;
17295
+ this.eventEmitter.emitToolStarted(chunk.tool, chunk.id);
17296
+ break;
17297
+ case "thinking":
17298
+ if (!isThinking) {
17299
+ this.eventEmitter.emitThinkingStarted(chunk.content);
17300
+ }
17301
+ break;
17302
+ case "result":
17303
+ if (this.currentToolName) {
17304
+ this.eventEmitter.emitToolCompleted(this.currentToolName);
17305
+ this.currentToolName = undefined;
17306
+ }
17307
+ break;
17308
+ case "error":
17309
+ this.eventEmitter.emitErrorOccurred(chunk.error);
17310
+ break;
17311
+ }
17312
+ }
17313
+ parseStreamLineToChunk(line) {
17314
+ if (!line.trim())
17315
+ return null;
17316
+ try {
17317
+ const item = JSON.parse(line);
17318
+ return this.processStreamItemToChunk(item);
17319
+ } catch {
17320
+ return null;
17321
+ }
17322
+ }
17323
+ processStreamItemToChunk(item) {
17324
+ if (item.type === "result") {
17325
+ return { type: "result", content: item.result || "" };
17326
+ }
17327
+ if (item.type === "stream_event" && item.event) {
17328
+ return this.handleEventToChunk(item.event);
17329
+ }
17330
+ return null;
17331
+ }
17332
+ handleEventToChunk(event) {
17333
+ const { type, delta, content_block, index } = event;
17334
+ if (type === "content_block_delta" && delta?.type === "text_delta") {
17335
+ return { type: "text_delta", content: delta.text || "" };
17336
+ }
17337
+ if (type === "content_block_delta" && delta?.type === "input_json_delta" && delta.partial_json !== undefined && index !== undefined) {
17338
+ const activeTool = this.activeTools.get(index);
17339
+ if (activeTool) {
17340
+ activeTool.parameterJson += delta.partial_json;
17341
+ }
17342
+ return null;
17343
+ }
17344
+ if (type === "content_block_start" && content_block) {
17345
+ if (content_block.type === "tool_use" && content_block.name) {
17346
+ if (index !== undefined) {
17347
+ this.activeTools.set(index, {
17348
+ name: content_block.name,
17349
+ id: content_block.id,
17350
+ index,
17351
+ parameterJson: "",
17352
+ startTime: Date.now()
17353
+ });
17354
+ }
17355
+ return {
17356
+ type: "tool_use",
17357
+ tool: content_block.name,
17358
+ id: content_block.id
17359
+ };
17360
+ }
17361
+ if (content_block.type === "thinking") {
17362
+ return { type: "thinking" };
17363
+ }
17364
+ }
17365
+ if (type === "content_block_stop" && index !== undefined) {
17366
+ const activeTool = this.activeTools.get(index);
17367
+ if (activeTool?.parameterJson) {
17368
+ try {
17369
+ const parameters = JSON.parse(activeTool.parameterJson);
17370
+ return {
17371
+ type: "tool_parameters",
17372
+ tool: activeTool.name,
17373
+ id: activeTool.id,
17374
+ parameters
17375
+ };
17376
+ } catch {}
17377
+ }
17378
+ return null;
17379
+ }
17380
+ return null;
17381
+ }
17135
17382
  executeRun(prompt) {
17136
17383
  return new Promise((resolve2, reject) => {
17137
17384
  const args = [
@@ -17275,6 +17522,99 @@ class CodexRunner {
17275
17522
  }
17276
17523
  throw lastError || new Error("Codex CLI failed after multiple attempts");
17277
17524
  }
17525
+ async* runStream(prompt) {
17526
+ const outputPath = join2(tmpdir(), `locus-codex-${randomUUID()}.txt`);
17527
+ const args = this.buildArgs(outputPath);
17528
+ const codex = spawn2("codex", args, {
17529
+ cwd: this.projectPath,
17530
+ stdio: ["pipe", "pipe", "pipe"],
17531
+ env: process.env,
17532
+ shell: false
17533
+ });
17534
+ let resolveChunk = null;
17535
+ const chunkQueue = [];
17536
+ let processEnded = false;
17537
+ let errorMessage = "";
17538
+ let finalOutput = "";
17539
+ const enqueueChunk = (chunk) => {
17540
+ if (resolveChunk) {
17541
+ const resolve2 = resolveChunk;
17542
+ resolveChunk = null;
17543
+ resolve2(chunk);
17544
+ } else {
17545
+ chunkQueue.push(chunk);
17546
+ }
17547
+ };
17548
+ const signalEnd = () => {
17549
+ processEnded = true;
17550
+ if (resolveChunk) {
17551
+ resolveChunk(null);
17552
+ resolveChunk = null;
17553
+ }
17554
+ };
17555
+ const processOutput = (data) => {
17556
+ const msg = data.toString();
17557
+ finalOutput += msg;
17558
+ for (const rawLine of msg.split(`
17559
+ `)) {
17560
+ const line = rawLine.trim();
17561
+ if (!line)
17562
+ continue;
17563
+ if (/^thinking\b/i.test(line)) {
17564
+ enqueueChunk({ type: "thinking", content: line });
17565
+ } else if (/^[→•✓]/.test(line) || /^Plan update\b/.test(line)) {
17566
+ enqueueChunk({
17567
+ type: "tool_use",
17568
+ tool: line.replace(/^[→•✓]\s*/, "")
17569
+ });
17570
+ } else if (this.shouldDisplay(line)) {
17571
+ enqueueChunk({ type: "text_delta", content: `${line}
17572
+ ` });
17573
+ }
17574
+ }
17575
+ };
17576
+ codex.stdout.on("data", processOutput);
17577
+ codex.stderr.on("data", processOutput);
17578
+ codex.on("error", (err) => {
17579
+ errorMessage = `Failed to start Codex CLI: ${err.message}. Ensure 'codex' is installed and available in PATH.`;
17580
+ signalEnd();
17581
+ });
17582
+ codex.on("close", (code) => {
17583
+ this.cleanupTempFile(outputPath);
17584
+ if (code === 0) {
17585
+ const result = this.readOutput(outputPath, finalOutput);
17586
+ enqueueChunk({ type: "result", content: result });
17587
+ } else if (!errorMessage) {
17588
+ errorMessage = this.createErrorFromOutput(code, finalOutput).message;
17589
+ }
17590
+ signalEnd();
17591
+ });
17592
+ codex.stdin.write(prompt);
17593
+ codex.stdin.end();
17594
+ while (true) {
17595
+ if (chunkQueue.length > 0) {
17596
+ const chunk = chunkQueue.shift();
17597
+ if (chunk)
17598
+ yield chunk;
17599
+ } else if (processEnded) {
17600
+ if (errorMessage) {
17601
+ yield { type: "error", error: errorMessage };
17602
+ }
17603
+ break;
17604
+ } else {
17605
+ const chunk = await new Promise((resolve2) => {
17606
+ resolveChunk = resolve2;
17607
+ });
17608
+ if (chunk === null) {
17609
+ if (errorMessage) {
17610
+ yield { type: "error", error: errorMessage };
17611
+ }
17612
+ break;
17613
+ }
17614
+ yield chunk;
17615
+ }
17616
+ }
17617
+ }
17278
17618
  executeRun(prompt) {
17279
17619
  return new Promise((resolve2, reject) => {
17280
17620
  const outputPath = join2(tmpdir(), `locus-codex-${randomUUID()}.txt`);
@@ -35504,111 +35844,10 @@ class LocusClient {
35504
35844
  }
35505
35845
  }
35506
35846
 
35507
- // ../sdk/src/agent/artifact-syncer.ts
35508
- import {
35509
- existsSync as existsSync2,
35510
- mkdirSync,
35511
- readdirSync,
35512
- readFileSync as readFileSync2,
35513
- statSync,
35514
- writeFileSync
35515
- } from "node:fs";
35516
- import { join as join3 } from "node:path";
35517
- class ArtifactSyncer {
35518
- deps;
35519
- constructor(deps) {
35520
- this.deps = deps;
35521
- }
35522
- async getOrCreateArtifactsGroup() {
35523
- try {
35524
- const groups = await this.deps.client.docs.listGroups(this.deps.workspaceId);
35525
- const artifactsGroup = groups.find((g) => g.name === "Artifacts");
35526
- if (artifactsGroup) {
35527
- return artifactsGroup.id;
35528
- }
35529
- const newGroup = await this.deps.client.docs.createGroup(this.deps.workspaceId, {
35530
- name: "Artifacts",
35531
- order: 999
35532
- });
35533
- this.deps.log("Created 'Artifacts' group for agent-generated docs", "info");
35534
- return newGroup.id;
35535
- } catch (error48) {
35536
- this.deps.log(`Failed to get/create Artifacts group: ${error48}`, "error");
35537
- throw error48;
35538
- }
35539
- }
35540
- async sync() {
35541
- const artifactsDir = getLocusPath(this.deps.projectPath, "artifactsDir");
35542
- if (!existsSync2(artifactsDir)) {
35543
- mkdirSync(artifactsDir, { recursive: true });
35544
- return;
35545
- }
35546
- try {
35547
- const files = readdirSync(artifactsDir);
35548
- this.deps.log(`Syncing ${files.length} artifacts to server...`, "info");
35549
- const groups = await this.deps.client.docs.listGroups(this.deps.workspaceId);
35550
- const groupMap = new Map(groups.map((g) => [g.id, g.name]));
35551
- let artifactsGroupId = groups.find((g) => g.name === "Artifacts")?.id;
35552
- if (!artifactsGroupId) {
35553
- artifactsGroupId = await this.getOrCreateArtifactsGroup();
35554
- }
35555
- const existingDocs = await this.deps.client.docs.list(this.deps.workspaceId);
35556
- for (const file2 of files) {
35557
- const filePath = join3(artifactsDir, file2);
35558
- if (statSync(filePath).isFile()) {
35559
- const content = readFileSync2(filePath, "utf-8");
35560
- const title = file2.replace(/\.md$/, "").trim();
35561
- if (!title)
35562
- continue;
35563
- const existing = existingDocs.find((d) => d.title === title);
35564
- if (existing) {
35565
- if (existing.content !== content || existing.groupId !== artifactsGroupId) {
35566
- await this.deps.client.docs.update(existing.id, this.deps.workspaceId, { content, groupId: artifactsGroupId });
35567
- this.deps.log(`Updated artifact: ${file2}`, "success");
35568
- }
35569
- } else {
35570
- await this.deps.client.docs.create(this.deps.workspaceId, {
35571
- title,
35572
- content,
35573
- groupId: artifactsGroupId,
35574
- type: "GENERAL" /* GENERAL */
35575
- });
35576
- this.deps.log(`Created artifact: ${file2}`, "success");
35577
- }
35578
- }
35579
- }
35580
- for (const doc3 of existingDocs) {
35581
- if (doc3.groupId === artifactsGroupId) {
35582
- const fileName = `${doc3.title}.md`;
35583
- const filePath = join3(artifactsDir, fileName);
35584
- if (!existsSync2(filePath)) {
35585
- writeFileSync(filePath, doc3.content || "");
35586
- this.deps.log(`Fetched artifact: ${fileName}`, "success");
35587
- }
35588
- } else {
35589
- const documentsDir = getLocusPath(this.deps.projectPath, "documentsDir");
35590
- const groupName = groupMap.get(doc3.groupId || "") || "General";
35591
- const groupDir = join3(documentsDir, groupName);
35592
- if (!existsSync2(groupDir)) {
35593
- mkdirSync(groupDir, { recursive: true });
35594
- }
35595
- const fileName = `${doc3.title}.md`;
35596
- const filePath = join3(groupDir, fileName);
35597
- if (!existsSync2(filePath) || readFileSync2(filePath, "utf-8") !== doc3.content) {
35598
- writeFileSync(filePath, doc3.content || "");
35599
- }
35600
- }
35601
- }
35602
- } catch (error48) {
35603
- this.deps.log(`Failed to sync artifacts: ${error48}`, "error");
35604
- }
35605
- }
35606
- }
35607
-
35608
35847
  // ../sdk/src/core/indexer.ts
35609
35848
  import { createHash } from "node:crypto";
35610
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
35611
- import { dirname, join as join4 } from "node:path";
35849
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
35850
+ import { dirname, join as join3 } from "node:path";
35612
35851
 
35613
35852
  // ../../node_modules/globby/index.js
35614
35853
  import process4 from "node:process";
@@ -36121,7 +36360,7 @@ class CodebaseIndexer {
36121
36360
  fullReindexRatioThreshold = 0.2;
36122
36361
  constructor(projectPath) {
36123
36362
  this.projectPath = projectPath;
36124
- this.indexPath = join4(projectPath, ".locus", "codebase-index.json");
36363
+ this.indexPath = join3(projectPath, ".locus", "codebase-index.json");
36125
36364
  }
36126
36365
  async index(onProgress, treeSummarizer, force = false) {
36127
36366
  if (!treeSummarizer) {
@@ -36177,11 +36416,11 @@ class CodebaseIndexer {
36177
36416
  }
36178
36417
  }
36179
36418
  async getFileTree() {
36180
- const gitmodulesPath = join4(this.projectPath, ".gitmodules");
36419
+ const gitmodulesPath = join3(this.projectPath, ".gitmodules");
36181
36420
  const submoduleIgnores = [];
36182
- if (existsSync3(gitmodulesPath)) {
36421
+ if (existsSync2(gitmodulesPath)) {
36183
36422
  try {
36184
- const content = readFileSync3(gitmodulesPath, "utf-8");
36423
+ const content = readFileSync2(gitmodulesPath, "utf-8");
36185
36424
  const lines = content.split(`
36186
36425
  `);
36187
36426
  for (const line of lines) {
@@ -36237,9 +36476,9 @@ class CodebaseIndexer {
36237
36476
  });
36238
36477
  }
36239
36478
  loadIndex() {
36240
- if (existsSync3(this.indexPath)) {
36479
+ if (existsSync2(this.indexPath)) {
36241
36480
  try {
36242
- return JSON.parse(readFileSync3(this.indexPath, "utf-8"));
36481
+ return JSON.parse(readFileSync2(this.indexPath, "utf-8"));
36243
36482
  } catch {
36244
36483
  return null;
36245
36484
  }
@@ -36248,10 +36487,10 @@ class CodebaseIndexer {
36248
36487
  }
36249
36488
  saveIndex(index) {
36250
36489
  const dir = dirname(this.indexPath);
36251
- if (!existsSync3(dir)) {
36252
- mkdirSync2(dir, { recursive: true });
36490
+ if (!existsSync2(dir)) {
36491
+ mkdirSync(dir, { recursive: true });
36253
36492
  }
36254
- writeFileSync2(this.indexPath, JSON.stringify(index, null, 2));
36493
+ writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
36255
36494
  }
36256
36495
  cloneIndex(index) {
36257
36496
  return JSON.parse(JSON.stringify(index));
@@ -36267,7 +36506,7 @@ class CodebaseIndexer {
36267
36506
  }
36268
36507
  hashFile(filePath) {
36269
36508
  try {
36270
- const content = readFileSync3(join4(this.projectPath, filePath), "utf-8");
36509
+ const content = readFileSync2(join3(this.projectPath, filePath), "utf-8");
36271
36510
  return createHash("sha256").update(content).digest("hex").slice(0, 16);
36272
36511
  } catch {
36273
36512
  return null;
@@ -36371,8 +36610,52 @@ Return ONLY valid JSON, no markdown formatting.`;
36371
36610
  }
36372
36611
  }
36373
36612
 
36613
+ // ../sdk/src/agent/document-fetcher.ts
36614
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
36615
+ import { join as join4 } from "node:path";
36616
+ class DocumentFetcher {
36617
+ deps;
36618
+ constructor(deps) {
36619
+ this.deps = deps;
36620
+ }
36621
+ async fetch() {
36622
+ const documentsDir = getLocusPath(this.deps.projectPath, "documentsDir");
36623
+ if (!existsSync3(documentsDir)) {
36624
+ mkdirSync2(documentsDir, { recursive: true });
36625
+ }
36626
+ try {
36627
+ const groups = await this.deps.client.docs.listGroups(this.deps.workspaceId);
36628
+ const groupMap = new Map(groups.map((g) => [g.id, g.name]));
36629
+ const docs2 = await this.deps.client.docs.list(this.deps.workspaceId);
36630
+ const artifactsGroupId = groups.find((g) => g.name === "Artifacts")?.id;
36631
+ let fetchedCount = 0;
36632
+ for (const doc3 of docs2) {
36633
+ if (doc3.groupId === artifactsGroupId) {
36634
+ continue;
36635
+ }
36636
+ const groupName = groupMap.get(doc3.groupId || "") || "General";
36637
+ const groupDir = join4(documentsDir, groupName);
36638
+ if (!existsSync3(groupDir)) {
36639
+ mkdirSync2(groupDir, { recursive: true });
36640
+ }
36641
+ const fileName = `${doc3.title}.md`;
36642
+ const filePath = join4(groupDir, fileName);
36643
+ if (!existsSync3(filePath) || readFileSync3(filePath, "utf-8") !== doc3.content) {
36644
+ writeFileSync2(filePath, doc3.content || "");
36645
+ fetchedCount++;
36646
+ }
36647
+ }
36648
+ if (fetchedCount > 0) {
36649
+ this.deps.log(`Fetched ${fetchedCount} document(s) from server`, "info");
36650
+ }
36651
+ } catch (error48) {
36652
+ this.deps.log(`Failed to fetch documents: ${error48}`, "error");
36653
+ }
36654
+ }
36655
+ }
36656
+
36374
36657
  // ../sdk/src/core/prompt-builder.ts
36375
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
36658
+ import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
36376
36659
  import { homedir } from "node:os";
36377
36660
  import { join as join5 } from "node:path";
36378
36661
  class PromptBuilder {
@@ -36463,7 +36746,7 @@ ${serverContext.context}
36463
36746
  `;
36464
36747
  prompt += `You have access to the following documentation directories for context:
36465
36748
  `;
36466
- prompt += `- Artifacts: \`.locus/artifacts\`
36749
+ prompt += `- Artifacts: \`.locus/artifacts\`)
36467
36750
  `;
36468
36751
  prompt += `- Documents: \`.locus/documents\`
36469
36752
  `;
@@ -36577,9 +36860,9 @@ ${fallback}
36577
36860
  `;
36578
36861
  prompt += `You have access to the following documentation directories for context:
36579
36862
  `;
36580
- prompt += `- Artifacts: \`.locus/artifacts\`
36863
+ prompt += `- Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
36581
36864
  `;
36582
- prompt += `- Documents: \`.locus/documents\`
36865
+ prompt += `- Documents: \`.locus/documents\` (synced from cloud)
36583
36866
  `;
36584
36867
  prompt += `If you need more information about the project strategies, plans, or architecture, please read files in these directories.
36585
36868
 
@@ -36625,12 +36908,12 @@ There is an index file in the .locus/codebase-index.json and if you need you can
36625
36908
  }
36626
36909
  getProjectStructure() {
36627
36910
  try {
36628
- const entries = readdirSync2(this.projectPath);
36911
+ const entries = readdirSync(this.projectPath);
36629
36912
  const folders = entries.filter((e) => {
36630
36913
  if (e.startsWith(".") || e === "node_modules")
36631
36914
  return false;
36632
36915
  try {
36633
- return statSync2(join5(this.projectPath, e)).isDirectory();
36916
+ return statSync(join5(this.projectPath, e)).isDirectory();
36634
36917
  } catch {
36635
36918
  return false;
36636
36919
  }
@@ -36688,9 +36971,9 @@ There is an index file in the .locus/codebase-index.json and if you need you can
36688
36971
  if (!existsSync4(dirPath))
36689
36972
  return;
36690
36973
  try {
36691
- const entries = readdirSync2(dirPath).filter((name) => {
36974
+ const entries = readdirSync(dirPath).filter((name) => {
36692
36975
  try {
36693
- return statSync2(join5(dirPath, name)).isDirectory();
36976
+ return statSync(join5(dirPath, name)).isDirectory();
36694
36977
  } catch {
36695
36978
  return false;
36696
36979
  }
@@ -36789,7 +37072,7 @@ class AgentWorker {
36789
37072
  client;
36790
37073
  aiRunner;
36791
37074
  indexerService;
36792
- artifactSyncer;
37075
+ documentFetcher;
36793
37076
  taskExecutor;
36794
37077
  consecutiveEmpty = 0;
36795
37078
  maxEmpty = 60;
@@ -36821,7 +37104,7 @@ class AgentWorker {
36821
37104
  projectPath,
36822
37105
  log
36823
37106
  });
36824
- this.artifactSyncer = new ArtifactSyncer({
37107
+ this.documentFetcher = new DocumentFetcher({
36825
37108
  client: this.client,
36826
37109
  workspaceId: config2.workspaceId,
36827
37110
  projectPath,
@@ -36907,9 +37190,9 @@ class AgentWorker {
36907
37190
  this.log(`Claimed: ${task2.title}`, "success");
36908
37191
  const result = await this.executeTask(task2);
36909
37192
  try {
36910
- await this.artifactSyncer.sync();
37193
+ await this.documentFetcher.fetch();
36911
37194
  } catch (err) {
36912
- this.log(`Artifact sync failed: ${err}`, "error");
37195
+ this.log(`Document fetch failed: ${err}`, "error");
36913
37196
  }
36914
37197
  if (result.success) {
36915
37198
  this.log(`Completed: ${task2.title}`, "success");