@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,33 @@
1
+ export interface AnthropicClientConfig {
2
+ apiKey: string;
3
+ model?: string;
4
+ }
5
+ export interface CachedPromptOptions {
6
+ systemPrompt?: string;
7
+ cacheableContext?: string[];
8
+ userPrompt: string;
9
+ }
10
+ /**
11
+ * Anthropic Client with Prompt Caching Support
12
+ *
13
+ * This client wraps the official Anthropic SDK and adds support for
14
+ * prompt caching to dramatically reduce latency and costs for repeated
15
+ * context (like codebase indexes, CLAUDE.md, etc.)
16
+ */
17
+ export declare class AnthropicClient {
18
+ private client;
19
+ private model;
20
+ constructor(config: AnthropicClientConfig);
21
+ /**
22
+ * Run a prompt with optional caching for large context blocks
23
+ *
24
+ * @param options - Prompt configuration with cacheable context
25
+ * @returns The generated text response
26
+ */
27
+ run(options: CachedPromptOptions): Promise<string>;
28
+ /**
29
+ * Simple run without caching (for short prompts)
30
+ */
31
+ runSimple(prompt: string): Promise<string>;
32
+ }
33
+ //# sourceMappingURL=anthropic-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic-client.d.ts","sourceRoot":"","sources":["../../src/ai/anthropic-client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,qBAAqB;IAOzC;;;;;OAKG;IACG,GAAG,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;IA8CxD;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAKjD"}
@@ -0,0 +1,70 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ import { DEFAULT_MODEL } from "../core/config";
3
+ /**
4
+ * Anthropic Client with Prompt Caching Support
5
+ *
6
+ * This client wraps the official Anthropic SDK and adds support for
7
+ * prompt caching to dramatically reduce latency and costs for repeated
8
+ * context (like codebase indexes, CLAUDE.md, etc.)
9
+ */
10
+ export class AnthropicClient {
11
+ client;
12
+ model;
13
+ constructor(config) {
14
+ this.client = new Anthropic({
15
+ apiKey: config.apiKey,
16
+ });
17
+ this.model = config.model || DEFAULT_MODEL;
18
+ }
19
+ /**
20
+ * Run a prompt with optional caching for large context blocks
21
+ *
22
+ * @param options - Prompt configuration with cacheable context
23
+ * @returns The generated text response
24
+ */
25
+ async run(options) {
26
+ const { systemPrompt, cacheableContext = [], userPrompt } = options;
27
+ // Build system message with cache breakpoints
28
+ const systemContent = [];
29
+ if (systemPrompt) {
30
+ systemContent.push({
31
+ type: "text",
32
+ text: systemPrompt,
33
+ });
34
+ }
35
+ // Add each cacheable context block with cache_control
36
+ for (let i = 0; i < cacheableContext.length; i++) {
37
+ const isLast = i === cacheableContext.length - 1;
38
+ systemContent.push({
39
+ type: "text",
40
+ text: cacheableContext[i],
41
+ // Only the last block gets the cache breakpoint
42
+ ...(isLast && {
43
+ cache_control: { type: "ephemeral" },
44
+ }),
45
+ });
46
+ }
47
+ const response = await this.client.messages.create({
48
+ model: this.model,
49
+ max_tokens: 8000,
50
+ system: systemContent,
51
+ messages: [
52
+ {
53
+ role: "user",
54
+ content: userPrompt,
55
+ },
56
+ ],
57
+ });
58
+ // Extract text from response
59
+ const textBlocks = response.content.filter((block) => block.type === "text");
60
+ return textBlocks.map((block) => block.text).join("\n");
61
+ }
62
+ /**
63
+ * Simple run without caching (for short prompts)
64
+ */
65
+ async runSimple(prompt) {
66
+ return this.run({
67
+ userPrompt: prompt,
68
+ });
69
+ }
70
+ }
@@ -0,0 +1,7 @@
1
+ export declare class ClaudeRunner {
2
+ private projectPath;
3
+ private model;
4
+ constructor(projectPath: string, model?: string);
5
+ run(prompt: string, _isPlanning?: boolean): Promise<string>;
6
+ }
7
+ //# sourceMappingURL=claude-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,qBAAa,YAAY;IAErB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;gBADL,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsB;IAGvC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;CAuC1D"}
@@ -0,0 +1,43 @@
1
+ import { spawn } from "node:child_process";
2
+ import { DEFAULT_MODEL } from "../core/config";
3
+ export class ClaudeRunner {
4
+ projectPath;
5
+ model;
6
+ constructor(projectPath, model = DEFAULT_MODEL) {
7
+ this.projectPath = projectPath;
8
+ this.model = model;
9
+ }
10
+ run(prompt, _isPlanning = false) {
11
+ return new Promise((resolve, reject) => {
12
+ const args = [
13
+ "--dangerously-skip-permissions",
14
+ "--print",
15
+ "--model",
16
+ this.model,
17
+ ];
18
+ const claude = spawn("claude", args, {
19
+ cwd: this.projectPath,
20
+ stdio: ["pipe", "pipe", "pipe"],
21
+ });
22
+ let output = "";
23
+ let errorOutput = "";
24
+ claude.stdout.on("data", (data) => {
25
+ output += data.toString();
26
+ process.stdout.write(data.toString());
27
+ });
28
+ claude.stderr.on("data", (data) => {
29
+ errorOutput += data.toString();
30
+ process.stderr.write(data.toString());
31
+ });
32
+ claude.on("error", (err) => reject(new Error(`Failed to start Claude CLI: ${err.message}`)));
33
+ claude.on("close", (code) => {
34
+ if (code === 0)
35
+ resolve(output);
36
+ else
37
+ reject(new Error(`Claude exited with code ${code}: ${errorOutput}`));
38
+ });
39
+ claude.stdin.write(prompt);
40
+ claude.stdin.end();
41
+ });
42
+ }
43
+ }
@@ -0,0 +1,3 @@
1
+ export { AnthropicClient } from "./anthropic-client";
2
+ export { ClaudeRunner } from "./claude-runner";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { AnthropicClient } from "./anthropic-client";
2
+ export { ClaudeRunner } from "./claude-runner";
@@ -0,0 +1,10 @@
1
+ export declare const DEFAULT_MODEL = "sonnet";
2
+ export declare const LOCUS_CONFIG: {
3
+ dir: string;
4
+ configFile: string;
5
+ indexFile: string;
6
+ contextFile: string;
7
+ artifactsDir: string;
8
+ };
9
+ export declare function getLocusPath(projectPath: string, fileName: keyof typeof LOCUS_CONFIG): string;
10
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC,eAAO,MAAM,YAAY;;;;;;CAMxB,CAAC;AAEF,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,OAAO,YAAY,GAClC,MAAM,CAKR"}
@@ -0,0 +1,15 @@
1
+ import { join } from "node:path";
2
+ export const DEFAULT_MODEL = "sonnet";
3
+ export const LOCUS_CONFIG = {
4
+ dir: ".locus",
5
+ configFile: "config.json",
6
+ indexFile: "codebase-index.json",
7
+ contextFile: "CLAUDE.md",
8
+ artifactsDir: "artifacts",
9
+ };
10
+ export function getLocusPath(projectPath, fileName) {
11
+ if (fileName === "contextFile") {
12
+ return join(projectPath, LOCUS_CONFIG.contextFile);
13
+ }
14
+ return join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG[fileName]);
15
+ }
@@ -0,0 +1,4 @@
1
+ export { DEFAULT_MODEL, getLocusPath, LOCUS_CONFIG } from "./config";
2
+ export { type CodebaseIndex, CodebaseIndexer } from "./indexer";
3
+ export { PromptBuilder } from "./prompt-builder";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,EAAE,KAAK,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_MODEL, getLocusPath, LOCUS_CONFIG } from "./config";
2
+ export { CodebaseIndexer } from "./indexer";
3
+ export { PromptBuilder } from "./prompt-builder";
@@ -0,0 +1,18 @@
1
+ export interface CodebaseIndex {
2
+ symbols: Record<string, string[]>;
3
+ responsibilities: Record<string, string>;
4
+ lastIndexed: string;
5
+ }
6
+ export declare class CodebaseIndexer {
7
+ private projectPath;
8
+ private indexPath;
9
+ constructor(projectPath: string);
10
+ /**
11
+ * Generates a codebase index by providing the entire file tree to an AI summarizer.
12
+ * This is much more efficient than per-file indexing for large projects.
13
+ */
14
+ index(onProgress?: (message: string) => void, treeSummarizer?: (tree: string) => Promise<CodebaseIndex>): Promise<CodebaseIndex>;
15
+ loadIndex(): CodebaseIndex | null;
16
+ saveIndex(index: CodebaseIndex): void;
17
+ }
18
+ //# sourceMappingURL=indexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/core/indexer.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;gBAEd,WAAW,EAAE,MAAM;IAK/B;;;OAGG;IACG,KAAK,CACT,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACtC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,GACxD,OAAO,CAAC,aAAa,CAAC;IA8CzB,SAAS,IAAI,aAAa,GAAG,IAAI;IAWjC,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;CAOtC"}
@@ -0,0 +1,73 @@
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 files = await globby(["**/*"], {
23
+ cwd: this.projectPath,
24
+ ignore: [
25
+ "**/node_modules/**",
26
+ "**/dist/**",
27
+ "**/build/**",
28
+ "**/.next/**",
29
+ "**/out/**",
30
+ "**/__tests__/**",
31
+ "**/*.test.*",
32
+ "**/*.spec.*",
33
+ "**/*.d.ts",
34
+ "**/tsconfig.tsbuildinfo",
35
+ "**/.locus/*.json", // Ignore index and other system JSONs
36
+ "**/.locus/*.md", // Ignore system MDs if any (except artifacts handled below)
37
+ "**/.locus/!(artifacts)/**", // Ignore everything in .locus EXCEPT artifacts
38
+ "bun.lock",
39
+ "package-lock.json",
40
+ "yarn.lock",
41
+ ],
42
+ });
43
+ // Format the tree for the AI
44
+ const treeString = files.join("\n");
45
+ if (onProgress)
46
+ onProgress("AI is analyzing codebase structure...");
47
+ // 2. Ask AI to generate the index based on the tree
48
+ const index = await treeSummarizer(treeString);
49
+ // 3. Post-process: Ensure symbols are extracted for core files if not provided by AI
50
+ // (AI is good at structure, but might miss specific exports unless it reads the files)
51
+ // For now, we trust the AI's structural summary and can supplement symbols later if needed.
52
+ index.lastIndexed = new Date().toISOString();
53
+ return index;
54
+ }
55
+ loadIndex() {
56
+ if (existsSync(this.indexPath)) {
57
+ try {
58
+ return JSON.parse(readFileSync(this.indexPath, "utf-8"));
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ saveIndex(index) {
67
+ const dir = dirname(this.indexPath);
68
+ if (!existsSync(dir)) {
69
+ mkdirSync(dir, { recursive: true });
70
+ }
71
+ writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
72
+ }
73
+ }
@@ -0,0 +1,8 @@
1
+ import { Task } from "@locusai/shared";
2
+ export declare class PromptBuilder {
3
+ private projectPath;
4
+ constructor(projectPath: string);
5
+ build(task: Task): Promise<string>;
6
+ private formatIndex;
7
+ }
8
+ //# sourceMappingURL=prompt-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-builder.d.ts","sourceRoot":"","sources":["../../src/core/prompt-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAIvC,qBAAa,aAAa;IACZ,OAAO,CAAC,WAAW;gBAAX,WAAW,EAAE,MAAM;IAEjC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAmExC,OAAO,CAAC,WAAW;CAwBpB"}
@@ -0,0 +1,87 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { getLocusPath } from "./config";
3
+ export class PromptBuilder {
4
+ projectPath;
5
+ constructor(projectPath) {
6
+ this.projectPath = projectPath;
7
+ }
8
+ async build(task) {
9
+ let prompt = `# Task: ${task.title}\n\n`;
10
+ if (task.assigneeRole) {
11
+ prompt += `## Role\nYou are acting as a ${task.assigneeRole} engineer.\n\n`;
12
+ }
13
+ prompt += `## Description\n${task.description || "No description provided."}\n\n`;
14
+ // 1. Add CLAUDE.md context
15
+ const contextPath = getLocusPath(this.projectPath, "contextFile");
16
+ if (existsSync(contextPath)) {
17
+ try {
18
+ const context = readFileSync(contextPath, "utf-8");
19
+ prompt += `## Project Context (from CLAUDE.md)\n${context}\n\n`;
20
+ }
21
+ catch (err) {
22
+ console.warn(`Warning: Could not read context file: ${err}`);
23
+ }
24
+ }
25
+ // 2. Add Codebase Index context
26
+ const indexPath = getLocusPath(this.projectPath, "indexFile");
27
+ if (existsSync(indexPath)) {
28
+ try {
29
+ const indexContent = readFileSync(indexPath, "utf-8");
30
+ const index = JSON.parse(indexContent);
31
+ prompt += this.formatIndex(index, task);
32
+ }
33
+ catch (err) {
34
+ console.warn(`Warning: Could not read codebase index: ${err}`);
35
+ }
36
+ }
37
+ // 3. Add Documents
38
+ if (task.docs && task.docs.length > 0) {
39
+ prompt += `## Attached Documents\n`;
40
+ for (const doc of task.docs) {
41
+ prompt += `### ${doc.title}\n${doc.content || "(No content)"}\n\n`;
42
+ }
43
+ }
44
+ // 4. Add Checklist
45
+ if (task.acceptanceChecklist && task.acceptanceChecklist.length > 0) {
46
+ prompt += `## Acceptance Criteria\n`;
47
+ for (const item of task.acceptanceChecklist) {
48
+ prompt += `- ${item.done ? "[x]" : "[ ]"} ${item.text}\n`;
49
+ }
50
+ prompt += "\n";
51
+ }
52
+ // 5. Add Comments & Feedback
53
+ if (task.comments && task.comments.length > 0) {
54
+ prompt += `## Task History & Feedback\n`;
55
+ prompt += `Review the following comments for context or rejection feedback:\n\n`;
56
+ for (const comment of task.comments) {
57
+ const date = new Date(comment.createdAt).toLocaleString();
58
+ prompt += `### ${comment.author} (${date})\n${comment.text}\n\n`;
59
+ }
60
+ }
61
+ prompt += `## Instructions
62
+ 1. Complete this task.
63
+ 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.
64
+ 3. **Paths**: Use relative paths from the project root at all times. Do NOT use absolute local paths (e.g., /Users/...).
65
+ 4. When finished successfully, output: <promise>COMPLETE</promise>\n`;
66
+ return prompt;
67
+ }
68
+ formatIndex(index, task) {
69
+ let section = `## Codebase Overview\nThis codebase has been indexed to help you navigate.\n\n`;
70
+ // Structural directories
71
+ const structuralDirs = Object.entries(index.responsibilities || {})
72
+ .filter(([path]) => !path.includes(".") || path.split("/").length <= 2)
73
+ .slice(0, 15);
74
+ if (structuralDirs.length > 0) {
75
+ section += `### Project Structure\n${structuralDirs.map(([p, d]) => `- \`${p}\`: ${d}`).join("\n")}\n\n`;
76
+ }
77
+ // Relevant symbols
78
+ const keywords = `${task.title} ${task.description}`.toLowerCase();
79
+ const symbols = Object.entries(index.symbols || {})
80
+ .filter(([symbol]) => keywords.includes(symbol.toLowerCase()))
81
+ .slice(0, 10);
82
+ if (symbols.length > 0) {
83
+ section += `### Potentially Relevant Symbols\n${symbols.map(([s, f]) => `- \`${s}\` is defined in: ${Array.isArray(f) ? f.join(", ") : f}`).join("\n")}\n\n`;
84
+ }
85
+ return section;
86
+ }
87
+ }
@@ -0,0 +1,20 @@
1
+ import { EventEmitter } from "events";
2
+ export declare enum LocusEvent {
3
+ TOKEN_EXPIRED = "TOKEN_EXPIRED",
4
+ AUTH_ERROR = "AUTH_ERROR",
5
+ REQUEST_ERROR = "REQUEST_ERROR"
6
+ }
7
+ export interface LocusConfig {
8
+ baseUrl: string;
9
+ token?: string | null;
10
+ timeout?: number;
11
+ }
12
+ export declare class LocusEmitter extends EventEmitter {
13
+ on(event: LocusEvent.TOKEN_EXPIRED, listener: () => void): this;
14
+ on(event: LocusEvent.AUTH_ERROR, listener: (error: Error) => void): this;
15
+ on(event: LocusEvent.REQUEST_ERROR, listener: (error: Error) => void): this;
16
+ emit(event: LocusEvent.TOKEN_EXPIRED): boolean;
17
+ emit(event: LocusEvent.AUTH_ERROR, error: Error): boolean;
18
+ emit(event: LocusEvent.REQUEST_ERROR, error: Error): boolean;
19
+ }
20
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,oBAAY,UAAU;IACpB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,aAAa,kBAAkB;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,YAAa,SAAQ,YAAY;IAC5C,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAC/D,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IACxE,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAQ3E,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,GAAG,OAAO;IAC9C,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;IACzD,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;CAI7D"}
package/dist/events.js ADDED
@@ -0,0 +1,15 @@
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
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Node.js-only exports
3
+ * This is a separate entry point for Node.js/CLI only
4
+ * It should NOT be imported by browser applications
5
+ *
6
+ * These modules use Node.js APIs (fs, child_process, etc.)
7
+ * and will break in browser environments
8
+ */
9
+ export * from "./agent";
10
+ export * from "./ai";
11
+ export * from "./core";
12
+ export * from "./index";
13
+ export { AgentOrchestrator, type OrchestratorConfig } from "./orchestrator";
14
+ //# sourceMappingURL=index-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../src/index-node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,cAAc,SAAS,CAAC;AAExB,cAAc,MAAM,CAAC;AAErB,cAAc,QAAQ,CAAC;AAEvB,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Node.js-only exports
3
+ * This is a separate entry point for Node.js/CLI only
4
+ * It should NOT be imported by browser applications
5
+ *
6
+ * These modules use Node.js APIs (fs, child_process, etc.)
7
+ * and will break in browser environments
8
+ */
9
+ // Node.js-only: Agent system
10
+ export * from "./agent";
11
+ // Node.js-only: AI clients
12
+ export * from "./ai";
13
+ // Node.js-only: Core utilities (uses fs)
14
+ export * from "./core";
15
+ // Re-export everything from main index (browser-safe)
16
+ export * from "./index";
17
+ // Node.js-only: Orchestrator
18
+ export { AgentOrchestrator } from "./orchestrator";
@@ -0,0 +1,34 @@
1
+ import { LocusConfig, LocusEmitter } from "./events";
2
+ import { AuthModule } from "./modules/auth";
3
+ import { CiModule } from "./modules/ci";
4
+ import { DocsModule } from "./modules/docs";
5
+ import { InvitationsModule } from "./modules/invitations";
6
+ import { OrganizationsModule } from "./modules/organizations";
7
+ import { SprintsModule } from "./modules/sprints";
8
+ import { TasksModule } from "./modules/tasks";
9
+ import { WorkspacesModule } from "./modules/workspaces";
10
+ export * from "./events";
11
+ export * from "./modules/auth";
12
+ export * from "./modules/ci";
13
+ export * from "./modules/docs";
14
+ export * from "./modules/invitations";
15
+ export * from "./modules/organizations";
16
+ export * from "./modules/sprints";
17
+ export * from "./modules/tasks";
18
+ export * from "./modules/workspaces";
19
+ export declare class LocusClient {
20
+ private readonly api;
21
+ readonly emitter: LocusEmitter;
22
+ readonly auth: AuthModule;
23
+ readonly tasks: TasksModule;
24
+ readonly sprints: SprintsModule;
25
+ readonly workspaces: WorkspacesModule;
26
+ readonly organizations: OrganizationsModule;
27
+ readonly invitations: InvitationsModule;
28
+ readonly docs: DocsModule;
29
+ readonly ci: CiModule;
30
+ constructor(config: LocusConfig);
31
+ private setupInterceptors;
32
+ setToken(token: string | null): void;
33
+ }
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAc,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AAErC,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,SAAgB,OAAO,EAAE,YAAY,CAAC;IAEtC,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,KAAK,EAAE,WAAW,CAAC;IACnC,SAAgB,OAAO,EAAE,aAAa,CAAC;IACvC,SAAgB,UAAU,EAAE,gBAAgB,CAAC;IAC7C,SAAgB,aAAa,EAAE,mBAAmB,CAAC;IACnD,SAAgB,WAAW,EAAE,iBAAiB,CAAC;IAC/C,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,EAAE,EAAE,QAAQ,CAAC;gBAEjB,MAAM,EAAE,WAAW;IAyB/B,OAAO,CAAC,iBAAiB;IAmDlB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAOrC"}
package/dist/index.js ADDED
@@ -0,0 +1,101 @@
1
+ import axios from "axios";
2
+ import { LocusEmitter, LocusEvent } from "./events";
3
+ import { AuthModule } from "./modules/auth";
4
+ import { CiModule } from "./modules/ci";
5
+ import { DocsModule } from "./modules/docs";
6
+ import { InvitationsModule } from "./modules/invitations";
7
+ import { OrganizationsModule } from "./modules/organizations";
8
+ import { SprintsModule } from "./modules/sprints";
9
+ import { TasksModule } from "./modules/tasks";
10
+ import { WorkspacesModule } from "./modules/workspaces";
11
+ // Browser-safe exports only
12
+ export * from "./events";
13
+ export * from "./modules/auth";
14
+ export * from "./modules/ci";
15
+ export * from "./modules/docs";
16
+ export * from "./modules/invitations";
17
+ export * from "./modules/organizations";
18
+ export * from "./modules/sprints";
19
+ export * from "./modules/tasks";
20
+ export * from "./modules/workspaces";
21
+ export class LocusClient {
22
+ api;
23
+ emitter;
24
+ auth;
25
+ tasks;
26
+ sprints;
27
+ workspaces;
28
+ organizations;
29
+ invitations;
30
+ docs;
31
+ ci;
32
+ constructor(config) {
33
+ this.emitter = new LocusEmitter();
34
+ this.api = axios.create({
35
+ baseURL: config.baseUrl,
36
+ timeout: config.timeout || 10000,
37
+ headers: {
38
+ "Content-Type": "application/json",
39
+ ...(config.token ? { Authorization: `Bearer ${config.token}` } : {}),
40
+ },
41
+ });
42
+ this.setupInterceptors();
43
+ // Initialize modules
44
+ this.auth = new AuthModule(this.api, this.emitter);
45
+ this.tasks = new TasksModule(this.api, this.emitter);
46
+ this.sprints = new SprintsModule(this.api, this.emitter);
47
+ this.workspaces = new WorkspacesModule(this.api, this.emitter);
48
+ this.organizations = new OrganizationsModule(this.api, this.emitter);
49
+ this.invitations = new InvitationsModule(this.api, this.emitter);
50
+ this.docs = new DocsModule(this.api, this.emitter);
51
+ this.ci = new CiModule(this.api, this.emitter);
52
+ }
53
+ setupInterceptors() {
54
+ this.api.interceptors.response.use((response) => {
55
+ if (response.data &&
56
+ typeof response.data === "object" &&
57
+ "data" in response.data) {
58
+ response.data = response.data.data;
59
+ }
60
+ return response;
61
+ }, (error) => {
62
+ const status = error.response?.status;
63
+ // Extract error message from API response format: { error: { message: "..." } }
64
+ let message;
65
+ // Try to get message from API error response
66
+ if (error.response?.data?.error?.message &&
67
+ typeof error.response.data.error.message === "string") {
68
+ message = error.response.data.error.message;
69
+ }
70
+ else if (error.response?.data?.message &&
71
+ typeof error.response.data.message === "string") {
72
+ message = error.response.data.message;
73
+ }
74
+ else if (error.message && typeof error.message === "string") {
75
+ message = error.message;
76
+ }
77
+ else {
78
+ message = "An error occurred";
79
+ }
80
+ // Create a new error with a meaningful message
81
+ const enhancedError = new Error(message);
82
+ enhancedError.name = `HTTP${status || "Error"}`;
83
+ if (status === 401) {
84
+ this.emitter.emit(LocusEvent.TOKEN_EXPIRED);
85
+ this.emitter.emit(LocusEvent.AUTH_ERROR, enhancedError);
86
+ }
87
+ else {
88
+ this.emitter.emit(LocusEvent.REQUEST_ERROR, enhancedError);
89
+ }
90
+ return Promise.reject(enhancedError);
91
+ });
92
+ }
93
+ setToken(token) {
94
+ if (token) {
95
+ this.api.defaults.headers.common.Authorization = `Bearer ${token}`;
96
+ }
97
+ else {
98
+ delete this.api.defaults.headers.common.Authorization;
99
+ }
100
+ }
101
+ }