@locusai/sdk 0.4.5 → 0.4.9

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 (58) hide show
  1. package/dist/index-node.js +1590 -20
  2. package/dist/index.js +429 -121
  3. package/dist/orchestrator.d.ts.map +1 -1
  4. package/package.json +12 -23
  5. package/dist/agent/artifact-syncer.js +0 -77
  6. package/dist/agent/codebase-indexer-service.js +0 -55
  7. package/dist/agent/index.js +0 -5
  8. package/dist/agent/sprint-planner.js +0 -68
  9. package/dist/agent/task-executor.js +0 -60
  10. package/dist/agent/worker.js +0 -252
  11. package/dist/ai/anthropic-client.js +0 -70
  12. package/dist/ai/claude-runner.js +0 -71
  13. package/dist/ai/index.js +0 -2
  14. package/dist/core/config.js +0 -15
  15. package/dist/core/index.js +0 -3
  16. package/dist/core/indexer.js +0 -113
  17. package/dist/core/prompt-builder.js +0 -83
  18. package/dist/events.js +0 -15
  19. package/dist/modules/auth.js +0 -23
  20. package/dist/modules/base.js +0 -8
  21. package/dist/modules/ci.js +0 -7
  22. package/dist/modules/docs.js +0 -38
  23. package/dist/modules/invitations.js +0 -22
  24. package/dist/modules/organizations.js +0 -39
  25. package/dist/modules/sprints.js +0 -34
  26. package/dist/modules/tasks.js +0 -56
  27. package/dist/modules/workspaces.js +0 -49
  28. package/dist/orchestrator.js +0 -356
  29. package/dist/utils/colors.js +0 -54
  30. package/dist/utils/retry.js +0 -37
  31. package/src/agent/artifact-syncer.ts +0 -111
  32. package/src/agent/codebase-indexer-service.ts +0 -71
  33. package/src/agent/index.ts +0 -5
  34. package/src/agent/sprint-planner.ts +0 -86
  35. package/src/agent/task-executor.ts +0 -85
  36. package/src/agent/worker.ts +0 -322
  37. package/src/ai/anthropic-client.ts +0 -93
  38. package/src/ai/claude-runner.ts +0 -86
  39. package/src/ai/index.ts +0 -2
  40. package/src/core/config.ts +0 -21
  41. package/src/core/index.ts +0 -3
  42. package/src/core/indexer.ts +0 -131
  43. package/src/core/prompt-builder.ts +0 -91
  44. package/src/events.ts +0 -35
  45. package/src/index-node.ts +0 -23
  46. package/src/index.ts +0 -159
  47. package/src/modules/auth.ts +0 -48
  48. package/src/modules/base.ts +0 -9
  49. package/src/modules/ci.ts +0 -12
  50. package/src/modules/docs.ts +0 -84
  51. package/src/modules/invitations.ts +0 -45
  52. package/src/modules/organizations.ts +0 -90
  53. package/src/modules/sprints.ts +0 -69
  54. package/src/modules/tasks.ts +0 -110
  55. package/src/modules/workspaces.ts +0 -94
  56. package/src/orchestrator.ts +0 -473
  57. package/src/utils/colors.ts +0 -63
  58. package/src/utils/retry.ts +0 -56
