@locusai/sdk 0.5.1 → 0.6.0

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.
@@ -52,7 +52,8 @@ __export(exports_src, {
52
52
  InvitationsModule: () => InvitationsModule,
53
53
  DocsModule: () => DocsModule,
54
54
  CiModule: () => CiModule,
55
- AuthModule: () => AuthModule
55
+ AuthModule: () => AuthModule,
56
+ AIModule: () => AIModule
56
57
  });
57
58
  module.exports = __toCommonJS(exports_src);
58
59
  var import_axios = __toESM(require("axios"));
@@ -85,6 +86,28 @@ class BaseModule {
85
86
  }
86
87
  }
87
88
 
89
+ // src/modules/ai.ts
90
+ class AIModule extends BaseModule {
91
+ async chat(workspaceId, body) {
92
+ const { data } = await this.api.post(`/ai/${workspaceId}/chat`, body);
93
+ return data;
94
+ }
95
+ async listSessions(workspaceId) {
96
+ const { data } = await this.api.get(`/ai/${workspaceId}/sessions`);
97
+ return data;
98
+ }
99
+ async getSession(workspaceId, sessionId) {
100
+ const { data } = await this.api.get(`/ai/${workspaceId}/session/${sessionId}`);
101
+ return data;
102
+ }
103
+ getChatStreamUrl(workspaceId, sessionId) {
104
+ return `${this.api.defaults.baseURL}/ai/${workspaceId}/chat/stream?sessionId=${sessionId}`;
105
+ }
106
+ async deleteSession(workspaceId, sessionId) {
107
+ await this.api.delete(`/ai/${workspaceId}/session/${sessionId}`);
108
+ }
109
+ }
110
+
88
111
  // src/modules/auth.ts
89
112
  class AuthModule extends BaseModule {
90
113
  async getMe() {
@@ -248,6 +271,10 @@ class SprintsModule extends BaseModule {
248
271
  const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/complete`);
249
272
  return data.sprint;
250
273
  }
274
+ async triggerAIPlanning(id, workspaceId) {
275
+ const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/trigger-ai-planning`);
276
+ return data.sprint;
277
+ }
251
278
  }
252
279
 
253
280
  // src/modules/tasks.ts
@@ -294,6 +321,16 @@ class TasksModule extends BaseModule {
294
321
  const { data } = await this.api.post(`/workspaces/${workspaceId}/tasks/${id}/comment`, body);
295
322
  return data.comment;
296
323
  }
324
+ async batchUpdate(ids, workspaceId, updates) {
325
+ await this.api.patch(`/workspaces/${workspaceId}/tasks/batch`, {
326
+ ids,
327
+ updates
328
+ });
329
+ }
330
+ async getContext(id, workspaceId) {
331
+ const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks/${id}/context`);
332
+ return data;
333
+ }
297
334
  }
298
335
 
299
336
  // src/modules/workspaces.ts
@@ -347,6 +384,7 @@ class LocusClient {
347
384
  api;
348
385
  emitter;
349
386
  auth;
387
+ ai;
350
388
  tasks;
351
389
  sprints;
352
390
  workspaces;
@@ -358,7 +396,7 @@ class LocusClient {
358
396
  this.emitter = new LocusEmitter;
359
397
  this.api = import_axios.default.create({
360
398
  baseURL: config.baseUrl,
361
- timeout: config.timeout || 1e4,
399
+ timeout: config.timeout || 60000,
362
400
  headers: {
363
401
  "Content-Type": "application/json",
364
402
  ...config.token ? { Authorization: `Bearer ${config.token}` } : {}
@@ -366,6 +404,7 @@ class LocusClient {
366
404
  });
367
405
  this.setupInterceptors();
368
406
  this.auth = new AuthModule(this.api, this.emitter);
407
+ this.ai = new AIModule(this.api, this.emitter);
369
408
  this.tasks = new TasksModule(this.api, this.emitter);
370
409
  this.sprints = new SprintsModule(this.api, this.emitter);
371
410
  this.workspaces = new WorkspacesModule(this.api, this.emitter);
@@ -439,7 +478,6 @@ __export(exports_worker, {
439
478
  AgentWorker: () => AgentWorker
440
479
  });
441
480
  module.exports = __toCommonJS(exports_worker);
442
- var import_shared3 = require("@locusai/shared");
443
481
 
444
482
  // src/core/config.ts
445
483
  var import_node_path = require("node:path");
@@ -456,7 +494,9 @@ var LOCUS_CONFIG = {
456
494
  configFile: "config.json",
457
495
  indexFile: "codebase-index.json",
458
496
  contextFile: "CLAUDE.md",
459
- artifactsDir: "artifacts"
497
+ artifactsDir: "artifacts",
498
+ documentsDir: "documents",
499
+ agentSkillsDir: ".agent/skills"
460
500
  };
461
501
  function getLocusPath(projectPath, fileName) {
462
502
  if (fileName === "contextFile") {
@@ -623,12 +663,8 @@ class ClaudeRunner {
623
663
  return null;
624
664
  }
625
665
  handleEvent(event) {
626
- const { type, delta, content_block } = event;
627
- if (type === "content_block_delta" && delta) {
628
- if (delta.type === "text_delta" && delta.text) {
629
- this.log?.(delta.text, "info");
630
- }
631
- } else if (type === "content_block_start" && content_block) {
666
+ const { type, content_block } = event;
667
+ if (type === "content_block_start" && content_block) {
632
668
  if (content_block.type === "tool_use" && content_block.name) {
633
669
  this.log?.(`
634
670
 
@@ -715,7 +751,13 @@ class CodexRunner {
715
751
  });
