@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.
- package/dist/agent/artifact-syncer.d.ts.map +1 -1
- package/dist/agent/index.d.ts +0 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/task-executor.d.ts +1 -3
- package/dist/agent/task-executor.d.ts.map +1 -1
- package/dist/agent/worker.d.ts +0 -2
- package/dist/agent/worker.d.ts.map +1 -1
- package/dist/agent/worker.js +295 -117
- package/dist/ai/claude-runner.d.ts.map +1 -1
- package/dist/ai/codex-runner.d.ts.map +1 -1
- package/dist/core/config.d.ts +2 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/prompt-builder.d.ts +9 -1
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/index-node.d.ts +1 -0
- package/dist/index-node.d.ts.map +1 -1
- package/dist/index-node.js +301 -123
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +41 -2
- package/dist/modules/ai.d.ts +32 -0
- package/dist/modules/ai.d.ts.map +1 -0
- package/dist/modules/sprints.d.ts +1 -0
- package/dist/modules/sprints.d.ts.map +1 -1
- package/dist/modules/tasks.d.ts +6 -0
- package/dist/modules/tasks.d.ts.map +1 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/agent/sprint-planner.d.ts +0 -16
- package/dist/agent/sprint-planner.d.ts.map +0 -1
package/dist/agent/worker.js
CHANGED
|
@@ -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 ||
|
|
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,
|
|
627
|
-
if (type === "
|
|
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 = [
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1195
|
-
|
|
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,
|
|
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
|
|
1444
|
+
case import_shared3.AssigneeRole.BACKEND:
|
|
1239
1445
|
return "Backend Engineer";
|
|
1240
|
-
case
|
|
1446
|
+
case import_shared3.AssigneeRole.FRONTEND:
|
|
1241
1447
|
return "Frontend Engineer";
|
|
1242
|
-
case
|
|
1448
|
+
case import_shared3.AssigneeRole.PM:
|
|
1243
1449
|
return "Product Manager";
|
|
1244
|
-
case
|
|
1450
|
+
case import_shared3.AssigneeRole.QA:
|
|
1245
1451
|
return "QA Engineer";
|
|
1246
|
-
case
|
|
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
|
-
|
|
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
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
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 =
|
|
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
|
-
|
|
1420
|
-
|
|
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(`
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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
|
-
|
|
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-
|
|
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;
|
|
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;
|
|
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"}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -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
|
|
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":"
|
|
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"}
|