@@ -1,3 +0,0 @@
1
- export { DEFAULT_MODEL, getLocusPath, LOCUS_CONFIG } from "./config.js";
2
- export { CodebaseIndexer } from "./indexer.js";
3
- export { PromptBuilder } from "./prompt-builder.js";
@@ -1,113 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { globby } from "globby";
4
- export class CodebaseIndexer {
5
- projectPath;
6
- indexPath;
7
- constructor(projectPath) {
8
- this.projectPath = projectPath;
9
- this.indexPath = join(projectPath, ".locus", "codebase-index.json");
10
- }
11
- /**
12
- * Generates a codebase index by providing the entire file tree to an AI summarizer.
13
- * This is much more efficient than per-file indexing for large projects.
14
- */
15
- async index(onProgress, treeSummarizer) {
16
- if (!treeSummarizer) {
17
- throw new Error("A treeSummarizer is required for this indexing method.");
18
- }
19
- if (onProgress)
20
- onProgress("Generating file tree...");
21
- // 1. Get a comprehensive but clean file tree
22
- const gitmodulesPath = join(this.projectPath, ".gitmodules");
23
- const submoduleIgnores = [];
24
- if (existsSync(gitmodulesPath)) {
25
- try {
26
- const content = readFileSync(gitmodulesPath, "utf-8");
27
- const lines = content.split("\n");
28
- for (const line of lines) {
29
- const match = line.match(/^\s*path\s*=\s*(.*)$/);
30
- const path = match?.[1]?.trim();
31
- if (path) {
32
- submoduleIgnores.push(`${path}/**`);
33
- submoduleIgnores.push(`**/${path}/**`);
34
- }
35
- }
36
- }
37
- catch {
38
- // Fallback if .gitmodules exists but can't be read or parsed
39
- }
40
- }
41
- const files = await globby(["**/*"], {
42
- cwd: this.projectPath,
43
- gitignore: true,
44
- ignore: [
45
- ...submoduleIgnores,
46
- "**/node_modules/**",
47
- "**/dist/**",
48
- "**/build/**",
49
- "**/target/**", // Rust build artifacts
50
- "**/bin/**",
51
- "**/obj/**",
52
- "**/.next/**",
53
- "**/.svelte-kit/**",
54
- "**/.nuxt/**",
55
- "**/.cache/**",
56
- "**/out/**",
57
- "**/__tests__/**",
58
- "**/coverage/**",
59
- "**/*.test.*",
60
- "**/*.spec.*",
61
- "**/*.d.ts",
62
- "**/tsconfig.tsbuildinfo",
63
- "**/.locus/*.json", // Ignore index and other system JSONs
64
- "**/.locus/*.md", // Ignore system MDs
65
- "**/.locus/!(artifacts)/**", // Ignore everything in .locus EXCEPT artifacts
66
- "**/.git/**",
67
- "**/.svn/**",
68
- "**/.hg/**",
69
- "**/.vscode/**",
70
- "**/.idea/**",
71
- "**/.DS_Store",
72
- "**/bun.lock",
73
- "**/package-lock.json",
74
- "**/yarn.lock",
75
- "**/pnpm-lock.yaml",
76
- "**/Cargo.lock",
77
- "**/go.sum",
78
- "**/poetry.lock",
79
- // Binary/Large Assets
80
- "**/*.{png,jpg,jpeg,gif,svg,ico,mp4,webm,wav,mp3,woff,woff2,eot,ttf,otf,pdf,zip,tar.gz,rar}",
81
- ],
82
- });
83
- // Format the tree for the AI
84
- const treeString = files.join("\n");
85
- if (onProgress)
86
- onProgress("AI is analyzing codebase structure...");
87
- // 2. Ask AI to generate the index based on the tree
88
- const index = await treeSummarizer(treeString);
89
- // 3. Post-process: Ensure symbols are extracted for core files if not provided by AI
90
- // (AI is good at structure, but might miss specific exports unless it reads the files)
91
- // For now, we trust the AI's structural summary and can supplement symbols later if needed.
92
- index.lastIndexed = new Date().toISOString();
93
- return index;
94
- }
95
- loadIndex() {
96
- if (existsSync(this.indexPath)) {
97
- try {
98
- return JSON.parse(readFileSync(this.indexPath, "utf-8"));
99
- }
100
- catch {
101
- return null;
102
- }
103
- }
104
- return null;
105
- }
106
- saveIndex(index) {
107
- const dir = dirname(this.indexPath);
108
- if (!existsSync(dir)) {
109
- mkdirSync(dir, { recursive: true });
110
- }
111
- writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
112
- }
113
- }
@@ -1,83 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { AssigneeRole } from "@locusai/shared";
3
- import { getLocusPath } from "./config.js";
4
- export class PromptBuilder {
5
- projectPath;
6
- constructor(projectPath) {
7
- this.projectPath = projectPath;
8
- }
9
- async build(task) {
10
- let prompt = `# Task: ${task.title}\n\n`;
11
- const roleText = this.roleToText(task.assigneeRole);
12
- if (roleText) {
13
- prompt += `## Role\nYou are acting as a ${roleText}.\n\n`;
14
- }
15
- prompt += `## Description\n${task.description || "No description provided."}\n\n`;
16
- // 1. Add CLAUDE.md context
17
- const contextPath = getLocusPath(this.projectPath, "contextFile");
18
- if (existsSync(contextPath)) {
19
- try {
20
- const context = readFileSync(contextPath, "utf-8");
21
- prompt += `## Project Context (from CLAUDE.md)\n${context}\n\n`;
22
- }
23
- catch (err) {
24
- console.warn(`Warning: Could not read context file: ${err}`);
25
- }
26
- }
27
- // 2. Add Codebase Index context
28
- const indexPath = getLocusPath(this.projectPath, "indexFile");
29
- if (existsSync(indexPath)) {
30
- prompt += `## Codebase Overview\nThere is an index file in the .locus/codebase-index.json and if you need you can check it.\n\n`;
31
- }
32
- // 3. Add Documents
33
- if (task.docs && task.docs.length > 0) {
34
- prompt += `## Attached Documents\n`;
35
- for (const doc of task.docs) {
36
- prompt += `### ${doc.title}\n${doc.content || "(No content)"}\n\n`;
37
- }
38
- }
39
- // 4. Add Checklist
40
- if (task.acceptanceChecklist && task.acceptanceChecklist.length > 0) {
41
- prompt += `## Acceptance Criteria\n`;
42
- for (const item of task.acceptanceChecklist) {
43
- prompt += `- ${item.done ? "[x]" : "[ ]"} ${item.text}\n`;
44
- }
45
- prompt += "\n";
46
- }
47
- // 5. Add Comments & Feedback
48
- if (task.comments && task.comments.length > 0) {
49
- const comments = task.comments.slice(0, 5);
50
- prompt += `## Task History & Feedback\n`;
51
- prompt += `Review the following comments for context or rejection feedback:\n\n`;
52
- for (const comment of comments) {
53
- const date = new Date(comment.createdAt).toLocaleString();
54
- prompt += `### ${comment.author} (${date})\n${comment.text}\n\n`;
55
- }
56
- }
57
- prompt += `## Instructions
58
- 1. Complete this task.
59
- 2. **Artifact Management**: If you create any high-level documentation (PRDs, technical drafts, architecture docs), you MUST save them in \`.locus/artifacts/\`. Do NOT create them in the root directory.
60
- 3. **Paths**: Use relative paths from the project root at all times. Do NOT use absolute local paths (e.g., /Users/...).
61
- 4. When finished successfully, output: <promise>COMPLETE</promise>\n`;
62
- return prompt;
63
- }
64
- roleToText(role) {
65
- if (!role) {
66
- return null;
67
- }
68
- switch (role) {
69
- case AssigneeRole.BACKEND:
70
- return "Backend Engineer";
71
- case AssigneeRole.FRONTEND:
72
- return "Frontend Engineer";
73
- case AssigneeRole.PM:
74
- return "Product Manager";
75
- case AssigneeRole.QA:
76
- return "QA Engineer";
77
- case AssigneeRole.DESIGN:
78
- return "Product Designer";
79
- default:
80
- return "engineer";
81
- }
82
- }
83
- }
package/dist/events.js DELETED
@@ -1,15 +0,0 @@
1
- import { EventEmitter } from "events";
2
- export var LocusEvent;
3
- (function (LocusEvent) {
4
- LocusEvent["TOKEN_EXPIRED"] = "TOKEN_EXPIRED";
5
- LocusEvent["AUTH_ERROR"] = "AUTH_ERROR";
6
- LocusEvent["REQUEST_ERROR"] = "REQUEST_ERROR";
7
- })(LocusEvent || (LocusEvent = {}));
8
- export class LocusEmitter extends EventEmitter {
9
- on(event, listener) {
10
- return super.on(event, listener);
11
- }
12
- emit(event, ...args) {
13
- return super.emit(event, ...args);
14
- }
15
- }
@@ -1,23 +0,0 @@
1
- import { BaseModule } from "./base.js";
2
- export class AuthModule extends BaseModule {
3
- async getMe() {
4
- const { data } = await this.api.get("/auth/me");
5
- return data;
6
- }
7
- async requestRegisterOtp(email) {
8
- const { data } = await this.api.post("/auth/register-otp", { email });
9
- return data;
10
- }
11
- async requestLoginOtp(email) {
12
- const { data } = await this.api.post("/auth/login-otp", { email });
13
- return data;
14
- }
15
- async verifyLogin(body) {
16
- const { data } = await this.api.post("/auth/verify-login", body);
17
- return data;
18
- }
19
- async completeRegistration(body) {
20
- const { data } = await this.api.post("/auth/complete-registration", body);
21
- return data;
22
- }
23
- }
@@ -1,8 +0,0 @@
1
- export class BaseModule {
2
- api;
3
- emitter;
4
- constructor(api, emitter) {
5
- this.api = api;
6
- this.emitter = emitter;
7
- }
8
- }
@@ -1,7 +0,0 @@
1
- import { BaseModule } from "./base.js";
2
- export class CiModule extends BaseModule {
3
- async report(body) {
4
- const { data } = await this.api.post("/ci/report", body);
5
- return data;
6
- }
7
- }
@@ -1,38 +0,0 @@
1
- import { BaseModule } from "./base.js";
2
- export class DocsModule extends BaseModule {
3
- async create(workspaceId, body) {
4
- const { data } = await this.api.post(`/workspaces/${workspaceId}/docs`, body);
5
- return data.doc;
6
- }
7
- async list(workspaceId) {
8
- const { data } = await this.api.get(`/workspaces/${workspaceId}/docs`);
9
- return data.docs;
10
- }
11
- async getById(id, workspaceId) {
12
- const { data } = await this.api.get(`/workspaces/${workspaceId}/docs/${id}`);
13
- return data.doc;
14
- }
15
- async update(id, workspaceId, body) {
16
- const { data } = await this.api.put(`/workspaces/${workspaceId}/docs/${id}`, body);
17
- return data.doc;
18
- }
19
- async delete(id, workspaceId) {
20
- await this.api.delete(`/workspaces/${workspaceId}/docs/${id}`);
21
- }
22
- // Group Management
23
- async listGroups(workspaceId) {
24
- const { data } = await this.api.get(`/workspaces/${workspaceId}/doc-groups`);
25
- return data.groups;
26
- }
27
- async createGroup(workspaceId, body) {
28
- const { data } = await this.api.post(`/workspaces/${workspaceId}/doc-groups`, body);
29
- return data.group;
30
- }
31
- async updateGroup(id, workspaceId, body) {
32
- const { data } = await this.api.patch(`/workspaces/${workspaceId}/doc-groups/${id}`, body);
33
- return data.group;
34
- }
35
- async deleteGroup(id, workspaceId) {
36
- await this.api.delete(`/workspaces/${workspaceId}/doc-groups/${id}`);
37
- }
38
- }
@@ -1,22 +0,0 @@
1
- import { BaseModule } from "./base.js";
2
- export class InvitationsModule extends BaseModule {
3
- async create(orgId, body) {
4
- const { data } = await this.api.post(`/org/${orgId}/invitations`, body);
5
- return data.invitation;
6
- }
7
- async list(orgId) {
8
- const { data } = await this.api.get(`/org/${orgId}/invitations`);
9
- return data.invitations;
10
- }
11
- async verify(token) {
12
- const { data } = await this.api.get(`/invitations/verify/${token}`);
13
- return data;
14
- }
15
- async accept(body) {
16
- const { data } = await this.api.post("/invitations/accept", body);
17
- return data;
18
- }
19
- async revoke(orgId, id) {
20
- await this.api.delete(`/org/${orgId}/invitations/${id}`);
21
- }
22
- }
@@ -1,39 +0,0 @@
1
- import { BaseModule } from "./base.js";
2
- export class OrganizationsModule extends BaseModule {
3
- async list() {
4
- const { data } = await this.api.get("/organizations");
5
- return data.organizations;
6
- }
7
- async getById(id) {
8
- const { data } = await this.api.get(`/organizations/${id}`);
9
- return data.organization;
10
- }
11
- async listMembers(id) {
12
- const { data } = await this.api.get(`/organizations/${id}/members`);
13
- return data.members;
14
- }
15
- async addMember(id, body) {
16
- const { data } = await this.api.post(`/organizations/${id}/members`, body);
17
- return data.membership;
18
- }
19
- async removeMember(orgId, userId) {
20
- await this.api.delete(`/organizations/${orgId}/members/${userId}`);
21
- }
22
- async delete(orgId) {
23
- await this.api.delete(`/organizations/${orgId}`);
24
- }
25
- // ============================================================================
26
- // API Key Management
27
- // ============================================================================
28
- async listApiKeys(orgId) {
29
- const { data } = await this.api.get(`/organizations/${orgId}/api-keys`);
30
- return data.apiKeys;
31
- }
32
- async createApiKey(orgId, name) {
33
- const { data } = await this.api.post(`/organizations/${orgId}/api-keys`, { name });
34
- return data.apiKey;
35
- }
36
- async deleteApiKey(orgId, keyId) {
37
- await this.api.delete(`/organizations/${orgId}/api-keys/${keyId}`);
38
- }
39
- }
@@ -1,34 +0,0 @@
1
- import { BaseModule } from "./base.js";
2
- export class SprintsModule extends BaseModule {
3
- async list(workspaceId) {
4
- const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints`);
5
- return data.sprints;
6
- }
7
- async getActive(workspaceId) {
8
- const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints/active`);
9
- return data.sprint;
10
- }
11
- async getById(id, workspaceId) {
12
- const { data } = await this.api.get(`/workspaces/${workspaceId}/sprints/${id}`);
13
- return data.sprint;
14
- }
15
- async create(workspaceId, body) {
16
- const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints`, body);
17
- return data.sprint;
18
- }
19
- async update(id, workspaceId, body) {
20
- const { data } = await this.api.patch(`/workspaces/${workspaceId}/sprints/${id}`, body);
21
- return data.sprint;
22
- }
23
- async delete(id, workspaceId) {
24
- await this.api.delete(`/workspaces/${workspaceId}/sprints/${id}`);
25
- }
26
- async start(id, workspaceId) {
27
- const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/start`);
28
- return data.sprint;
29
- }
30
- async complete(id, workspaceId) {
31
- const { data } = await this.api.post(`/workspaces/${workspaceId}/sprints/${id}/complete`);
32
- return data.sprint;
33
- }
34
- }
@@ -1,56 +0,0 @@
1
- import { TaskStatus, } from "@locusai/shared";
2
- import { BaseModule } from "./base.js";
3
- export class TasksModule extends BaseModule {
4
- /**
5
- * List all tasks in a workspace, optionally filtered
6
- */
7
- async list(workspaceId, options) {
8
- const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks`);
9
- let tasks = data.tasks;
10
- // Client-side filtering (API doesn't support query params yet)
11
- if (options?.sprintId) {
12
- tasks = tasks.filter((t) => t.sprintId === options.sprintId);
13
- }
14
- if (options?.status) {
15
- const statuses = Array.isArray(options.status)
16
- ? options.status
17
- : [options.status];
18
- tasks = tasks.filter((t) => statuses.includes(t.status));
19
- }
20
- return tasks;
21
- }
22
- /**
23
- * Get available tasks for an agent to work on.
24
- * Returns tasks in BACKLOG or IN_PROGRESS (unassigned) status.
25
- */
26
- async getAvailable(workspaceId, sprintId) {
27
- const tasks = await this.list(workspaceId, {
28
- sprintId,
29
- });
30
- return tasks.filter((t) => t.status === TaskStatus.BACKLOG ||
31
- (t.status === TaskStatus.IN_PROGRESS && !t.assignedTo));
32
- }
33
- async getById(id, workspaceId) {
34
- const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks/${id}`);
35
- return data.task;
36
- }
37
- async create(workspaceId, body) {
38
- const { data } = await this.api.post(`/workspaces/${workspaceId}/tasks`, body);
39
- return data.task;
40
- }
41
- async update(id, workspaceId, body) {
42
- const { data } = await this.api.patch(`/workspaces/${workspaceId}/tasks/${id}`, body);
43
- return data.task;
44
- }
45
- async delete(id, workspaceId) {
46
- await this.api.delete(`/workspaces/${workspaceId}/tasks/${id}`);
47
- }
48
- async getBacklog(workspaceId) {
49
- const { data } = await this.api.get(`/workspaces/${workspaceId}/tasks/backlog`);
50
- return data.tasks;
51
- }
52
- async addComment(id, workspaceId, body) {
53
- const { data } = await this.api.post(`/workspaces/${workspaceId}/tasks/${id}/comment`, body);
54
- return data.comment;
55
- }
56
- }
@@ -1,49 +0,0 @@
1
- import { BaseModule } from "./base.js";
2
- export class WorkspacesModule extends BaseModule {
3
- async listAll() {
4
- const { data } = await this.api.get("/workspaces");
5
- return data.workspaces;
6
- }
7
- async listByOrg(orgId) {
8
- const { data } = await this.api.get(`/workspaces/org/${orgId}`);
9
- return data.workspaces;
10
- }
11
- async create(body) {
12
- const { orgId, ...bodyWithoutOrgId } = body;
13
- const { data } = await this.api.post(`/workspaces/org/${orgId}`, bodyWithoutOrgId);
14
- return data.workspace;
15
- }
16
- async createWithAutoOrg(body) {
17
- const { data } = await this.api.post("/workspaces", body);
18
- return data.workspace;
19
- }
20
- async getById(id) {
21
- const { data } = await this.api.get(`/workspaces/${id}`);
22
- return data.workspace;
23
- }
24
- async update(id, body) {
25
- const { data } = await this.api.put(`/workspaces/${id}`, body);
26
- return data.workspace;
27
- }
28
- async delete(id) {
29
- await this.api.delete(`/workspaces/${id}`);
30
- }
31
- async getStats(id) {
32
- const { data } = await this.api.get(`/workspaces/${id}/stats`);
33
- return data;
34
- }
35
- async getActivity(id, limit) {
36
- const { data } = await this.api.get(`/workspaces/${id}/activity`, {
37
- params: { limit },
38
- });
39
- return data.activity;
40
- }
41
- /**
42
- * Dispatch a task from the workspace backlog to an agent.
43
- * Atomically moves a task from BACKLOG to IN_PROGRESS and assigns it.
44
- */
45
- async dispatch(id, workerId, sprintId) {
46
- const { data } = await this.api.post(`/workspaces/${id}/dispatch`, { workerId, sprintId });
47
- return data.task;
48
- }
49
- }