@locusai/sdk 0.2.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.
Files changed (105) hide show
  1. package/dist/agent/artifact-syncer.d.ts +17 -0
  2. package/dist/agent/artifact-syncer.d.ts.map +1 -0
  3. package/dist/agent/artifact-syncer.js +77 -0
  4. package/dist/agent/codebase-indexer-service.d.ts +18 -0
  5. package/dist/agent/codebase-indexer-service.d.ts.map +1 -0
  6. package/dist/agent/codebase-indexer-service.js +55 -0
  7. package/dist/agent/index.d.ts +6 -0
  8. package/dist/agent/index.d.ts.map +1 -0
  9. package/dist/agent/index.js +5 -0
  10. package/dist/agent/sprint-planner.d.ts +17 -0
  11. package/dist/agent/sprint-planner.d.ts.map +1 -0
  12. package/dist/agent/sprint-planner.js +62 -0
  13. package/dist/agent/task-executor.d.ts +24 -0
  14. package/dist/agent/task-executor.d.ts.map +1 -0
  15. package/dist/agent/task-executor.js +56 -0
  16. package/dist/agent/worker.d.ts +37 -0
  17. package/dist/agent/worker.d.ts.map +1 -0
  18. package/dist/agent/worker.js +232 -0
  19. package/dist/ai/anthropic-client.d.ts +33 -0
  20. package/dist/ai/anthropic-client.d.ts.map +1 -0
  21. package/dist/ai/anthropic-client.js +70 -0
  22. package/dist/ai/claude-runner.d.ts +7 -0
  23. package/dist/ai/claude-runner.d.ts.map +1 -0
  24. package/dist/ai/claude-runner.js +43 -0
  25. package/dist/ai/index.d.ts +3 -0
  26. package/dist/ai/index.d.ts.map +1 -0
  27. package/dist/ai/index.js +2 -0
  28. package/dist/core/config.d.ts +10 -0
  29. package/dist/core/config.d.ts.map +1 -0
  30. package/dist/core/config.js +15 -0
  31. package/dist/core/index.d.ts +4 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/index.js +3 -0
  34. package/dist/core/indexer.d.ts +18 -0
  35. package/dist/core/indexer.d.ts.map +1 -0
  36. package/dist/core/indexer.js +73 -0
  37. package/dist/core/prompt-builder.d.ts +8 -0
  38. package/dist/core/prompt-builder.d.ts.map +1 -0
  39. package/dist/core/prompt-builder.js +87 -0
  40. package/dist/events.d.ts +20 -0
  41. package/dist/events.d.ts.map +1 -0
  42. package/dist/events.js +15 -0
  43. package/dist/index-node.d.ts +14 -0
  44. package/dist/index-node.d.ts.map +1 -0
  45. package/dist/index-node.js +18 -0
  46. package/dist/index.d.ts +34 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +101 -0
  49. package/dist/modules/auth.d.ts +14 -0
  50. package/dist/modules/auth.d.ts.map +1 -0
  51. package/dist/modules/auth.js +23 -0
  52. package/dist/modules/base.d.ts +8 -0
  53. package/dist/modules/base.d.ts.map +1 -0
  54. package/dist/modules/base.js +8 -0
  55. package/dist/modules/ci.d.ts +8 -0
  56. package/dist/modules/ci.d.ts.map +1 -0
  57. package/dist/modules/ci.js +7 -0
  58. package/dist/modules/docs.d.ts +14 -0
  59. package/dist/modules/docs.d.ts.map +1 -0
  60. package/dist/modules/docs.js +38 -0
  61. package/dist/modules/invitations.d.ts +10 -0
  62. package/dist/modules/invitations.d.ts.map +1 -0
  63. package/dist/modules/invitations.js +22 -0
  64. package/dist/modules/organizations.d.ts +24 -0
  65. package/dist/modules/organizations.d.ts.map +1 -0
  66. package/dist/modules/organizations.js +39 -0
  67. package/dist/modules/sprints.d.ts +13 -0
  68. package/dist/modules/sprints.d.ts.map +1 -0
  69. package/dist/modules/sprints.js +34 -0
  70. package/dist/modules/tasks.d.ts +24 -0
  71. package/dist/modules/tasks.d.ts.map +1 -0
  72. package/dist/modules/tasks.js +56 -0
  73. package/dist/modules/workspaces.d.ts +21 -0
  74. package/dist/modules/workspaces.d.ts.map +1 -0
  75. package/dist/modules/workspaces.js +49 -0
  76. package/dist/orchestrator.d.ts +90 -0
  77. package/dist/orchestrator.d.ts.map +1 -0
  78. package/dist/orchestrator.js +326 -0
  79. package/package.json +53 -0
  80. package/src/agent/artifact-syncer.ts +111 -0
  81. package/src/agent/codebase-indexer-service.ts +71 -0
  82. package/src/agent/index.ts +5 -0
  83. package/src/agent/sprint-planner.ts +78 -0
  84. package/src/agent/task-executor.ts +77 -0
  85. package/src/agent/worker.ts +299 -0
  86. package/src/ai/anthropic-client.ts +93 -0
  87. package/src/ai/claude-runner.ts +49 -0
  88. package/src/ai/index.ts +2 -0
  89. package/src/core/config.ts +21 -0
  90. package/src/core/index.ts +3 -0
  91. package/src/core/indexer.ts +91 -0
  92. package/src/core/prompt-builder.ts +100 -0
  93. package/src/events.ts +32 -0
  94. package/src/index-node.ts +20 -0
  95. package/src/index.ts +119 -0
  96. package/src/modules/auth.ts +48 -0
  97. package/src/modules/base.ts +9 -0
  98. package/src/modules/ci.ts +12 -0
  99. package/src/modules/docs.ts +84 -0
  100. package/src/modules/invitations.ts +45 -0
  101. package/src/modules/organizations.ts +90 -0
  102. package/src/modules/sprints.ts +69 -0
  103. package/src/modules/tasks.ts +110 -0
  104. package/src/modules/workspaces.ts +94 -0
  105. package/src/orchestrator.ts +430 -0