716
752
  }
717
753
  buildArgs(outputPath) {
718
- const args = ["exec", "--full-auto", "--output-last-message", outputPath];
754
+ const args = [
755
+ "exec",
756
+ "--full-auto",
757
+ "--skip-git-repo-check",
758
+ "--output-last-message",
759
+ outputPath
760
+ ];
719
761
  if (this.model) {
720
762
  args.push("--model", this.model);
721
763
  }
@@ -781,6 +823,7 @@ function createAiRunner(provider, config) {
781
823
  // src/agent/artifact-syncer.ts
782
824
  var import_node_fs2 = require("node:fs");
783
825
  var import_node_path4 = require("node:path");
826
+ var import_shared2 = require("@locusai/shared");
784
827
  class ArtifactSyncer {
785
828
  deps;
786
829
  constructor(deps) {
@@ -812,10 +855,13 @@ class ArtifactSyncer {
812
855
  }
813
856
  try {
814
857
  const files = import_node_fs2.readdirSync(artifactsDir);
815
- if (files.length === 0)
816
- return;
817
858
  this.deps.log(`Syncing ${files.length} artifacts to server...`, "info");
818
- const artifactsGroupId = await this.getOrCreateArtifactsGroup();
859
+ const groups = await this.deps.client.docs.listGroups(this.deps.workspaceId);
860
+ const groupMap = new Map(groups.map((g) => [g.id, g.name]));
861
+ let artifactsGroupId = groups.find((g) => g.name === "Artifacts")?.id;
862
+ if (!artifactsGroupId) {
863
+ artifactsGroupId = await this.getOrCreateArtifactsGroup();
864
+ }
819
865
  const existingDocs = await this.deps.client.docs.list(this.deps.workspaceId);
820
866
  for (const file of files) {
821
867
  const filePath = import_node_path4.join(artifactsDir, file);
@@ -834,12 +880,35 @@ class ArtifactSyncer {
834
880
  await this.deps.client.docs.create(this.deps.workspaceId, {
835
881
  title,
836
882
  content,
837
- groupId: artifactsGroupId
883
+ groupId: artifactsGroupId,
884
+ type: import_shared2.DocType.GENERAL
838
885
  });
839
886
  this.deps.log(`Created artifact: ${file}`, "success");
840
887
  }
841
888
  }
842
889
  }
890
+ for (const doc of existingDocs) {
891
+ if (doc.groupId === artifactsGroupId) {
892
+ const fileName = `${doc.title}.md`;
893
+ const filePath = import_node_path4.join(artifactsDir, fileName);
894
+ if (!import_node_fs2.existsSync(filePath)) {
895
+ import_node_fs2.writeFileSync(filePath, doc.content || "");
896
+ this.deps.log(`Fetched artifact: ${fileName}`, "success");
897
+ }
898
+ } else {
899
+ const documentsDir = getLocusPath(this.deps.projectPath, "documentsDir");
900
+ const groupName = groupMap.get(doc.groupId || "") || "General";
901
+ const groupDir = import_node_path4.join(documentsDir, groupName);
902
+ if (!import_node_fs2.existsSync(groupDir)) {
903
+ import_node_fs2.mkdirSync(groupDir, { recursive: true });
904
+ }
905
+ const fileName = `${doc.title}.md`;
906
+ const filePath = import_node_path4.join(groupDir, fileName);
907
+ if (!import_node_fs2.existsSync(filePath) || import_node_fs2.readFileSync(filePath, "utf-8") !== doc.content) {
908
+ import_node_fs2.writeFileSync(filePath, doc.content || "");
909
+ }
910
+ }
911
+ }
843
912
  } catch (error) {
844
913
  this.deps.log(`Failed to sync artifacts: ${error}`, "error");
845
914
  }
@@ -1107,53 +1176,17 @@ Return ONLY valid JSON, no markdown formatting.`;
1107
1176
  }
1108
1177
  }
1109
1178
 
1110
- // src/agent/sprint-planner.ts
1111
- class SprintPlanner {
1112
- deps;
1113
- constructor(deps) {
1114
- this.deps = deps;
1115
- }
1116
- async planSprint(sprint, tasks2) {
1117
- this.deps.log(`Planning sprint: ${sprint.name}`, "info");
1118
- try {
1119
- const taskList = tasks2.map((t) => `- [${t.id}] ${t.title}: ${t.description || "No description"}`).join(`
1120
- `);
1121
- const planningPrompt = `# Sprint Planning: ${sprint.name}
1122
-
1123
- You are an expert project manager and lead engineer. You need to create a mindmap and execution plan for the following tasks in this sprint.
1124
-
1125
- ## Tasks
1126
- ${taskList}
1127
-
1128
- ## Instructions
1129
- 1. Analyze dependencies between these tasks.
1130
- 2. Prioritize them for the most efficient execution.
1131
- 3. Create a mindmap (in Markdown or Mermaid format) that visualizes the sprint structure.
1132
- 4. Output your final plan. The plan should clearly state the order of execution.
1133
-
1134
- **IMPORTANT**:
1135
- - Do NOT create any files on the filesystem during this planning phase.
1136
- - Avoid using absolute local paths (e.g., /Users/...) in your output. Use relative paths starting from the project root if necessary.
1137
- - Your output will be saved as the official sprint mindmap on the server.`;
1138
- const plan = await this.deps.aiRunner.run(planningPrompt, true);
1139
- this.deps.log("Sprint mindmap generated and posted to server.", "success");
1140
- return plan;
1141
- } catch (error) {
1142
- this.deps.log(`Sprint planning failed: ${error}`, "error");
1143
- return sprint.mindmap || "";
1144
- }
1145
- }
1146
- }
1147
-
1148
1179
  // src/core/prompt-builder.ts
1149
1180
  var import_node_fs4 = require("node:fs");
1150
- var import_shared2 = require("@locusai/shared");
1181
+ var import_node_os2 = require("node:os");
1182
+ var import_node_path6 = require("node:path");
1183
+ var import_shared3 = require("@locusai/shared");
1151
1184
  class PromptBuilder {
1152
1185
  projectPath;
1153
1186
  constructor(projectPath) {
1154
1187
  this.projectPath = projectPath;
1155
1188
  }
1156
- async build(task) {
1189
+ async build(task, options = {}) {
1157
1190
  let prompt = `# Task: ${task.title}
1158
1191
 
1159
1192
  `;
@@ -1168,18 +1201,81 @@ You are acting as a ${roleText}.
1168
1201
  ${task.description || "No description provided."}
1169
1202
 
1170
1203
  `;
1204
+ const projectConfig = this.getProjectConfig();
1205
+ if (projectConfig) {
1206
+ prompt += `## Project Metadata
1207
+ `;
1208
+ prompt += `- Version: ${projectConfig.version || "Unknown"}
1209
+ `;
1210
+ prompt += `- Created At: ${projectConfig.createdAt || "Unknown"}
1211
+
1212
+ `;
1213
+ }
1214
+ let serverContext = null;
1215
+ if (options.taskContext) {
1216
+ try {
1217
+ serverContext = JSON.parse(options.taskContext);
1218
+ } catch {
1219
+ serverContext = { context: options.taskContext };
1220
+ }
1221
+ }
1171
1222
  const contextPath = getLocusPath(this.projectPath, "contextFile");
1223
+ let hasLocalContext = false;
1172
1224
  if (import_node_fs4.existsSync(contextPath)) {
1173
1225
  try {
1174
1226
  const context = import_node_fs4.readFileSync(contextPath, "utf-8");
1175
- prompt += `## Project Context (from CLAUDE.md)
1227
+ if (context.trim().length > 20) {
1228
+ prompt += `## Project Context (Local)
1176
1229
  ${context}
1177
1230
 
1178
1231
  `;
1232
+ hasLocalContext = true;
1233
+ }
1179
1234
  } catch (err) {
1180
1235
  console.warn(`Warning: Could not read context file: ${err}`);
1181
1236
  }
1182
1237
  }
1238
+ if (!hasLocalContext) {
1239
+ const fallback = this.getFallbackContext();
1240
+ if (fallback) {
1241
+ prompt += `## Project Context (README Fallback)
1242
+ ${fallback}
1243
+
1244
+ `;
1245
+ }
1246
+ }
1247
+ if (serverContext) {
1248
+ prompt += `## Project Context (Server)
1249
+ `;
1250
+ if (serverContext.project) {
1251
+ prompt += `- Project: ${serverContext.project.name || "Unknown"}
1252
+ `;
1253
+ if (!hasLocalContext && serverContext.project.techStack?.length) {
1254
+ prompt += `- Tech Stack: ${serverContext.project.techStack.join(", ")}
1255
+ `;
1256
+ }
1257
+ }
1258
+ if (serverContext.context) {
1259
+ prompt += `
1260
+ ${serverContext.context}
1261
+ `;
1262
+ }
1263
+ prompt += `
1264
+ `;
1265
+ }
1266
+ prompt += this.getProjectStructure();
1267
+ prompt += this.getSkillsInfo();
1268
+ prompt += `## Project Knowledge Base
1269
+ `;
1270
+ prompt += `You have access to the following documentation directories for context:
1271
+ `;
1272
+ prompt += `- Artifacts: \`.locus/artifacts\`
1273
+ `;
1274
+ prompt += `- Documents: \`.locus/documents\`
1275
+ `;
1276
+ prompt += `If you need more information about the project strategies, plans, or architecture, please read files in these directories.
1277
+
1278
+ `;
1183
1279
  const indexPath = getLocusPath(this.projectPath, "indexFile");
1184
1280
  if (import_node_fs4.existsSync(indexPath)) {
1185
1281
  prompt += `## Codebase Overview
@@ -1188,11 +1284,19 @@ There is an index file in the .locus/codebase-index.json and if you need you can
1188
1284
  `;
1189
1285
  }
1190
1286
  if (task.docs && task.docs.length > 0) {
1191
- prompt += `## Attached Documents
1287
+ prompt += `## Attached Documents (Summarized)
1288
+ `;
1289
+ prompt += `> Full content available on server. Rely on Task Description for specific requirements.
1290
+
1192
1291
  `;
1193
1292
  for (const doc of task.docs) {
1194
- prompt += `### ${doc.title}
1195
- ${doc.content || "(No content)"}
1293
+ const content = doc.content || "";
1294
+ const limit = 800;
1295
+ const preview = content.slice(0, limit);
1296
+ const isTruncated = content.length > limit;
1297
+ prompt += `### Doc: ${doc.title}
1298
+ ${preview}${isTruncated ? `
1299
+ ...(truncated)...` : ""}
1196
1300
 
1197
1301
  `;
1198
1302
  }
@@ -1208,7 +1312,7 @@ ${doc.content || "(No content)"}
1208
1312
  `;
1209
1313
  }
1210
1314
  if (task.comments && task.comments.length > 0) {
1211
- const comments = task.comments.slice(0, 5);
1315
+ const comments = task.comments.slice(0, 3);
1212
1316
  prompt += `## Task History & Feedback
1213
1317
  `;
1214
1318
  prompt += `Review the following comments for context or rejection feedback:
@@ -1230,20 +1334,122 @@ ${comment.text}
1230
1334
  `;
1231
1335
  return prompt;
1232
1336
  }
1337
+ getProjectConfig() {
1338
+ const configPath = getLocusPath(this.projectPath, "configFile");
1339
+ if (import_node_fs4.existsSync(configPath)) {
1340
+ try {
1341
+ return JSON.parse(import_node_fs4.readFileSync(configPath, "utf-8"));
1342
+ } catch {
1343
+ return null;
1344
+ }
1345
+ }
1346
+ return null;
1347
+ }
1348
+ getFallbackContext() {
1349
+ const readmePath = import_node_path6.join(this.projectPath, "README.md");
1350
+ if (import_node_fs4.existsSync(readmePath)) {
1351
+ try {
1352
+ const content = import_node_fs4.readFileSync(readmePath, "utf-8");
1353
+ const limit = 1000;
1354
+ return content.slice(0, limit) + (content.length > limit ? `
1355
+ ...(truncated)...` : "");
1356
+ } catch {
1357
+ return "";
1358
+ }
1359
+ }
1360
+ return "";
1361
+ }
1362
+ getProjectStructure() {
1363
+ try {
1364
+ const entries = import_node_fs4.readdirSync(this.projectPath);
1365
+ const folders = entries.filter((e) => {
1366
+ if (e.startsWith(".") || e === "node_modules")
1367
+ return false;
1368
+ try {
1369
+ return import_node_fs4.statSync(import_node_path6.join(this.projectPath, e)).isDirectory();
1370
+ } catch {
1371
+ return false;
1372
+ }
1373
+ });
1374
+ if (folders.length === 0)
1375
+ return "";
1376
+ let structure = `## Project Structure
1377
+ `;
1378
+ structure += `Key directories in this project:
1379
+ `;
1380
+ for (const folder of folders) {
1381
+ structure += `- \`${folder}/\`
1382
+ `;
1383
+ }
1384
+ return `${structure}
1385
+ `;
1386
+ } catch {
1387
+ return "";
1388
+ }
1389
+ }
1390
+ getSkillsInfo() {
1391
+ const projectSkillsDirs = [
1392
+ LOCUS_CONFIG.agentSkillsDir,
1393
+ ".cursor/skills",
1394
+ ".claude/skills",
1395
+ ".codex/skills",
1396
+ ".gemini/skills"
1397
+ ];
1398
+ const globalHome = import_node_os2.homedir();
1399
+ const globalSkillsDirs = [
1400
+ import_node_path6.join(globalHome, ".cursor/skills"),
1401
+ import_node_path6.join(globalHome, ".claude/skills"),
1402
+ import_node_path6.join(globalHome, ".codex/skills"),
1403
+ import_node_path6.join(globalHome, ".gemini/skills")
1404
+ ];
1405
+ const allSkillNames = new Set;
1406
+ for (const relativePath of projectSkillsDirs) {
1407
+ const fullPath = import_node_path6.join(this.projectPath, relativePath);
1408
+ this.scanSkillsInDirectory(fullPath, allSkillNames);
1409
+ }
1410
+ for (const fullPath of globalSkillsDirs) {
1411
+ this.scanSkillsInDirectory(fullPath, allSkillNames);
1412
+ }
1413
+ const uniqueSkills = Array.from(allSkillNames).sort();
1414
+ if (uniqueSkills.length === 0)
1415
+ return "";
1416
+ return `## Available Agent Skills
1417
+ ` + `The project has the following specialized skills available (from project or global locations):
1418
+ ` + uniqueSkills.map((s) => `- ${s}`).join(`
1419
+ `) + `
1420
+
1421
+ `;
1422
+ }
1423
+ scanSkillsInDirectory(dirPath, skillSet) {
1424
+ if (!import_node_fs4.existsSync(dirPath))
1425
+ return;
1426
+ try {
1427
+ const entries = import_node_fs4.readdirSync(dirPath).filter((name) => {
1428
+ try {
1429
+ return import_node_fs4.statSync(import_node_path6.join(dirPath, name)).isDirectory();
1430
+ } catch {
1431
+ return false;
1432
+ }
1433
+ });
1434
+ for (const entry of entries) {
1435
+ skillSet.add(entry);
1436
+ }
1437
+ } catch {}
1438
+ }
1233
1439
  roleToText(role) {
1234
1440
  if (!role) {
1235
1441
  return null;
1236
1442
  }
1237
1443
  switch (role) {
1238
- case import_shared2.AssigneeRole.BACKEND:
1444
+ case import_shared3.AssigneeRole.BACKEND:
1239
1445
  return "Backend Engineer";
1240
- case import_shared2.AssigneeRole.FRONTEND:
1446
+ case import_shared3.AssigneeRole.FRONTEND:
1241
1447
  return "Frontend Engineer";
1242
- case import_shared2.AssigneeRole.PM:
1448
+ case import_shared3.AssigneeRole.PM:
1243
1449
  return "Product Manager";
1244
- case import_shared2.AssigneeRole.QA:
1450
+ case import_shared3.AssigneeRole.QA:
1245
1451
  return "QA Engineer";
1246
- case import_shared2.AssigneeRole.DESIGN:
1452
+ case import_shared3.AssigneeRole.DESIGN:
1247
1453
  return "Product Designer";
1248
1454
  default:
1249
1455
  return "engineer";
@@ -1259,18 +1465,11 @@ class TaskExecutor {
1259
1465
  this.deps = deps;
1260
1466
  this.promptBuilder = new PromptBuilder(deps.projectPath);
1261
1467
  }
1262
- updateSprintPlan(sprintPlan) {
1263
- this.deps.sprintPlan = sprintPlan;
1264
- }
1265
- async execute(task) {
1468
+ async execute(task, context) {
1266
1469
  this.deps.log(`Executing: ${task.title}`, "info");
1267
- let basePrompt = await this.promptBuilder.build(task);
1268
- if (this.deps.sprintPlan) {
1269
- basePrompt = `## Sprint Context
1270
- ${this.deps.sprintPlan}
1271
-
1272
- ${basePrompt}`;
1273
- }
1470
+ const basePrompt = await this.promptBuilder.build(task, {
1471
+ taskContext: context
1472
+ });
1274
1473
  try {
1275
1474
  let plan = null;
1276
1475
  if (this.deps.skipPlanning) {
@@ -1329,16 +1528,14 @@ class AgentWorker {
1329
1528
  config;
1330
1529
  client;
1331
1530
  aiRunner;
1332
- sprintPlanner;
1333
1531
  indexerService;
1334
1532
  artifactSyncer;
1335
1533
  taskExecutor;
1336
1534
  consecutiveEmpty = 0;
1337
- maxEmpty = 5;
1535
+ maxEmpty = 60;
1338
1536
  maxTasks = 50;
1339
1537
  tasksCompleted = 0;
1340
1538
  pollInterval = 1e4;
1341
- sprintPlan = null;
1342
1539
  constructor(config) {
1343
1540
  this.config = config;
1344
1541
  const projectPath = config.projectPath || process.cwd();
@@ -1359,10 +1556,6 @@ class AgentWorker {
1359
1556
  model: config.model,
1360
1557
  log
1361
1558
  });
1362
- this.sprintPlanner = new SprintPlanner({
1363
- aiRunner: this.aiRunner,
1364
- log
1365
- });
1366
1559
  this.indexerService = new CodebaseIndexerService({
1367
1560
  aiRunner: this.aiRunner,
1368
1561
  projectPath,
@@ -1377,7 +1570,6 @@ class AgentWorker {
1377
1570
  this.taskExecutor = new TaskExecutor({
1378
1571
  aiRunner: this.aiRunner,
1379
1572
  projectPath,
1380
- sprintPlan: null,
1381
1573
  skipPlanning: config.skipPlanning,
1382
1574
  log
1383
1575
  });
@@ -1416,8 +1608,13 @@ class AgentWorker {
1416
1608
  }
1417
1609
  async executeTask(task) {
1418
1610
  const fullTask = await this.client.tasks.getById(task.id, this.config.workspaceId);
1419
- this.taskExecutor.updateSprintPlan(this.sprintPlan);
1420
- const result = await this.taskExecutor.execute(fullTask);
1611
+ let context = "";
1612
+ try {
1613
+ context = await this.client.tasks.getContext(task.id, this.config.workspaceId);
1614
+ } catch (err) {
1615
+ this.log(`Failed to fetch task context: ${err}`, "warn");
1616
+ }
1617
+ const result = await this.taskExecutor.execute(fullTask, context);
1421
1618
  await this.indexerService.reindex();
1422
1619
  return result;
1423
1620
  }
@@ -1425,35 +1622,12 @@ class AgentWorker {
1425
1622
  this.log(`Agent started in ${this.config.projectPath || process.cwd()}`, "success");
1426
1623
  const sprint = await this.getActiveSprint();
1427
1624
  if (sprint) {
1428
- this.log(`Found active sprint: ${sprint.name} (${sprint.id})`, "info");
1429
- const tasks2 = await this.client.tasks.list(this.config.workspaceId, {
1430
- sprintId: sprint.id
1431
- });
1432
- const activeTasks = tasks2.filter((t) => t.status === import_shared3.TaskStatus.BACKLOG || t.status === import_shared3.TaskStatus.IN_PROGRESS);
1433
- this.log(`Sprint tasks found: ${activeTasks.length}`, "info");
1434
- if (activeTasks.length <= 1) {
1435
- this.log("Skipping mindmap generation (only one task in sprint).", "info");
1436
- this.sprintPlan = null;
1437
- } else {
1438
- const latestTaskCreation = activeTasks.reduce((latest, task) => {
1439
- const taskDate = new Date(task.createdAt);
1440
- return taskDate > latest ? taskDate : latest;
1441
- }, new Date(0));
1442
- const mindmapDate = sprint.mindmapUpdatedAt ? new Date(sprint.mindmapUpdatedAt) : new Date(0);
1443
- const needsPlanning = !sprint.mindmap || sprint.mindmap.trim() === "" || latestTaskCreation > mindmapDate;
1444
- if (needsPlanning) {
1445
- if (sprint.mindmap && latestTaskCreation > mindmapDate) {
1446
- this.log("New tasks have been added to the sprint since last mindmap. Regenerating...", "warn");
1447
- }
1448
- this.sprintPlan = await this.sprintPlanner.planSprint(sprint, tasks2);
1449
- await this.client.sprints.update(sprint.id, this.config.workspaceId, {
1450
- mindmap: this.sprintPlan,
1451
- mindmapUpdatedAt: new Date
1452
- });
1453
- } else {
1454
- this.log("Using existing sprint mindmap.", "info");
1455
- this.sprintPlan = sprint.mindmap ?? null;
1456
- }
1625
+ this.log(`Active sprint found: ${sprint.name}. Ensuring plan is up to date...`, "info");
1626
+ try {
1627
+ await this.client.sprints.triggerAIPlanning(sprint.id, this.config.workspaceId);
1628
+ this.log(`Sprint plan sync checked on server.`, "success");
1629
+ } catch (err) {
1630
+ this.log(`Sprint planning sync failed (non-critical): ${err instanceof Error ? err.message : String(err)}`, "warn");
1457
1631
  }
1458
1632
  } else {
1459
1633
  this.log("No active sprint found for planning.", "warn");
@@ -1473,7 +1647,11 @@ class AgentWorker {
1473
1647
  this.consecutiveEmpty = 0;
1474
1648
  this.log(`Claimed: ${task.title}`, "success");
1475
1649
  const result = await this.executeTask(task);
1476
- await this.artifactSyncer.sync();
1650
+ try {
1651
+ await this.artifactSyncer.sync();
1652
+ } catch (err) {
1653
+ this.log(`Artifact sync failed: ${err}`, "error");
1654
+ }
1477
1655
  if (result.success) {
1478
1656
  this.log(`Completed: ${task.title}`, "success");
1479
1657
  await this.client.tasks.update(task.id, this.config.workspaceId, {
@@ -1510,7 +1688,7 @@ if (process.argv[1]?.includes("agent-worker") || process.argv[1]?.includes("work
1510
1688
  config.workspaceId = args[++i];
1511
1689
  else if (arg === "--sprint-id")
1512
1690
  config.sprintId = args[++i];
1513
- else if (arg === "--api-base")
1691
+ else if (arg === "--api-url")
1514
1692
  config.apiBase = args[++i];
1515
1693
  else if (arg === "--api-key")
1516
1694
  config.apiKey = args[++i];
@@ -1 +1 @@
1
- {"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAmB5C,qBAAa,YAAa,YAAW,QAAQ;IAKzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IALd,OAAO,CAAC,WAAW,CAAS;gBAG1B,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA;IAKf,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAyB/D,OAAO,CAAC,UAAU;IAoElB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,oBAAoB;CAO7B"}
1
+ {"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAmB5C,qBAAa,YAAa,YAAW,QAAQ;IAKzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IALd,OAAO,CAAC,WAAW,CAAS;gBAG1B,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA;IAKf,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAyB/D,OAAO,CAAC,UAAU;IAoElB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,oBAAoB;CAO7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"codex-runner.d.ts","sourceRoot":"","sources":["../../src/ai/codex-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,qBAAa,WAAY,YAAW,QAAQ;IAExC,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;gBAFJ,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsC,EAC7C,GAAG,CAAC,EAAE,KAAK,YAAA;IAGf,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuB1C,OAAO,CAAC,UAAU;IAoDlB,OAAO,CAAC,SAAS;IAWjB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"codex-runner.d.ts","sourceRoot":"","sources":["../../src/ai/codex-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,qBAAa,WAAY,YAAW,QAAQ;IAExC,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;gBAFJ,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsC,EAC7C,GAAG,CAAC,EAAE,KAAK,YAAA;IAGf,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuB1C,OAAO,CAAC,UAAU;IAoDlB,OAAO,CAAC,SAAS;IAiBjB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,KAAK;CAGd"}
@@ -10,6 +10,8 @@ export declare const LOCUS_CONFIG: {
10
10
  indexFile: string;
11
11
  contextFile: string;
12
12
  artifactsDir: string;
13
+ documentsDir: string;
14
+ agentSkillsDir: string;
13
15
  };
14
16
  export declare function getLocusPath(projectPath: string, fileName: keyof typeof LOCUS_CONFIG): string;
15
17
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;CAGX,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEhE,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAGlD,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;CAMxB,CAAC;AAEF,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,OAAO,YAAY,GAClC,MAAM,CAKR"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;CAGX,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEhE,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAGlD,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;;;CAQxB,CAAC;AAEF,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,OAAO,YAAY,GAClC,MAAM,CAKR"}
@@ -1,8 +1,16 @@
1
1
  import { Task } from "@locusai/shared";
2
+ export interface PromptOptions {
3
+ taskContext?: string;
4
+ }
2
5
  export declare class PromptBuilder {
3
6
  private projectPath;
4
7
  constructor(projectPath: string);
5
- build(task: Task): Promise<string>;
8
+ build(task: Task, options?: PromptOptions): Promise<string>;
9
+ private getProjectConfig;
10
+ private getFallbackContext;
11
+ private getProjectStructure;
12
+ private getSkillsInfo;
13
+ private scanSkillsInDirectory;
6
14
  roleToText(role: Task["assigneeRole"]): string | null;
7
15
  }
8
16
  //# sourceMappingURL=prompt-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-builder.d.ts","sourceRoot":"","sources":["../../src/core/prompt-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGrD,qBAAa,aAAa;IACZ,OAAO,CAAC,WAAW;gBAAX,WAAW,EAAE,MAAM;IAEjC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IA+DxC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,MAAM,GAAG,IAAI;CAoBtD"}
1
+ {"version":3,"file":"prompt-builder.d.ts","sourceRoot":"","sources":["../../src/core/prompt-builder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGrD,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,WAAW;gBAAX,WAAW,EAAE,MAAM;IAEjC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAsIrE,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,CAAC,aAAa;IAyCrB,OAAO,CAAC,qBAAqB;IAmB7B,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,MAAM,GAAG,IAAI;CAoBtD"}