@locusai/sdk 0.8.0 → 0.8.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/index.d.ts +1 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/review-service.d.ts +21 -0
- package/dist/agent/review-service.d.ts.map +1 -0
- package/dist/agent/task-executor.d.ts +1 -1
- package/dist/agent/task-executor.d.ts.map +1 -1
- package/dist/agent/worker.d.ts +2 -3
- package/dist/agent/worker.d.ts.map +1 -1
- package/dist/agent/worker.js +108 -47
- package/dist/ai/claude-runner.d.ts +1 -1
- package/dist/ai/claude-runner.d.ts.map +1 -1
- package/dist/ai/runner.d.ts +1 -1
- package/dist/ai/runner.d.ts.map +1 -1
- package/dist/core/config.d.ts +3 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/index-node.js +132 -70
- package/package.json +2 -2
package/dist/agent/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { CodebaseIndexerService } from "./codebase-indexer-service.js";
|
|
2
2
|
export { DocumentFetcher } from "./document-fetcher.js";
|
|
3
|
+
export { ReviewService, type ReviewServiceDeps } from "./review-service.js";
|
|
3
4
|
export { TaskExecutor } from "./task-executor.js";
|
|
4
5
|
export { AgentWorker, type WorkerConfig } from "./worker.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Sprint } from "@locusai/shared";
|
|
2
|
+
import { LogFn } from "../ai/factory.js";
|
|
3
|
+
import type { AiRunner } from "../ai/runner.js";
|
|
4
|
+
export interface ReviewServiceDeps {
|
|
5
|
+
aiRunner: AiRunner;
|
|
6
|
+
projectPath: string;
|
|
7
|
+
log: LogFn;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Reviews staged git changes and produces a markdown report.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ReviewService {
|
|
13
|
+
private deps;
|
|
14
|
+
constructor(deps: ReviewServiceDeps);
|
|
15
|
+
/**
|
|
16
|
+
* Stages all changes and runs an AI review on the diff.
|
|
17
|
+
* Returns a markdown review report, or null if there are no changes.
|
|
18
|
+
*/
|
|
19
|
+
reviewStagedChanges(sprint: Sprint | null): Promise<string | null>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=review-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-service.d.ts","sourceRoot":"","sources":["../../src/agent/review-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,KAAK,CAAC;CACZ;AAED;;GAEG;AACH,qBAAa,aAAa;IACZ,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,iBAAiB;IAE3C;;;OAGG;IACG,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAkEzE"}
|
|
@@ -7,7 +7,7 @@ export interface TaskExecutorDeps {
|
|
|
7
7
|
log: LogFn;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* Handles task execution
|
|
10
|
+
* Handles direct task execution (single-pass, no separate planning phase)
|
|
11
11
|
*/
|
|
12
12
|
export declare class TaskExecutor {
|
|
13
13
|
private deps;
|
|
@@ -1 +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,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,KAAK,CAAC;CACZ;AAED;;GAEG;AACH,qBAAa,YAAY;IAGX,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,aAAa,CAAgB;gBAEjB,IAAI,EAAE,gBAAgB;IAIpC,OAAO,CACX,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;
|
|
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,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,KAAK,CAAC;CACZ;AAED;;GAEG;AACH,qBAAa,YAAY;IAGX,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,aAAa,CAAgB;gBAEjB,IAAI,EAAE,gBAAgB;IAIpC,OAAO,CACX,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAyBlD"}
|
package/dist/agent/worker.d.ts
CHANGED
|
@@ -20,16 +20,15 @@ export declare class AgentWorker {
|
|
|
20
20
|
private indexerService;
|
|
21
21
|
private documentFetcher;
|
|
22
22
|
private taskExecutor;
|
|
23
|
-
private
|
|
24
|
-
private maxEmpty;
|
|
23
|
+
private reviewService;
|
|
25
24
|
private maxTasks;
|
|
26
25
|
private tasksCompleted;
|
|
27
|
-
private pollInterval;
|
|
28
26
|
constructor(config: WorkerConfig);
|
|
29
27
|
log(message: string, level?: "info" | "success" | "warn" | "error"): void;
|
|
30
28
|
private getActiveSprint;
|
|
31
29
|
private getNextTask;
|
|
32
30
|
private executeTask;
|
|
31
|
+
private runStagedChangesReview;
|
|
33
32
|
run(): Promise<void>;
|
|
34
33
|
}
|
|
35
34
|
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agent/worker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agent/worker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,iBAAiB,CAAC;AAuB5D,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,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AAED;;;GAGG;AACH,qBAAa,WAAW;IAcV,OAAO,CAAC,MAAM;IAb1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAW;IAG3B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAgB;IAGrC,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,cAAc,CAAK;gBAEP,MAAM,EAAE,YAAY;IAuDxC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAgB;YAe5D,eAAe;YAcf,WAAW;YAiBX,WAAW;YA6BX,sBAAsB;IAoC9B,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2D3B"}
|
package/dist/agent/worker.js
CHANGED
|
@@ -512,6 +512,8 @@ __export(exports_worker, {
|
|
|
512
512
|
AgentWorker: () => AgentWorker
|
|
513
513
|
});
|
|
514
514
|
module.exports = __toCommonJS(exports_worker);
|
|
515
|
+
var import_node_fs5 = require("node:fs");
|
|
516
|
+
var import_node_path7 = require("node:path");
|
|
515
517
|
|
|
516
518
|
// src/core/config.ts
|
|
517
519
|
var import_node_path = require("node:path");
|
|
@@ -531,14 +533,22 @@ var LOCUS_CONFIG = {
|
|
|
531
533
|
artifactsDir: "artifacts",
|
|
532
534
|
documentsDir: "documents",
|
|
533
535
|
agentSkillsDir: ".agent/skills",
|
|
534
|
-
sessionsDir: "sessions"
|
|
536
|
+
sessionsDir: "sessions",
|
|
537
|
+
reviewsDir: "reviews",
|
|
538
|
+
plansDir: "plans"
|
|
535
539
|
};
|
|
536
540
|
var LOCUS_GITIGNORE_PATTERNS = [
|
|
537
541
|
"# Locus AI - Session data (user-specific, can grow large)",
|
|
538
542
|
".locus/sessions/",
|
|
539
543
|
"",
|
|
540
544
|
"# Locus AI - Artifacts (local-only, user-specific)",
|
|
541
|
-
".locus/artifacts/"
|
|
545
|
+
".locus/artifacts/",
|
|
546
|
+
"",
|
|
547
|
+
"# Locus AI - Review reports (generated per sprint)",
|
|
548
|
+
".locus/reviews/",
|
|
549
|
+
"",
|
|
550
|
+
"# Locus AI - Plans (generated per task)",
|
|
551
|
+
".locus/plans/"
|
|
542
552
|
];
|
|
543
553
|
function getLocusPath(projectPath, fileName) {
|
|
544
554
|
if (fileName === "contextFile") {
|
|
@@ -633,7 +643,7 @@ class ClaudeRunner {
|
|
|
633
643
|
setEventEmitter(emitter) {
|
|
634
644
|
this.eventEmitter = emitter;
|
|
635
645
|
}
|
|
636
|
-
async run(prompt
|
|
646
|
+
async run(prompt) {
|
|
637
647
|
const maxRetries = 3;
|
|
638
648
|
let lastError = null;
|
|
639
649
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
@@ -1479,7 +1489,7 @@ File tree:
|
|
|
1479
1489
|
${tree}
|
|
1480
1490
|
|
|
1481
1491
|
Return ONLY valid JSON, no markdown formatting.`;
|
|
1482
|
-
const response = await this.deps.aiRunner.run(prompt
|
|
1492
|
+
const response = await this.deps.aiRunner.run(prompt);
|
|
1483
1493
|
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
1484
1494
|
if (jsonMatch) {
|
|
1485
1495
|
return JSON.parse(jsonMatch[0]);
|
|
@@ -1542,6 +1552,66 @@ class DocumentFetcher {
|
|
|
1542
1552
|
}
|
|
1543
1553
|
}
|
|
1544
1554
|
|
|
1555
|
+
// src/agent/review-service.ts
|
|
1556
|
+
var import_node_child_process3 = require("node:child_process");
|
|
1557
|
+
|
|
1558
|
+
class ReviewService {
|
|
1559
|
+
deps;
|
|
1560
|
+
constructor(deps) {
|
|
1561
|
+
this.deps = deps;
|
|
1562
|
+
}
|
|
1563
|
+
async reviewStagedChanges(sprint) {
|
|
1564
|
+
const { projectPath, log } = this.deps;
|
|
1565
|
+
try {
|
|
1566
|
+
import_node_child_process3.execSync("git add -A", { cwd: projectPath, stdio: "pipe" });
|
|
1567
|
+
log("Staged all changes for review.", "info");
|
|
1568
|
+
} catch (err) {
|
|
1569
|
+
log(`Failed to stage changes: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
let diff;
|
|
1573
|
+
try {
|
|
1574
|
+
diff = import_node_child_process3.execSync("git diff --cached --stat && echo '---' && git diff --cached", {
|
|
1575
|
+
cwd: projectPath,
|
|
1576
|
+
maxBuffer: 10 * 1024 * 1024
|
|
1577
|
+
}).toString();
|
|
1578
|
+
} catch (err) {
|
|
1579
|
+
log(`Failed to get staged diff: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
1580
|
+
return null;
|
|
1581
|
+
}
|
|
1582
|
+
if (!diff.trim()) {
|
|
1583
|
+
return null;
|
|
1584
|
+
}
|
|
1585
|
+
const sprintInfo = sprint ? `Sprint: ${sprint.name} (${sprint.id})` : "No active sprint";
|
|
1586
|
+
const reviewPrompt = `# Code Review Request
|
|
1587
|
+
|
|
1588
|
+
## Context
|
|
1589
|
+
${sprintInfo}
|
|
1590
|
+
Date: ${new Date().toISOString()}
|
|
1591
|
+
|
|
1592
|
+
## Staged Changes (git diff)
|
|
1593
|
+
\`\`\`diff
|
|
1594
|
+
${diff}
|
|
1595
|
+
\`\`\`
|
|
1596
|
+
|
|
1597
|
+
## Instructions
|
|
1598
|
+
You are reviewing the staged changes at the end of a sprint. Produce a thorough markdown review report with the following sections:
|
|
1599
|
+
|
|
1600
|
+
1. **Summary** — Brief overview of what changed and why.
|
|
1601
|
+
2. **Files Changed** — List each file with a short description of changes.
|
|
1602
|
+
3. **Code Quality** — Note any code quality concerns (naming, structure, complexity).
|
|
1603
|
+
4. **Potential Issues** — Identify bugs, security issues, edge cases, or regressions.
|
|
1604
|
+
5. **Recommendations** — Actionable suggestions for improvement.
|
|
1605
|
+
6. **Overall Assessment** — A short verdict (e.g., "Looks good", "Needs attention", "Critical issues found").
|
|
1606
|
+
|
|
1607
|
+
Keep the review concise but thorough. Focus on substance over style.
|
|
1608
|
+
Do NOT output <promise>COMPLETE</promise> — just output the review report as markdown.`;
|
|
1609
|
+
log("Running AI review on staged changes...", "info");
|
|
1610
|
+
const report = await this.deps.aiRunner.run(reviewPrompt);
|
|
1611
|
+
return report;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1545
1615
|
// src/core/prompt-builder.ts
|
|
1546
1616
|
var import_node_fs4 = require("node:fs");
|
|
1547
1617
|
var import_node_os2 = require("node:os");
|
|
@@ -1907,29 +1977,8 @@ class TaskExecutor {
|
|
|
1907
1977
|
taskContext: context
|
|
1908
1978
|
});
|
|
1909
1979
|
try {
|
|
1910
|
-
let plan = null;
|
|
1911
|
-
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
1912
|
-
const planningPrompt = `${basePrompt}
|
|
1913
|
-
|
|
1914
|
-
## Phase 1: Planning
|
|
1915
|
-
Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
|
|
1916
|
-
plan = await this.deps.aiRunner.run(planningPrompt, true);
|
|
1917
1980
|
this.deps.log("Starting Execution...", "info");
|
|
1918
|
-
|
|
1919
|
-
if (plan != null) {
|
|
1920
|
-
executionPrompt += `
|
|
1921
|
-
|
|
1922
|
-
## Phase 2: Execution
|
|
1923
|
-
Based on the plan, execute the task:
|
|
1924
|
-
|
|
1925
|
-
${plan}`;
|
|
1926
|
-
} else {
|
|
1927
|
-
executionPrompt += `
|
|
1928
|
-
|
|
1929
|
-
## Execution
|
|
1930
|
-
Execute the task directly.`;
|
|
1931
|
-
}
|
|
1932
|
-
executionPrompt += `
|
|
1981
|
+
const executionPrompt = `${basePrompt}
|
|
1933
1982
|
|
|
1934
1983
|
When finished, output: <promise>COMPLETE</promise>`;
|
|
1935
1984
|
const output = await this.deps.aiRunner.run(executionPrompt);
|
|
@@ -1963,11 +2012,9 @@ class AgentWorker {
|
|
|
1963
2012
|
indexerService;
|
|
1964
2013
|
documentFetcher;
|
|
1965
2014
|
taskExecutor;
|
|
1966
|
-
|
|
1967
|
-
maxEmpty = 60;
|
|
2015
|
+
reviewService;
|
|
1968
2016
|
maxTasks = 50;
|
|
1969
2017
|
tasksCompleted = 0;
|
|
1970
|
-
pollInterval = 1e4;
|
|
1971
2018
|
constructor(config) {
|
|
1972
2019
|
this.config = config;
|
|
1973
2020
|
const projectPath = config.projectPath || process.cwd();
|
|
@@ -2004,6 +2051,11 @@ class AgentWorker {
|
|
|
2004
2051
|
projectPath,
|
|
2005
2052
|
log
|
|
2006
2053
|
});
|
|
2054
|
+
this.reviewService = new ReviewService({
|
|
2055
|
+
aiRunner: this.aiRunner,
|
|
2056
|
+
projectPath,
|
|
2057
|
+
log
|
|
2058
|
+
});
|
|
2007
2059
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
2008
2060
|
this.log(`Using ${providerLabel} CLI for all phases`, "info");
|
|
2009
2061
|
}
|
|
@@ -2049,33 +2101,42 @@ class AgentWorker {
|
|
|
2049
2101
|
await this.indexerService.reindex();
|
|
2050
2102
|
return result;
|
|
2051
2103
|
}
|
|
2104
|
+
async runStagedChangesReview(sprint) {
|
|
2105
|
+
try {
|
|
2106
|
+
const report = await this.reviewService.reviewStagedChanges(sprint);
|
|
2107
|
+
if (report) {
|
|
2108
|
+
const reviewsDir = import_node_path7.join(this.config.projectPath, LOCUS_CONFIG.dir, "reviews");
|
|
2109
|
+
if (!import_node_fs5.existsSync(reviewsDir)) {
|
|
2110
|
+
import_node_fs5.mkdirSync(reviewsDir, { recursive: true });
|
|
2111
|
+
}
|
|
2112
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
2113
|
+
const sprintSlug = sprint?.name ? sprint.name.toLowerCase().replace(/\s+/g, "-").slice(0, 40) : "no-sprint";
|
|
2114
|
+
const fileName = `review-${sprintSlug}-${timestamp}.md`;
|
|
2115
|
+
const filePath = import_node_path7.join(reviewsDir, fileName);
|
|
2116
|
+
import_node_fs5.writeFileSync(filePath, report);
|
|
2117
|
+
this.log(`Review report saved to .locus/reviews/${fileName}`, "success");
|
|
2118
|
+
} else {
|
|
2119
|
+
this.log("No staged changes to review.", "info");
|
|
2120
|
+
}
|
|
2121
|
+
} catch (err) {
|
|
2122
|
+
this.log(`Review failed: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2052
2125
|
async run() {
|
|
2053
2126
|
this.log(`Agent started in ${this.config.projectPath || process.cwd()}`, "success");
|
|
2054
2127
|
const sprint = await this.getActiveSprint();
|
|
2055
2128
|
if (sprint) {
|
|
2056
|
-
this.log(`Active sprint found: ${sprint.name}
|
|
2057
|
-
try {
|
|
2058
|
-
await this.client.sprints.triggerAIPlanning(sprint.id, this.config.workspaceId);
|
|
2059
|
-
this.log(`Sprint plan sync checked on server.`, "success");
|
|
2060
|
-
} catch (err) {
|
|
2061
|
-
this.log(`Sprint planning sync failed (non-critical): ${err instanceof Error ? err.message : String(err)}`, "warn");
|
|
2062
|
-
}
|
|
2129
|
+
this.log(`Active sprint found: ${sprint.name}`, "info");
|
|
2063
2130
|
} else {
|
|
2064
|
-
this.log("No active sprint found
|
|
2131
|
+
this.log("No active sprint found.", "warn");
|
|
2065
2132
|
}
|
|
2066
|
-
while (this.tasksCompleted < this.maxTasks
|
|
2133
|
+
while (this.tasksCompleted < this.maxTasks) {
|
|
2067
2134
|
const task = await this.getNextTask();
|
|
2068
2135
|
if (!task) {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
this.consecutiveEmpty++;
|
|
2073
|
-
if (this.consecutiveEmpty >= this.maxEmpty)
|
|
2074
|
-
break;
|
|
2075
|
-
await new Promise((r) => setTimeout(r, this.pollInterval));
|
|
2076
|
-
continue;
|
|
2136
|
+
this.log("No tasks remaining. Running review on staged changes...", "info");
|
|
2137
|
+
await this.runStagedChangesReview(sprint);
|
|
2138
|
+
break;
|
|
2077
2139
|
}
|
|
2078
|
-
this.consecutiveEmpty = 0;
|
|
2079
2140
|
this.log(`Claimed: ${task.title}`, "success");
|
|
2080
2141
|
const result = await this.executeTask(task);
|
|
2081
2142
|
try {
|
|
@@ -14,7 +14,7 @@ export declare class ClaudeRunner implements AiRunner {
|
|
|
14
14
|
* Set an event emitter to receive execution events.
|
|
15
15
|
*/
|
|
16
16
|
setEventEmitter(emitter: ExecEventEmitter): void;
|
|
17
|
-
run(prompt: string
|
|
17
|
+
run(prompt: string): Promise<string>;
|
|
18
18
|
runStream(prompt: string): AsyncGenerator<StreamChunk, void, unknown>;
|
|
19
19
|
/**
|
|
20
20
|
* Emit an event corresponding to a stream chunk.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAgC5C,qBAAa,YAAa,YAAW,QAAQ;IAQzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IARd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,WAAW,CAA+C;gBAGhE,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA;IAKrB;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAI1C,GAAG,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAgC5C,qBAAa,YAAa,YAAW,QAAQ;IAQzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IARd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,WAAW,CAA+C;gBAGhE,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA;IAKrB;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAI1C,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IAqK5E;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;IAwE1B,OAAO,CAAC,UAAU;IAkFlB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;CAO7B"}
|
package/dist/ai/runner.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Provider } from "../core/config.js";
|
|
|
2
2
|
import type { ExecEventEmitter } from "../exec/event-emitter.js";
|
|
3
3
|
import type { StreamChunk } from "../exec/types.js";
|
|
4
4
|
export interface AiRunner {
|
|
5
|
-
run(prompt: string
|
|
5
|
+
run(prompt: string): Promise<string>;
|
|
6
6
|
runStream(prompt: string): AsyncGenerator<StreamChunk, void, unknown>;
|
|
7
7
|
/**
|
|
8
8
|
* Set an event emitter to receive execution events.
|
package/dist/ai/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/ai/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/ai/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACtE;;OAEG;IACH,eAAe,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACnD;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC"}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -13,8 +13,10 @@ export declare const LOCUS_CONFIG: {
|
|
|
13
13
|
documentsDir: string;
|
|
14
14
|
agentSkillsDir: string;
|
|
15
15
|
sessionsDir: string;
|
|
16
|
+
reviewsDir: string;
|
|
17
|
+
plansDir: string;
|
|
16
18
|
};
|
|
17
|
-
export declare const LOCUS_GITIGNORE_PATTERNS: readonly ["# Locus AI - Session data (user-specific, can grow large)", ".locus/sessions/", "", "# Locus AI - Artifacts (local-only, user-specific)", ".locus/artifacts/"];
|
|
19
|
+
export declare const LOCUS_GITIGNORE_PATTERNS: readonly ["# Locus AI - Session data (user-specific, can grow large)", ".locus/sessions/", "", "# Locus AI - Artifacts (local-only, user-specific)", ".locus/artifacts/", "", "# Locus AI - Review reports (generated per sprint)", ".locus/reviews/", "", "# Locus AI - Plans (generated per task)", ".locus/plans/"];
|
|
18
20
|
export declare function getLocusPath(projectPath: string, fileName: keyof typeof LOCUS_CONFIG): string;
|
|
19
21
|
/**
|
|
20
22
|
* Gets the artifacts directory path for a specific agent.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;CAGX,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEhE,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAGlD,CAAC;AAEF,eAAO,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;CAGX,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEhE,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAGlD,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;CAWxB,CAAC;AAIF,eAAO,MAAM,wBAAwB,wTAY3B,CAAC;AAEX,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,OAAO,YAAY,GAClC,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,MAAM,CASR"}
|
package/dist/index-node.js
CHANGED
|
@@ -512,6 +512,8 @@ __export(exports_worker, {
|
|
|
512
512
|
AgentWorker: () => AgentWorker
|
|
513
513
|
});
|
|
514
514
|
module.exports = __toCommonJS(exports_worker);
|
|
515
|
+
var import_node_fs5 = require("node:fs");
|
|
516
|
+
var import_node_path7 = require("node:path");
|
|
515
517
|
|
|
516
518
|
// src/core/config.ts
|
|
517
519
|
var import_node_path = require("node:path");
|
|
@@ -531,14 +533,22 @@ var LOCUS_CONFIG = {
|
|
|
531
533
|
artifactsDir: "artifacts",
|
|
532
534
|
documentsDir: "documents",
|
|
533
535
|
agentSkillsDir: ".agent/skills",
|
|
534
|
-
sessionsDir: "sessions"
|
|
536
|
+
sessionsDir: "sessions",
|
|
537
|
+
reviewsDir: "reviews",
|
|
538
|
+
plansDir: "plans"
|
|
535
539
|
};
|
|
536
540
|
var LOCUS_GITIGNORE_PATTERNS = [
|
|
537
541
|
"# Locus AI - Session data (user-specific, can grow large)",
|
|
538
542
|
".locus/sessions/",
|
|
539
543
|
"",
|
|
540
544
|
"# Locus AI - Artifacts (local-only, user-specific)",
|
|
541
|
-
".locus/artifacts/"
|
|
545
|
+
".locus/artifacts/",
|
|
546
|
+
"",
|
|
547
|
+
"# Locus AI - Review reports (generated per sprint)",
|
|
548
|
+
".locus/reviews/",
|
|
549
|
+
"",
|
|
550
|
+
"# Locus AI - Plans (generated per task)",
|
|
551
|
+
".locus/plans/"
|
|
542
552
|
];
|
|
543
553
|
function getLocusPath(projectPath, fileName) {
|
|
544
554
|
if (fileName === "contextFile") {
|
|
@@ -633,7 +643,7 @@ class ClaudeRunner {
|
|
|
633
643
|
setEventEmitter(emitter) {
|
|
634
644
|
this.eventEmitter = emitter;
|
|
635
645
|
}
|
|
636
|
-
async run(prompt
|
|
646
|
+
async run(prompt) {
|
|
637
647
|
const maxRetries = 3;
|
|
638
648
|
let lastError = null;
|
|
639
649
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
@@ -1479,7 +1489,7 @@ File tree:
|
|
|
1479
1489
|
${tree}
|
|
1480
1490
|
|
|
1481
1491
|
Return ONLY valid JSON, no markdown formatting.`;
|
|
1482
|
-
const response = await this.deps.aiRunner.run(prompt
|
|
1492
|
+
const response = await this.deps.aiRunner.run(prompt);
|
|
1483
1493
|
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
1484
1494
|
if (jsonMatch) {
|
|
1485
1495
|
return JSON.parse(jsonMatch[0]);
|
|
@@ -1542,6 +1552,66 @@ class DocumentFetcher {
|
|
|
1542
1552
|
}
|
|
1543
1553
|
}
|
|
1544
1554
|
|
|
1555
|
+
// src/agent/review-service.ts
|
|
1556
|
+
var import_node_child_process3 = require("node:child_process");
|
|
1557
|
+
|
|
1558
|
+
class ReviewService {
|
|
1559
|
+
deps;
|
|
1560
|
+
constructor(deps) {
|
|
1561
|
+
this.deps = deps;
|
|
1562
|
+
}
|
|
1563
|
+
async reviewStagedChanges(sprint) {
|
|
1564
|
+
const { projectPath, log } = this.deps;
|
|
1565
|
+
try {
|
|
1566
|
+
import_node_child_process3.execSync("git add -A", { cwd: projectPath, stdio: "pipe" });
|
|
1567
|
+
log("Staged all changes for review.", "info");
|
|
1568
|
+
} catch (err) {
|
|
1569
|
+
log(`Failed to stage changes: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
let diff;
|
|
1573
|
+
try {
|
|
1574
|
+
diff = import_node_child_process3.execSync("git diff --cached --stat && echo '---' && git diff --cached", {
|
|
1575
|
+
cwd: projectPath,
|
|
1576
|
+
maxBuffer: 10 * 1024 * 1024
|
|
1577
|
+
}).toString();
|
|
1578
|
+
} catch (err) {
|
|
1579
|
+
log(`Failed to get staged diff: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
1580
|
+
return null;
|
|
1581
|
+
}
|
|
1582
|
+
if (!diff.trim()) {
|
|
1583
|
+
return null;
|
|
1584
|
+
}
|
|
1585
|
+
const sprintInfo = sprint ? `Sprint: ${sprint.name} (${sprint.id})` : "No active sprint";
|
|
1586
|
+
const reviewPrompt = `# Code Review Request
|
|
1587
|
+
|
|
1588
|
+
## Context
|
|
1589
|
+
${sprintInfo}
|
|
1590
|
+
Date: ${new Date().toISOString()}
|
|
1591
|
+
|
|
1592
|
+
## Staged Changes (git diff)
|
|
1593
|
+
\`\`\`diff
|
|
1594
|
+
${diff}
|
|
1595
|
+
\`\`\`
|
|
1596
|
+
|
|
1597
|
+
## Instructions
|
|
1598
|
+
You are reviewing the staged changes at the end of a sprint. Produce a thorough markdown review report with the following sections:
|
|
1599
|
+
|
|
1600
|
+
1. **Summary** — Brief overview of what changed and why.
|
|
1601
|
+
2. **Files Changed** — List each file with a short description of changes.
|
|
1602
|
+
3. **Code Quality** — Note any code quality concerns (naming, structure, complexity).
|
|
1603
|
+
4. **Potential Issues** — Identify bugs, security issues, edge cases, or regressions.
|
|
1604
|
+
5. **Recommendations** — Actionable suggestions for improvement.
|
|
1605
|
+
6. **Overall Assessment** — A short verdict (e.g., "Looks good", "Needs attention", "Critical issues found").
|
|
1606
|
+
|
|
1607
|
+
Keep the review concise but thorough. Focus on substance over style.
|
|
1608
|
+
Do NOT output <promise>COMPLETE</promise> — just output the review report as markdown.`;
|
|
1609
|
+
log("Running AI review on staged changes...", "info");
|
|
1610
|
+
const report = await this.deps.aiRunner.run(reviewPrompt);
|
|
1611
|
+
return report;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1545
1615
|
// src/core/prompt-builder.ts
|
|
1546
1616
|
var import_node_fs4 = require("node:fs");
|
|
1547
1617
|
var import_node_os2 = require("node:os");
|
|
@@ -1907,29 +1977,8 @@ class TaskExecutor {
|
|
|
1907
1977
|
taskContext: context
|
|
1908
1978
|
});
|
|
1909
1979
|
try {
|
|
1910
|
-
let plan = null;
|
|
1911
|
-
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
1912
|
-
const planningPrompt = `${basePrompt}
|
|
1913
|
-
|
|
1914
|
-
## Phase 1: Planning
|
|
1915
|
-
Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
|
|
1916
|
-
plan = await this.deps.aiRunner.run(planningPrompt, true);
|
|
1917
1980
|
this.deps.log("Starting Execution...", "info");
|
|
1918
|
-
|
|
1919
|
-
if (plan != null) {
|
|
1920
|
-
executionPrompt += `
|
|
1921
|
-
|
|
1922
|
-
## Phase 2: Execution
|
|
1923
|
-
Based on the plan, execute the task:
|
|
1924
|
-
|
|
1925
|
-
${plan}`;
|
|
1926
|
-
} else {
|
|
1927
|
-
executionPrompt += `
|
|
1928
|
-
|
|
1929
|
-
## Execution
|
|
1930
|
-
Execute the task directly.`;
|
|
1931
|
-
}
|
|
1932
|
-
executionPrompt += `
|
|
1981
|
+
const executionPrompt = `${basePrompt}
|
|
1933
1982
|
|
|
1934
1983
|
When finished, output: <promise>COMPLETE</promise>`;
|
|
1935
1984
|
const output = await this.deps.aiRunner.run(executionPrompt);
|
|
@@ -1963,11 +2012,9 @@ class AgentWorker {
|
|
|
1963
2012
|
indexerService;
|
|
1964
2013
|
documentFetcher;
|
|
1965
2014
|
taskExecutor;
|
|
1966
|
-
|
|
1967
|
-
maxEmpty = 60;
|
|
2015
|
+
reviewService;
|
|
1968
2016
|
maxTasks = 50;
|
|
1969
2017
|
tasksCompleted = 0;
|
|
1970
|
-
pollInterval = 1e4;
|
|
1971
2018
|
constructor(config) {
|
|
1972
2019
|
this.config = config;
|
|
1973
2020
|
const projectPath = config.projectPath || process.cwd();
|
|
@@ -2004,6 +2051,11 @@ class AgentWorker {
|
|
|
2004
2051
|
projectPath,
|
|
2005
2052
|
log
|
|
2006
2053
|
});
|
|
2054
|
+
this.reviewService = new ReviewService({
|
|
2055
|
+
aiRunner: this.aiRunner,
|
|
2056
|
+
projectPath,
|
|
2057
|
+
log
|
|
2058
|
+
});
|
|
2007
2059
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
2008
2060
|
this.log(`Using ${providerLabel} CLI for all phases`, "info");
|
|
2009
2061
|
}
|
|
@@ -2049,33 +2101,42 @@ class AgentWorker {
|
|
|
2049
2101
|
await this.indexerService.reindex();
|
|
2050
2102
|
return result;
|
|
2051
2103
|
}
|
|
2104
|
+
async runStagedChangesReview(sprint) {
|
|
2105
|
+
try {
|
|
2106
|
+
const report = await this.reviewService.reviewStagedChanges(sprint);
|
|
2107
|
+
if (report) {
|
|
2108
|
+
const reviewsDir = import_node_path7.join(this.config.projectPath, LOCUS_CONFIG.dir, "reviews");
|
|
2109
|
+
if (!import_node_fs5.existsSync(reviewsDir)) {
|
|
2110
|
+
import_node_fs5.mkdirSync(reviewsDir, { recursive: true });
|
|
2111
|
+
}
|
|
2112
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
2113
|
+
const sprintSlug = sprint?.name ? sprint.name.toLowerCase().replace(/\s+/g, "-").slice(0, 40) : "no-sprint";
|
|
2114
|
+
const fileName = `review-${sprintSlug}-${timestamp}.md`;
|
|
2115
|
+
const filePath = import_node_path7.join(reviewsDir, fileName);
|
|
2116
|
+
import_node_fs5.writeFileSync(filePath, report);
|
|
2117
|
+
this.log(`Review report saved to .locus/reviews/${fileName}`, "success");
|
|
2118
|
+
} else {
|
|
2119
|
+
this.log("No staged changes to review.", "info");
|
|
2120
|
+
}
|
|
2121
|
+
} catch (err) {
|
|
2122
|
+
this.log(`Review failed: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2052
2125
|
async run() {
|
|
2053
2126
|
this.log(`Agent started in ${this.config.projectPath || process.cwd()}`, "success");
|
|
2054
2127
|
const sprint = await this.getActiveSprint();
|
|
2055
2128
|
if (sprint) {
|
|
2056
|
-
this.log(`Active sprint found: ${sprint.name}
|
|
2057
|
-
try {
|
|
2058
|
-
await this.client.sprints.triggerAIPlanning(sprint.id, this.config.workspaceId);
|
|
2059
|
-
this.log(`Sprint plan sync checked on server.`, "success");
|
|
2060
|
-
} catch (err) {
|
|
2061
|
-
this.log(`Sprint planning sync failed (non-critical): ${err instanceof Error ? err.message : String(err)}`, "warn");
|
|
2062
|
-
}
|
|
2129
|
+
this.log(`Active sprint found: ${sprint.name}`, "info");
|
|
2063
2130
|
} else {
|
|
2064
|
-
this.log("No active sprint found
|
|
2131
|
+
this.log("No active sprint found.", "warn");
|
|
2065
2132
|
}
|
|
2066
|
-
while (this.tasksCompleted < this.maxTasks
|
|
2133
|
+
while (this.tasksCompleted < this.maxTasks) {
|
|
2067
2134
|
const task = await this.getNextTask();
|
|
2068
2135
|
if (!task) {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
this.consecutiveEmpty++;
|
|
2073
|
-
if (this.consecutiveEmpty >= this.maxEmpty)
|
|
2074
|
-
break;
|
|
2075
|
-
await new Promise((r) => setTimeout(r, this.pollInterval));
|
|
2076
|
-
continue;
|
|
2136
|
+
this.log("No tasks remaining. Running review on staged changes...", "info");
|
|
2137
|
+
await this.runStagedChangesReview(sprint);
|
|
2138
|
+
break;
|
|
2077
2139
|
}
|
|
2078
|
-
this.consecutiveEmpty = 0;
|
|
2079
2140
|
this.log(`Claimed: ${task.title}`, "success");
|
|
2080
2141
|
const result = await this.executeTask(task);
|
|
2081
2142
|
try {
|
|
@@ -2157,6 +2218,7 @@ __export(exports_index_node, {
|
|
|
2157
2218
|
TasksModule: () => TasksModule,
|
|
2158
2219
|
TaskExecutor: () => TaskExecutor,
|
|
2159
2220
|
SprintsModule: () => SprintsModule,
|
|
2221
|
+
ReviewService: () => ReviewService,
|
|
2160
2222
|
PromptBuilder: () => PromptBuilder,
|
|
2161
2223
|
PROVIDER: () => PROVIDER,
|
|
2162
2224
|
OrganizationsModule: () => OrganizationsModule,
|
|
@@ -2598,8 +2660,8 @@ class ExecEventEmitter {
|
|
|
2598
2660
|
}
|
|
2599
2661
|
}
|
|
2600
2662
|
// src/exec/history-manager.ts
|
|
2601
|
-
var
|
|
2602
|
-
var
|
|
2663
|
+
var import_node_fs6 = require("node:fs");
|
|
2664
|
+
var import_node_path8 = require("node:path");
|
|
2603
2665
|
var DEFAULT_MAX_SESSIONS = 30;
|
|
2604
2666
|
function generateSessionId2() {
|
|
2605
2667
|
const timestamp = Date.now().toString(36);
|
|
@@ -2611,30 +2673,30 @@ class HistoryManager {
|
|
|
2611
2673
|
historyDir;
|
|
2612
2674
|
maxSessions;
|
|
2613
2675
|
constructor(projectPath, options) {
|
|
2614
|
-
this.historyDir = options?.historyDir ??
|
|
2676
|
+
this.historyDir = options?.historyDir ?? import_node_path8.join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.sessionsDir);
|
|
2615
2677
|
this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
|
|
2616
2678
|
this.ensureHistoryDir();
|
|
2617
2679
|
}
|
|
2618
2680
|
ensureHistoryDir() {
|
|
2619
|
-
if (!
|
|
2620
|
-
|
|
2681
|
+
if (!import_node_fs6.existsSync(this.historyDir)) {
|
|
2682
|
+
import_node_fs6.mkdirSync(this.historyDir, { recursive: true });
|
|
2621
2683
|
}
|
|
2622
2684
|
}
|
|
2623
2685
|
getSessionPath(sessionId) {
|
|
2624
|
-
return
|
|
2686
|
+
return import_node_path8.join(this.historyDir, `${sessionId}.json`);
|
|
2625
2687
|
}
|
|
2626
2688
|
saveSession(session) {
|
|
2627
2689
|
const filePath = this.getSessionPath(session.id);
|
|
2628
2690
|
session.updatedAt = Date.now();
|
|
2629
|
-
|
|
2691
|
+
import_node_fs6.writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
2630
2692
|
}
|
|
2631
2693
|
loadSession(sessionId) {
|
|
2632
2694
|
const filePath = this.getSessionPath(sessionId);
|
|
2633
|
-
if (!
|
|
2695
|
+
if (!import_node_fs6.existsSync(filePath)) {
|
|
2634
2696
|
return null;
|
|
2635
2697
|
}
|
|
2636
2698
|
try {
|
|
2637
|
-
const content =
|
|
2699
|
+
const content = import_node_fs6.readFileSync(filePath, "utf-8");
|
|
2638
2700
|
return JSON.parse(content);
|
|
2639
2701
|
} catch {
|
|
2640
2702
|
return null;
|
|
@@ -2642,18 +2704,18 @@ class HistoryManager {
|
|
|
2642
2704
|
}
|
|
2643
2705
|
deleteSession(sessionId) {
|
|
2644
2706
|
const filePath = this.getSessionPath(sessionId);
|
|
2645
|
-
if (!
|
|
2707
|
+
if (!import_node_fs6.existsSync(filePath)) {
|
|
2646
2708
|
return false;
|
|
2647
2709
|
}
|
|
2648
2710
|
try {
|
|
2649
|
-
|
|
2711
|
+
import_node_fs6.rmSync(filePath);
|
|
2650
2712
|
return true;
|
|
2651
2713
|
} catch {
|
|
2652
2714
|
return false;
|
|
2653
2715
|
}
|
|
2654
2716
|
}
|
|
2655
2717
|
listSessions(options) {
|
|
2656
|
-
const files =
|
|
2718
|
+
const files = import_node_fs6.readdirSync(this.historyDir);
|
|
2657
2719
|
let sessions = [];
|
|
2658
2720
|
for (const file of files) {
|
|
2659
2721
|
if (file.endsWith(".json")) {
|
|
@@ -2726,11 +2788,11 @@ class HistoryManager {
|
|
|
2726
2788
|
return deleted;
|
|
2727
2789
|
}
|
|
2728
2790
|
getSessionCount() {
|
|
2729
|
-
const files =
|
|
2791
|
+
const files = import_node_fs6.readdirSync(this.historyDir);
|
|
2730
2792
|
return files.filter((f) => f.endsWith(".json")).length;
|
|
2731
2793
|
}
|
|
2732
2794
|
sessionExists(sessionId) {
|
|
2733
|
-
return
|
|
2795
|
+
return import_node_fs6.existsSync(this.getSessionPath(sessionId));
|
|
2734
2796
|
}
|
|
2735
2797
|
findSessionByPartialId(partialId) {
|
|
2736
2798
|
const sessions = this.listSessions();
|
|
@@ -2744,12 +2806,12 @@ class HistoryManager {
|
|
|
2744
2806
|
return this.historyDir;
|
|
2745
2807
|
}
|
|
2746
2808
|
clearAllSessions() {
|
|
2747
|
-
const files =
|
|
2809
|
+
const files = import_node_fs6.readdirSync(this.historyDir);
|
|
2748
2810
|
let deleted = 0;
|
|
2749
2811
|
for (const file of files) {
|
|
2750
2812
|
if (file.endsWith(".json")) {
|
|
2751
2813
|
try {
|
|
2752
|
-
|
|
2814
|
+
import_node_fs6.rmSync(import_node_path8.join(this.historyDir, file));
|
|
2753
2815
|
deleted++;
|
|
2754
2816
|
} catch {}
|
|
2755
2817
|
}
|
|
@@ -3014,9 +3076,9 @@ ${currentPrompt}`);
|
|
|
3014
3076
|
}
|
|
3015
3077
|
}
|
|
3016
3078
|
// src/orchestrator.ts
|
|
3017
|
-
var
|
|
3018
|
-
var
|
|
3019
|
-
var
|
|
3079
|
+
var import_node_child_process4 = require("node:child_process");
|
|
3080
|
+
var import_node_fs7 = require("node:fs");
|
|
3081
|
+
var import_node_path9 = require("node:path");
|
|
3020
3082
|
var import_node_url = require("node:url");
|
|
3021
3083
|
var import_shared3 = require("@locusai/shared");
|
|
3022
3084
|
var import_events4 = require("events");
|
|
@@ -3112,9 +3174,9 @@ ${c.success("✅ Orchestrator finished")}`);
|
|
|
3112
3174
|
`);
|
|
3113
3175
|
const potentialPaths = [];
|
|
3114
3176
|
const currentModulePath = import_node_url.fileURLToPath("file:///home/runner/work/locusai/locusai/packages/sdk/src/orchestrator.ts");
|
|
3115
|
-
const currentModuleDir =
|
|
3116
|
-
potentialPaths.push(
|
|
3117
|
-
const workerPath = potentialPaths.find((p) =>
|
|
3177
|
+
const currentModuleDir = import_node_path9.dirname(currentModulePath);
|
|
3178
|
+
potentialPaths.push(import_node_path9.join(currentModuleDir, "agent", "worker.js"), import_node_path9.join(currentModuleDir, "worker.js"), import_node_path9.join(currentModuleDir, "agent", "worker.ts"));
|
|
3179
|
+
const workerPath = potentialPaths.find((p) => import_node_fs7.existsSync(p));
|
|
3118
3180
|
if (!workerPath) {
|
|
3119
3181
|
throw new Error(`Worker file not found. Checked: ${potentialPaths.join(", ")}. ` + `Make sure the SDK is properly built and installed.`);
|
|
3120
3182
|
}
|
|
@@ -3139,7 +3201,7 @@ ${c.success("✅ Orchestrator finished")}`);
|
|
|
3139
3201
|
if (this.resolvedSprintId) {
|
|
3140
3202
|
workerArgs.push("--sprint-id", this.resolvedSprintId);
|
|
3141
3203
|
}
|
|
3142
|
-
const agentProcess =
|
|
3204
|
+
const agentProcess = import_node_child_process4.spawn(process.execPath, [workerPath, ...workerArgs], {
|
|
3143
3205
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3144
3206
|
env: {
|
|
3145
3207
|
...process.env,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"clean": "rm -rf node_modules"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@locusai/shared": "^0.8.
|
|
33
|
+
"@locusai/shared": "^0.8.1",
|
|
34
34
|
"axios": "^1.13.2",
|
|
35
35
|
"events": "^3.3.0",
|
|
36
36
|
"globby": "^14.0.2"
|