@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.
- package/dist/agent/artifact-syncer.d.ts +17 -0
- package/dist/agent/artifact-syncer.d.ts.map +1 -0
- package/dist/agent/artifact-syncer.js +77 -0
- package/dist/agent/codebase-indexer-service.d.ts +18 -0
- package/dist/agent/codebase-indexer-service.d.ts.map +1 -0
- package/dist/agent/codebase-indexer-service.js +55 -0
- package/dist/agent/index.d.ts +6 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +5 -0
- package/dist/agent/sprint-planner.d.ts +17 -0
- package/dist/agent/sprint-planner.d.ts.map +1 -0
- package/dist/agent/sprint-planner.js +62 -0
- package/dist/agent/task-executor.d.ts +24 -0
- package/dist/agent/task-executor.d.ts.map +1 -0
- package/dist/agent/task-executor.js +56 -0
- package/dist/agent/worker.d.ts +37 -0
- package/dist/agent/worker.d.ts.map +1 -0
- package/dist/agent/worker.js +232 -0
- package/dist/ai/anthropic-client.d.ts +33 -0
- package/dist/ai/anthropic-client.d.ts.map +1 -0
- package/dist/ai/anthropic-client.js +70 -0
- package/dist/ai/claude-runner.d.ts +7 -0
- package/dist/ai/claude-runner.d.ts.map +1 -0
- package/dist/ai/claude-runner.js +43 -0
- package/dist/ai/index.d.ts +3 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +2 -0
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +15 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/indexer.d.ts +18 -0
- package/dist/core/indexer.d.ts.map +1 -0
- package/dist/core/indexer.js +73 -0
- package/dist/core/prompt-builder.d.ts +8 -0
- package/dist/core/prompt-builder.d.ts.map +1 -0
- package/dist/core/prompt-builder.js +87 -0
- package/dist/events.d.ts +20 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +15 -0
- package/dist/index-node.d.ts +14 -0
- package/dist/index-node.d.ts.map +1 -0
- package/dist/index-node.js +18 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/modules/auth.d.ts +14 -0
- package/dist/modules/auth.d.ts.map +1 -0
- package/dist/modules/auth.js +23 -0
- package/dist/modules/base.d.ts +8 -0
- package/dist/modules/base.d.ts.map +1 -0
- package/dist/modules/base.js +8 -0
- package/dist/modules/ci.d.ts +8 -0
- package/dist/modules/ci.d.ts.map +1 -0
- package/dist/modules/ci.js +7 -0
- package/dist/modules/docs.d.ts +14 -0
- package/dist/modules/docs.d.ts.map +1 -0
- package/dist/modules/docs.js +38 -0
- package/dist/modules/invitations.d.ts +10 -0
- package/dist/modules/invitations.d.ts.map +1 -0
- package/dist/modules/invitations.js +22 -0
- package/dist/modules/organizations.d.ts +24 -0
- package/dist/modules/organizations.d.ts.map +1 -0
- package/dist/modules/organizations.js +39 -0
- package/dist/modules/sprints.d.ts +13 -0
- package/dist/modules/sprints.d.ts.map +1 -0
- package/dist/modules/sprints.js +34 -0
- package/dist/modules/tasks.d.ts +24 -0
- package/dist/modules/tasks.d.ts.map +1 -0
- package/dist/modules/tasks.js +56 -0
- package/dist/modules/workspaces.d.ts +21 -0
- package/dist/modules/workspaces.d.ts.map +1 -0
- package/dist/modules/workspaces.js +49 -0
- package/dist/orchestrator.d.ts +90 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +326 -0
- package/package.json +53 -0
- package/src/agent/artifact-syncer.ts +111 -0
- package/src/agent/codebase-indexer-service.ts +71 -0
- package/src/agent/index.ts +5 -0
- package/src/agent/sprint-planner.ts +78 -0
- package/src/agent/task-executor.ts +77 -0
- package/src/agent/worker.ts +299 -0
- package/src/ai/anthropic-client.ts +93 -0
- package/src/ai/claude-runner.ts +49 -0
- package/src/ai/index.ts +2 -0
- package/src/core/config.ts +21 -0
- package/src/core/index.ts +3 -0
- package/src/core/indexer.ts +91 -0
- package/src/core/prompt-builder.ts +100 -0
- package/src/events.ts +32 -0
- package/src/index-node.ts +20 -0
- package/src/index.ts +119 -0
- package/src/modules/auth.ts +48 -0
- package/src/modules/base.ts +9 -0
- package/src/modules/ci.ts +12 -0
- package/src/modules/docs.ts +84 -0
- package/src/modules/invitations.ts +45 -0
- package/src/modules/organizations.ts +90 -0
- package/src/modules/sprints.ts +69 -0
- package/src/modules/tasks.ts +110 -0
- package/src/modules/workspaces.ts +94 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/ai/index.js
ADDED
|
@@ -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 @@
|
|
|
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,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 @@
|
|
|
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
|
+
}
|
package/dist/events.d.ts
ADDED
|
@@ -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";
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|