@@ -0,0 +1,17 @@
1
+ import type { LocusClient } from "../index";
2
+ export interface ArtifactSyncerDeps {
3
+ client: LocusClient;
4
+ workspaceId: string;
5
+ projectPath: string;
6
+ log: (message: string, level?: "info" | "success" | "warn" | "error") => void;
7
+ }
8
+ /**
9
+ * Handles syncing local artifacts to the platform
10
+ */
11
+ export declare class ArtifactSyncer {
12
+ private deps;
13
+ constructor(deps: ArtifactSyncerDeps);
14
+ private getOrCreateArtifactsGroup;
15
+ sync(): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=artifact-syncer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-syncer.d.ts","sourceRoot":"","sources":["../../src/agent/artifact-syncer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;CAC/E;AAED;;GAEG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;YAE9B,yBAAyB;IA8BjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAwD5B"}
@@ -0,0 +1,77 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { getLocusPath } from "../core/config";
4
+ /**
5
+ * Handles syncing local artifacts to the platform
6
+ */
7
+ export class ArtifactSyncer {
8
+ deps;
9
+ constructor(deps) {
10
+ this.deps = deps;
11
+ }
12
+ async getOrCreateArtifactsGroup() {
13
+ try {
14
+ const groups = await this.deps.client.docs.listGroups(this.deps.workspaceId);
15
+ const artifactsGroup = groups.find((g) => g.name === "Artifacts");
16
+ if (artifactsGroup) {
17
+ return artifactsGroup.id;
18
+ }
19
+ // Create the Artifacts group
20
+ const newGroup = await this.deps.client.docs.createGroup(this.deps.workspaceId, {
21
+ name: "Artifacts",
22
+ order: 999, // Place at the end
23
+ });
24
+ this.deps.log("Created 'Artifacts' group for agent-generated docs", "info");
25
+ return newGroup.id;
26
+ }
27
+ catch (error) {
28
+ this.deps.log(`Failed to get/create Artifacts group: ${error}`, "error");
29
+ throw error;
30
+ }
31
+ }
32
+ async sync() {
33
+ const artifactsDir = getLocusPath(this.deps.projectPath, "artifactsDir");
34
+ if (!existsSync(artifactsDir)) {
35
+ mkdirSync(artifactsDir, { recursive: true });
36
+ return;
37
+ }
38
+ try {
39
+ const files = readdirSync(artifactsDir);
40
+ if (files.length === 0)
41
+ return;
42
+ this.deps.log(`Syncing ${files.length} artifacts to server...`, "info");
43
+ // Get or create the Artifacts group
44
+ const artifactsGroupId = await this.getOrCreateArtifactsGroup();
45
+ // Get existing docs to check for updates
46
+ const existingDocs = await this.deps.client.docs.list(this.deps.workspaceId);
47
+ for (const file of files) {
48
+ const filePath = join(artifactsDir, file);
49
+ if (statSync(filePath).isFile()) {
50
+ const content = readFileSync(filePath, "utf-8");
51
+ const title = file.replace(/\.md$/, "").trim();
52
+ if (!title)
53
+ continue;
54
+ const existing = existingDocs.find((d) => d.title === title);
55
+ if (existing) {
56
+ if (existing.content !== content ||
57
+ existing.groupId !== artifactsGroupId) {
58
+ await this.deps.client.docs.update(existing.id, this.deps.workspaceId, { content, groupId: artifactsGroupId });
59
+ this.deps.log(`Updated artifact: ${file}`, "success");
60
+ }
61
+ }
62
+ else {
63
+ await this.deps.client.docs.create(this.deps.workspaceId, {
64
+ title,
65
+ content,
66
+ groupId: artifactsGroupId,
67
+ });
68
+ this.deps.log(`Created artifact: ${file}`, "success");
69
+ }
70
+ }
71
+ }
72
+ }
73
+ catch (error) {
74
+ this.deps.log(`Failed to sync artifacts: ${error}`, "error");
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,18 @@
1
+ import type { AnthropicClient } from "../ai/anthropic-client";
2
+ import type { ClaudeRunner } from "../ai/claude-runner";
3
+ export interface CodebaseIndexerServiceDeps {
4
+ anthropicClient: AnthropicClient | null;
5
+ claudeRunner: ClaudeRunner;
6
+ projectPath: string;
7
+ log: (message: string, level?: "info" | "success" | "warn" | "error") => void;
8
+ }
9
+ /**
10
+ * Handles codebase indexing with AI analysis
11
+ */
12
+ export declare class CodebaseIndexerService {
13
+ private deps;
14
+ private indexer;
15
+ constructor(deps: CodebaseIndexerServiceDeps);
16
+ reindex(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=codebase-indexer-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebase-indexer-service.d.ts","sourceRoot":"","sources":["../../src/agent/codebase-indexer-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,WAAW,0BAA0B;IACzC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;CAC/E;AAED;;GAEG;AACH,qBAAa,sBAAsB;IAGrB,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,OAAO,CAAkB;gBAEb,IAAI,EAAE,0BAA0B;IAI9C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAiD/B"}
@@ -0,0 +1,55 @@
1
+ import { CodebaseIndexer } from "../core/indexer";
2
+ /**
3
+ * Handles codebase indexing with AI analysis
4
+ */
5
+ export class CodebaseIndexerService {
6
+ deps;
7
+ indexer;
8
+ constructor(deps) {
9
+ this.deps = deps;
10
+ this.indexer = new CodebaseIndexer(deps.projectPath);
11
+ }
12
+ async reindex() {
13
+ try {
14
+ this.deps.log("Reindexing codebase...", "info");
15
+ const index = await this.indexer.index((msg) => this.deps.log(msg, "info"), async (tree) => {
16
+ const prompt = `You are a codebase analysis expert. Analyze the file tree and extract:
17
+ 1. Key symbols (classes, functions, types) and their locations
18
+ 2. Responsibilities of each directory/file
19
+ 3. Overall project structure
20
+
21
+ Analyze this file tree and provide a JSON response with:
22
+ - "symbols": object mapping symbol names to file paths (array)
23
+ - "responsibilities": object mapping paths to brief descriptions
24
+
25
+ File tree:
26
+ ${tree}
27
+
28
+ Return ONLY valid JSON, no markdown formatting.`;
29
+ let response;
30
+ if (this.deps.anthropicClient) {
31
+ // Use Anthropic SDK with caching for faster indexing
32
+ response = await this.deps.anthropicClient.run({
33
+ systemPrompt: "You are a codebase analysis expert specialized in extracting structure and symbols from file trees.",
34
+ userPrompt: prompt,
35
+ });
36
+ }
37
+ else {
38
+ // Fallback to Claude CLI
39
+ response = await this.deps.claudeRunner.run(prompt, true);
40
+ }
41
+ // Extract JSON from response (handle markdown code blocks)
42
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
43
+ if (jsonMatch) {
44
+ return JSON.parse(jsonMatch[0]);
45
+ }
46
+ return { symbols: {}, responsibilities: {}, lastIndexed: "" };
47
+ });
48
+ this.indexer.saveIndex(index);
49
+ this.deps.log("Codebase reindexed successfully", "success");
50
+ }
51
+ catch (error) {
52
+ this.deps.log(`Failed to reindex codebase: ${error}`, "error");
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,6 @@
1
+ export { ArtifactSyncer } from "./artifact-syncer";
2
+ export { CodebaseIndexerService } from "./codebase-indexer-service";
3
+ export { SprintPlanner } from "./sprint-planner";
4
+ export { TaskExecutor } from "./task-executor";
5
+ export { AgentWorker, type WorkerConfig } from "./worker";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { ArtifactSyncer } from "./artifact-syncer";
2
+ export { CodebaseIndexerService } from "./codebase-indexer-service";
3
+ export { SprintPlanner } from "./sprint-planner";
4
+ export { TaskExecutor } from "./task-executor";
5
+ export { AgentWorker } from "./worker";
@@ -0,0 +1,17 @@
1
+ import type { Sprint, Task } from "@locusai/shared";
2
+ import type { AnthropicClient } from "../ai/anthropic-client";
3
+ import type { ClaudeRunner } from "../ai/claude-runner";
4
+ export interface SprintPlannerDeps {
5
+ anthropicClient: AnthropicClient | null;
6
+ claudeRunner: ClaudeRunner;
7
+ log: (message: string, level?: "info" | "success" | "warn" | "error") => void;
8
+ }
9
+ /**
10
+ * Handles sprint planning and mindmap generation
11
+ */
12
+ export declare class SprintPlanner {
13
+ private deps;
14
+ constructor(deps: SprintPlannerDeps);
15
+ planSprint(sprint: Sprint, tasks: Task[]): Promise<string>;
16
+ }
17
+ //# sourceMappingURL=sprint-planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprint-planner.d.ts","sourceRoot":"","sources":["../../src/agent/sprint-planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;CAC/E;AAED;;GAEG;AACH,qBAAa,aAAa;IACZ,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,iBAAiB;IAErC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CA6DjE"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Handles sprint planning and mindmap generation
3
+ */
4
+ export class SprintPlanner {
5
+ deps;
6
+ constructor(deps) {
7
+ this.deps = deps;
8
+ }
9
+ async planSprint(sprint, tasks) {
10
+ this.deps.log(`Planning sprint: ${sprint.name}`, "info");
11
+ const taskList = tasks
12
+ .map((t) => `- [${t.id}] ${t.title}: ${t.description || "No description"}`)
13
+ .join("\n");
14
+ let plan;
15
+ if (this.deps.anthropicClient) {
16
+ // Use Anthropic SDK with caching for faster planning
17
+ const systemPrompt = `You are an expert project manager and lead engineer specialized in sprint planning and task prioritization.`;
18
+ const userPrompt = `# Sprint Planning: ${sprint.name}
19
+
20
+ ## Tasks
21
+ ${taskList}
22
+
23
+ ## Instructions
24
+ 1. Analyze dependencies between these tasks.
25
+ 2. Prioritize them for the most efficient execution.
26
+ 3. Create a mindmap (in Markdown or Mermaid format) that visualizes the sprint structure.
27
+ 4. Output your final plan. The plan should clearly state the order of execution.
28
+
29
+ **IMPORTANT**:
30
+ - Do NOT create any files on the filesystem during this planning phase.
31
+ - Avoid using absolute local paths (e.g., /Users/...) in your output. Use relative paths starting from the project root if necessary.
32
+ - Your output will be saved as the official sprint mindmap on the server.`;
33
+ plan = await this.deps.anthropicClient.run({
34
+ systemPrompt,
35
+ userPrompt,
36
+ });
37
+ }
38
+ else {
39
+ // Fallback to Claude CLI
40
+ const planningPrompt = `# Sprint Planning: ${sprint.name}
41
+
42
+ 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.
43
+
44
+ ## Tasks
45
+ ${taskList}
46
+
47
+ ## Instructions
48
+ 1. Analyze dependencies between these tasks.
49
+ 2. Prioritize them for the most efficient execution.
50
+ 3. Create a mindmap (in Markdown or Mermaid format) that visualizes the sprint structure.
51
+ 4. Output your final plan. The plan should clearly state the order of execution.
52
+
53
+ **IMPORTANT**:
54
+ - Do NOT create any files on the filesystem during this planning phase.
55
+ - Avoid using absolute local paths (e.g., /Users/...) in your output. Use relative paths starting from the project root if necessary.
56
+ - Your output will be saved as the official sprint mindmap on the server.`;
57
+ plan = await this.deps.claudeRunner.run(planningPrompt, true);
58
+ }
59
+ this.deps.log("Sprint mindmap generated and posted to server.", "success");
60
+ return plan;
61
+ }
62
+ }
@@ -0,0 +1,24 @@
1
+ import type { Task } from "@locusai/shared";
2
+ import type { AnthropicClient } from "../ai/anthropic-client";
3
+ import type { ClaudeRunner } from "../ai/claude-runner";
4
+ export interface TaskExecutorDeps {
5
+ anthropicClient: AnthropicClient | null;
6
+ claudeRunner: ClaudeRunner;
7
+ projectPath: string;
8
+ sprintPlan: string | null;
9
+ log: (message: string, level?: "info" | "success" | "warn" | "error") => void;
10
+ }
11
+ /**
12
+ * Handles task execution with two-phase approach (planning + execution)
13
+ */
14
+ export declare class TaskExecutor {
15
+ private deps;
16
+ private promptBuilder;
17
+ constructor(deps: TaskExecutorDeps);
18
+ updateSprintPlan(sprintPlan: string | null): void;
19
+ execute(task: Task): Promise<{
20
+ success: boolean;
21
+ summary: string;
22
+ }>;
23
+ }
24
+ //# sourceMappingURL=task-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/agent/task-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;CAC/E;AAED;;GAEG;AACH,qBAAa,YAAY;IAGX,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,aAAa,CAAgB;gBAEjB,IAAI,EAAE,gBAAgB;IAI1C,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIpC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAiD1E"}
@@ -0,0 +1,56 @@
1
+ import { PromptBuilder } from "../core/prompt-builder";
2
+ /**
3
+ * Handles task execution with two-phase approach (planning + execution)
4
+ */
5
+ export class TaskExecutor {
6
+ deps;
7
+ promptBuilder;
8
+ constructor(deps) {
9
+ this.deps = deps;
10
+ this.promptBuilder = new PromptBuilder(deps.projectPath);
11
+ }
12
+ updateSprintPlan(sprintPlan) {
13
+ this.deps.sprintPlan = sprintPlan;
14
+ }
15
+ async execute(task) {
16
+ this.deps.log(`Executing: ${task.title}`, "info");
17
+ let basePrompt = await this.promptBuilder.build(task);
18
+ if (this.deps.sprintPlan) {
19
+ basePrompt = `## Sprint Context\n${this.deps.sprintPlan}\n\n${basePrompt}`;
20
+ }
21
+ try {
22
+ let plan;
23
+ if (this.deps.anthropicClient) {
24
+ // Phase 1: Planning (using Anthropic SDK with caching)
25
+ this.deps.log("Phase 1: Planning (Anthropic SDK)...", "info");
26
+ // Build cacheable context blocks
27
+ const cacheableContext = [basePrompt];
28
+ plan = await this.deps.anthropicClient.run({
29
+ systemPrompt: "You are an expert software engineer. Analyze the task carefully and create a detailed implementation plan.",
30
+ cacheableContext,
31
+ userPrompt: "## Phase 1: Planning\nAnalyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.",
32
+ });
33
+ }
34
+ else {
35
+ // Phase 1: Planning (using Claude CLI)
36
+ this.deps.log("Phase 1: Planning (Claude CLI)...", "info");
37
+ const planningPrompt = `${basePrompt}\n\n## Phase 1: Planning\nAnalyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
38
+ plan = await this.deps.claudeRunner.run(planningPrompt, true);
39
+ }
40
+ // Phase 2: Execution (always using Claude CLI for agentic tools)
41
+ this.deps.log("Plan generated. Starting Phase 2: Execution...", "info");
42
+ const executionPrompt = `${basePrompt}\n\n## Phase 2: Execution\nBased on the plan, execute the task:\n\n${plan}\n\nWhen finished, output: <promise>COMPLETE</promise>`;
43
+ const output = await this.deps.claudeRunner.run(executionPrompt);
44
+ const success = output.includes("<promise>COMPLETE</promise>");
45
+ return {
46
+ success,
47
+ summary: success
48
+ ? "Task completed by Claude"
49
+ : "Claude did not signal completion",
50
+ };
51
+ }
52
+ catch (error) {
53
+ return { success: false, summary: `Error: ${error}` };
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,37 @@
1
+ export interface WorkerConfig {
2
+ agentId: string;
3
+ workspaceId: string;
4
+ sprintId?: string;
5
+ apiBase: string;
6
+ projectPath: string;
7
+ apiKey: string;
8
+ anthropicApiKey?: string;
9
+ model?: string;
10
+ }
11
+ /**
12
+ * Main agent worker that orchestrates task execution
13
+ * Delegates responsibilities to specialized services
14
+ */
15
+ export declare class AgentWorker {
16
+ private config;
17
+ private client;
18
+ private claudeRunner;
19
+ private anthropicClient;
20
+ private sprintPlanner;
21
+ private indexerService;
22
+ private artifactSyncer;
23
+ private taskExecutor;
24
+ private consecutiveEmpty;
25
+ private maxEmpty;
26
+ private maxTasks;
27
+ private tasksCompleted;
28
+ private pollInterval;
29
+ private sprintPlan;
30
+ constructor(config: WorkerConfig);
31
+ log(message: string, level?: "info" | "success" | "warn" | "error"): void;
32
+ private getActiveSprint;
33
+ private getNextTask;
34
+ private executeTask;
35
+ run(): Promise<void>;
36
+ }
37
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agent/worker.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,WAAW;IAmBV,OAAO,CAAC,MAAM;IAlB1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,eAAe,CAAyB;IAGhD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IAGnC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,UAAU,CAAuB;gBAErB,MAAM,EAAE,YAAY;IA+DxC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAgB;YAQ5D,eAAe;YAcf,WAAW;YAiBX,WAAW;IAqBnB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA+F3B"}
@@ -0,0 +1,232 @@
1
+ import { AnthropicClient } from "../ai/anthropic-client";
2
+ import { ClaudeRunner } from "../ai/claude-runner";
3
+ import { LocusClient } from "../index";
4
+ import { ArtifactSyncer } from "./artifact-syncer";
5
+ import { CodebaseIndexerService } from "./codebase-indexer-service";
6
+ import { SprintPlanner } from "./sprint-planner";
7
+ import { TaskExecutor } from "./task-executor";
8
+ /**
9
+ * Main agent worker that orchestrates task execution
10
+ * Delegates responsibilities to specialized services
11
+ */
12
+ export class AgentWorker {
13
+ config;
14
+ client;
15
+ claudeRunner;
16
+ anthropicClient;
17
+ // Services
18
+ sprintPlanner;
19
+ indexerService;
20
+ artifactSyncer;
21
+ taskExecutor;
22
+ // State
23
+ consecutiveEmpty = 0;
24
+ maxEmpty = 10;
25
+ maxTasks = 50;
26
+ tasksCompleted = 0;
27
+ pollInterval = 10_000;
28
+ sprintPlan = null;
29
+ constructor(config) {
30
+ this.config = config;
31
+ const projectPath = config.projectPath || process.cwd();
32
+ // Initialize API client
33
+ this.client = new LocusClient({
34
+ baseUrl: config.apiBase,
35
+ token: config.apiKey,
36
+ });
37
+ // Initialize AI clients
38
+ this.claudeRunner = new ClaudeRunner(projectPath, config.model);
39
+ this.anthropicClient = config.anthropicApiKey
40
+ ? new AnthropicClient({
41
+ apiKey: config.anthropicApiKey,
42
+ model: config.model,
43
+ })
44
+ : null;
45
+ // Initialize services with dependencies
46
+ const logFn = this.log.bind(this);
47
+ this.sprintPlanner = new SprintPlanner({
48
+ anthropicClient: this.anthropicClient,
49
+ claudeRunner: this.claudeRunner,
50
+ log: logFn,
51
+ });
52
+ this.indexerService = new CodebaseIndexerService({
53
+ anthropicClient: this.anthropicClient,
54
+ claudeRunner: this.claudeRunner,
55
+ projectPath,
56
+ log: logFn,
57
+ });
58
+ this.artifactSyncer = new ArtifactSyncer({
59
+ client: this.client,
60
+ workspaceId: config.workspaceId,
61
+ projectPath,
62
+ log: logFn,
63
+ });
64
+ this.taskExecutor = new TaskExecutor({
65
+ anthropicClient: this.anthropicClient,
66
+ claudeRunner: this.claudeRunner,
67
+ projectPath,
68
+ sprintPlan: null, // Will be set after sprint planning
69
+ log: logFn,
70
+ });
71
+ // Log initialization
72
+ if (this.anthropicClient) {
73
+ this.log("Using Anthropic SDK with prompt caching for planning phases", "info");
74
+ }
75
+ else {
76
+ this.log("Using Claude CLI for all phases (no Anthropic API key provided)", "info");
77
+ }
78
+ }
79
+ log(message, level = "info") {
80
+ const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
81
+ const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
82
+ console.log(`[${timestamp}] [${this.config.agentId.slice(-8)}] ${prefix} ${message}`);
83
+ }
84
+ async getActiveSprint() {
85
+ try {
86
+ if (this.config.sprintId) {
87
+ return await this.client.sprints.getById(this.config.sprintId, this.config.workspaceId);
88
+ }
89
+ return await this.client.sprints.getActive(this.config.workspaceId);
90
+ }
91
+ catch (_error) {
92
+ return null;
93
+ }
94
+ }
95
+ async getNextTask() {
96
+ try {
97
+ const task = await this.client.workspaces.dispatch(this.config.workspaceId, this.config.agentId, this.config.sprintId);
98
+ return task;
99
+ }
100
+ catch (error) {
101
+ this.log(`No task dispatched: ${error instanceof Error ? error.message : String(error)}`, "info");
102
+ return null;
103
+ }
104
+ }
105
+ async executeTask(task) {
106
+ // Reindex codebase before execution to ensure fresh context
107
+ await this.indexerService.reindex();
108
+ // Fetch full task details to get comments/feedback
109
+ const fullTask = await this.client.tasks.getById(task.id, this.config.workspaceId);
110
+ // Update task executor with current sprint plan
111
+ this.taskExecutor.updateSprintPlan(this.sprintPlan);
112
+ // Execute the task
113
+ const result = await this.taskExecutor.execute(fullTask);
114
+ return result;
115
+ }
116
+ async run() {
117
+ this.log(`Agent started in ${this.config.projectPath || process.cwd()}`, "success");
118
+ // Initial Sprint Planning Phase
119
+ const sprint = await this.getActiveSprint();
120
+ if (sprint) {
121
+ this.log(`Found active sprint: ${sprint.name} (${sprint.id})`, "info");
122
+ const tasks = await this.client.tasks.list(this.config.workspaceId, {
123
+ sprintId: sprint.id,
124
+ });
125
+ this.log(`Sprint tasks found: ${tasks.length}`, "info");
126
+ const latestTaskCreation = tasks.reduce((latest, task) => {
127
+ const taskDate = new Date(task.createdAt);
128
+ return taskDate > latest ? taskDate : latest;
129
+ }, new Date(0));
130
+ const mindmapDate = sprint.mindmapUpdatedAt
131
+ ? new Date(sprint.mindmapUpdatedAt)
132
+ : new Date(0);
133
+ const needsPlanning = !sprint.mindmap ||
134
+ sprint.mindmap.trim() === "" ||
135
+ latestTaskCreation > mindmapDate;
136
+ if (needsPlanning) {
137
+ if (sprint.mindmap && latestTaskCreation > mindmapDate) {
138
+ this.log("New tasks have been added to the sprint since last mindmap. Regenerating...", "warn");
139
+ }
140
+ this.sprintPlan = await this.sprintPlanner.planSprint(sprint, tasks);
141
+ // Save mindmap to server
142
+ await this.client.sprints.update(sprint.id, this.config.workspaceId, {
143
+ mindmap: this.sprintPlan,
144
+ mindmapUpdatedAt: new Date(),
145
+ });
146
+ }
147
+ else {
148
+ this.log("Using existing sprint mindmap.", "info");
149
+ this.sprintPlan = sprint.mindmap ?? null;
150
+ }
151
+ }
152
+ else {
153
+ this.log("No active sprint found for planning.", "warn");
154
+ }
155
+ // Main task execution loop
156
+ while (this.tasksCompleted < this.maxTasks &&
157
+ this.consecutiveEmpty < this.maxEmpty) {
158
+ const task = await this.getNextTask();
159
+ if (!task) {
160
+ this.consecutiveEmpty++;
161
+ if (this.consecutiveEmpty >= this.maxEmpty)
162
+ break;
163
+ await new Promise((r) => setTimeout(r, this.pollInterval));
164
+ continue;
165
+ }
166
+ this.consecutiveEmpty = 0;
167
+ this.log(`Claimed: ${task.title}`, "success");
168
+ const result = await this.executeTask(task);
169
+ // Sync artifacts after task execution
170
+ await this.artifactSyncer.sync();
171
+ if (result.success) {
172
+ await this.client.tasks.update(task.id, this.config.workspaceId, {
173
+ status: "VERIFICATION",
174
+ });
175
+ await this.client.tasks.addComment(task.id, this.config.workspaceId, {
176
+ author: this.config.agentId,
177
+ text: `✅ ${result.summary}`,
178
+ });
179
+ this.tasksCompleted++;
180
+ }
181
+ else {
182
+ await this.client.tasks.update(task.id, this.config.workspaceId, {
183
+ status: "BACKLOG",
184
+ assignedTo: null,
185
+ });
186
+ await this.client.tasks.addComment(task.id, this.config.workspaceId, {
187
+ author: this.config.agentId,
188
+ text: `❌ ${result.summary}`,
189
+ });
190
+ }
191
+ }
192
+ process.exit(0);
193
+ }
194
+ }
195
+ // CLI entry point
196
+ if (process.argv[1]?.includes("agent-worker") ||
197
+ process.argv[1]?.includes("worker")) {
198
+ const args = process.argv.slice(2);
199
+ const config = {};
200
+ for (let i = 0; i < args.length; i++) {
201
+ const arg = args[i];
202
+ if (arg === "--agent-id")
203
+ config.agentId = args[++i];
204
+ else if (arg === "--workspace-id")
205
+ config.workspaceId = args[++i];
206
+ else if (arg === "--sprint-id")
207
+ config.sprintId = args[++i];
208
+ else if (arg === "--api-base")
209
+ config.apiBase = args[++i];
210
+ else if (arg === "--api-key")
211
+ config.apiKey = args[++i];
212
+ else if (arg === "--anthropic-api-key")
213
+ config.anthropicApiKey = args[++i];
214
+ else if (arg === "--project-path")
215
+ config.projectPath = args[++i];
216
+ else if (arg === "--model")
217
+ config.model = args[++i];
218
+ }
219
+ if (!config.agentId ||
220
+ !config.workspaceId ||
221
+ !config.apiBase ||
222
+ !config.apiKey ||
223
+ !config.projectPath) {
224
+ console.error("Missing required arguments");
225
+ process.exit(1);
226
+ }
227
+ const worker = new AgentWorker(config);
228
+ worker.run().catch((err) => {
229
+ console.error("Fatal worker error:", err);
230
+ process.exit(1);
231
+ });
232
+ }