@locusai/sdk 0.7.7 → 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 +112 -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 +136 -70
- package/dist/index.js +4 -0
- package/dist/modules/workspaces.d.ts +6 -1
- package/dist/modules/workspaces.d.ts.map +1 -1
- 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
|
@@ -386,6 +386,10 @@ class WorkspacesModule extends BaseModule {
|
|
|
386
386
|
const { data } = await this.api.get(`/workspaces/${id}/stats`);
|
|
387
387
|
return data;
|
|
388
388
|
}
|
|
389
|
+
async getManifestStatus(workspaceId) {
|
|
390
|
+
const { data } = await this.api.get(`/workspaces/${workspaceId}/manifest-status`);
|
|
391
|
+
return data;
|
|
392
|
+
}
|
|
389
393
|
async getActivity(id, limit) {
|
|
390
394
|
const { data } = await this.api.get(`/workspaces/${id}/activity`, {
|
|
391
395
|
params: { limit }
|
|
@@ -508,6 +512,8 @@ __export(exports_worker, {
|
|
|
508
512
|
AgentWorker: () => AgentWorker
|
|
509
513
|
});
|
|
510
514
|
module.exports = __toCommonJS(exports_worker);
|
|
515
|
+
var import_node_fs5 = require("node:fs");
|
|
516
|
+
var import_node_path7 = require("node:path");
|
|
511
517
|
|
|
512
518
|
// src/core/config.ts
|
|
513
519
|
var import_node_path = require("node:path");
|
|
@@ -527,14 +533,22 @@ var LOCUS_CONFIG = {
|
|
|
527
533
|
artifactsDir: "artifacts",
|
|
528
534
|
documentsDir: "documents",
|
|
529
535
|
agentSkillsDir: ".agent/skills",
|
|
530
|
-
sessionsDir: "sessions"
|
|
536
|
+
sessionsDir: "sessions",
|
|
537
|
+
reviewsDir: "reviews",
|
|
538
|
+
plansDir: "plans"
|
|
531
539
|
};
|
|
532
540
|
var LOCUS_GITIGNORE_PATTERNS = [
|
|
533
541
|
"# Locus AI - Session data (user-specific, can grow large)",
|
|
534
542
|
".locus/sessions/",
|
|
535
543
|
"",
|
|
536
544
|
"# Locus AI - Artifacts (local-only, user-specific)",
|
|
537
|
-
".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/"
|
|
538
552
|
];
|
|
539
553
|
function getLocusPath(projectPath, fileName) {
|
|
540
554
|
if (fileName === "contextFile") {
|
|
@@ -629,7 +643,7 @@ class ClaudeRunner {
|
|
|
629
643
|
setEventEmitter(emitter) {
|
|
630
644
|
this.eventEmitter = emitter;
|
|
631
645
|
}
|
|
632
|
-
async run(prompt
|
|
646
|
+
async run(prompt) {
|
|
633
647
|
const maxRetries = 3;
|
|
634
648
|
let lastError = null;
|
|
635
649
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
@@ -1475,7 +1489,7 @@ File tree:
|
|
|
1475
1489
|
${tree}
|
|
1476
1490
|
|
|
1477
1491
|
Return ONLY valid JSON, no markdown formatting.`;
|
|
1478
|
-
const response = await this.deps.aiRunner.run(prompt
|
|
1492
|
+
const response = await this.deps.aiRunner.run(prompt);
|
|
1479
1493
|
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
1480
1494
|
if (jsonMatch) {
|
|
1481
1495
|
return JSON.parse(jsonMatch[0]);
|
|
@@ -1538,6 +1552,66 @@ class DocumentFetcher {
|
|
|
1538
1552
|
}
|
|
1539
1553
|
}
|
|
1540
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
|
+
|
|
1541
1615
|
// src/core/prompt-builder.ts
|
|
1542
1616
|
var import_node_fs4 = require("node:fs");
|
|
1543
1617
|
var import_node_os2 = require("node:os");
|
|
@@ -1903,29 +1977,8 @@ class TaskExecutor {
|
|
|
1903
1977
|
taskContext: context
|
|
1904
1978
|
});
|
|
1905
1979
|
try {
|
|
1906
|
-
let plan = null;
|
|
1907
|
-
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
1908
|
-
const planningPrompt = `${basePrompt}
|
|
1909
|
-
|
|
1910
|
-
## Phase 1: Planning
|
|
1911
|
-
Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
|
|
1912
|
-
plan = await this.deps.aiRunner.run(planningPrompt, true);
|
|
1913
1980
|
this.deps.log("Starting Execution...", "info");
|
|
1914
|
-
|
|
1915
|
-
if (plan != null) {
|
|
1916
|
-
executionPrompt += `
|
|
1917
|
-
|
|
1918
|
-
## Phase 2: Execution
|
|
1919
|
-
Based on the plan, execute the task:
|
|
1920
|
-
|
|
1921
|
-
${plan}`;
|
|
1922
|
-
} else {
|
|
1923
|
-
executionPrompt += `
|
|
1924
|
-
|
|
1925
|
-
## Execution
|
|
1926
|
-
Execute the task directly.`;
|
|
1927
|
-
}
|
|
1928
|
-
executionPrompt += `
|
|
1981
|
+
const executionPrompt = `${basePrompt}
|
|
1929
1982
|
|
|
1930
1983
|
When finished, output: <promise>COMPLETE</promise>`;
|
|
1931
1984
|
const output = await this.deps.aiRunner.run(executionPrompt);
|
|
@@ -1959,11 +2012,9 @@ class AgentWorker {
|
|
|
1959
2012
|
indexerService;
|
|
1960
2013
|
documentFetcher;
|
|
1961
2014
|
taskExecutor;
|
|
1962
|
-
|
|
1963
|
-
maxEmpty = 60;
|
|
2015
|
+
reviewService;
|
|
1964
2016
|
maxTasks = 50;
|
|
1965
2017
|
tasksCompleted = 0;
|
|
1966
|
-
pollInterval = 1e4;
|
|
1967
2018
|
constructor(config) {
|
|
1968
2019
|
this.config = config;
|
|
1969
2020
|
const projectPath = config.projectPath || process.cwd();
|
|
@@ -2000,6 +2051,11 @@ class AgentWorker {
|
|
|
2000
2051
|
projectPath,
|
|
2001
2052
|
log
|
|
2002
2053
|
});
|
|
2054
|
+
this.reviewService = new ReviewService({
|
|
2055
|
+
aiRunner: this.aiRunner,
|
|
2056
|
+
projectPath,
|
|
2057
|
+
log
|
|
2058
|
+
});
|
|
2003
2059
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
2004
2060
|
this.log(`Using ${providerLabel} CLI for all phases`, "info");
|
|
2005
2061
|
}
|
|
@@ -2045,33 +2101,42 @@ class AgentWorker {
|
|
|
2045
2101
|
await this.indexerService.reindex();
|
|
2046
2102
|
return result;
|
|
2047
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
|
+
}
|
|
2048
2125
|
async run() {
|
|
2049
2126
|
this.log(`Agent started in ${this.config.projectPath || process.cwd()}`, "success");
|
|
2050
2127
|
const sprint = await this.getActiveSprint();
|
|
2051
2128
|
if (sprint) {
|
|
2052
|
-
this.log(`Active sprint found: ${sprint.name}
|
|
2053
|
-
try {
|
|
2054
|
-
await this.client.sprints.triggerAIPlanning(sprint.id, this.config.workspaceId);
|
|
2055
|
-
this.log(`Sprint plan sync checked on server.`, "success");
|
|
2056
|
-
} catch (err) {
|
|
2057
|
-
this.log(`Sprint planning sync failed (non-critical): ${err instanceof Error ? err.message : String(err)}`, "warn");
|
|
2058
|
-
}
|
|
2129
|
+
this.log(`Active sprint found: ${sprint.name}`, "info");
|
|
2059
2130
|
} else {
|
|
2060
|
-
this.log("No active sprint found
|
|
2131
|
+
this.log("No active sprint found.", "warn");
|
|
2061
2132
|
}
|
|
2062
|
-
while (this.tasksCompleted < this.maxTasks
|
|
2133
|
+
while (this.tasksCompleted < this.maxTasks) {
|
|
2063
2134
|
const task = await this.getNextTask();
|
|
2064
2135
|
if (!task) {
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
this.consecutiveEmpty++;
|
|
2069
|
-
if (this.consecutiveEmpty >= this.maxEmpty)
|
|
2070
|
-
break;
|
|
2071
|
-
await new Promise((r) => setTimeout(r, this.pollInterval));
|
|
2072
|
-
continue;
|
|
2136
|
+
this.log("No tasks remaining. Running review on staged changes...", "info");
|
|
2137
|
+
await this.runStagedChangesReview(sprint);
|
|
2138
|
+
break;
|
|
2073
2139
|
}
|
|
2074
|
-
this.consecutiveEmpty = 0;
|
|
2075
2140
|
this.log(`Claimed: ${task.title}`, "success");
|
|
2076
2141
|
const result = await this.executeTask(task);
|
|
2077
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
|
@@ -386,6 +386,10 @@ class WorkspacesModule extends BaseModule {
|
|
|
386
386
|
const { data } = await this.api.get(`/workspaces/${id}/stats`);
|
|
387
387
|
return data;
|
|
388
388
|
}
|
|
389
|
+
async getManifestStatus(workspaceId) {
|
|
390
|
+
const { data } = await this.api.get(`/workspaces/${workspaceId}/manifest-status`);
|
|
391
|
+
return data;
|
|
392
|
+
}
|
|
389
393
|
async getActivity(id, limit) {
|
|
390
394
|
const { data } = await this.api.get(`/workspaces/${id}/activity`, {
|
|
391
395
|
params: { limit }
|
|
@@ -508,6 +512,8 @@ __export(exports_worker, {
|
|
|
508
512
|
AgentWorker: () => AgentWorker
|
|
509
513
|
});
|
|
510
514
|
module.exports = __toCommonJS(exports_worker);
|
|
515
|
+
var import_node_fs5 = require("node:fs");
|
|
516
|
+
var import_node_path7 = require("node:path");
|
|
511
517
|
|
|
512
518
|
// src/core/config.ts
|
|
513
519
|
var import_node_path = require("node:path");
|
|
@@ -527,14 +533,22 @@ var LOCUS_CONFIG = {
|
|
|
527
533
|
artifactsDir: "artifacts",
|
|
528
534
|
documentsDir: "documents",
|
|
529
535
|
agentSkillsDir: ".agent/skills",
|
|
530
|
-
sessionsDir: "sessions"
|
|
536
|
+
sessionsDir: "sessions",
|
|
537
|
+
reviewsDir: "reviews",
|
|
538
|
+
plansDir: "plans"
|
|
531
539
|
};
|
|
532
540
|
var LOCUS_GITIGNORE_PATTERNS = [
|
|
533
541
|
"# Locus AI - Session data (user-specific, can grow large)",
|
|
534
542
|
".locus/sessions/",
|
|
535
543
|
"",
|
|
536
544
|
"# Locus AI - Artifacts (local-only, user-specific)",
|
|
537
|
-
".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/"
|
|
538
552
|
];
|
|
539
553
|
function getLocusPath(projectPath, fileName) {
|
|
540
554
|
if (fileName === "contextFile") {
|
|
@@ -629,7 +643,7 @@ class ClaudeRunner {
|
|
|
629
643
|
setEventEmitter(emitter) {
|
|
630
644
|
this.eventEmitter = emitter;
|
|
631
645
|
}
|
|
632
|
-
async run(prompt
|
|
646
|
+
async run(prompt) {
|
|
633
647
|
const maxRetries = 3;
|
|
634
648
|
let lastError = null;
|
|
635
649
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
@@ -1475,7 +1489,7 @@ File tree:
|
|
|
1475
1489
|
${tree}
|
|
1476
1490
|
|
|
1477
1491
|
Return ONLY valid JSON, no markdown formatting.`;
|
|
1478
|
-
const response = await this.deps.aiRunner.run(prompt
|
|
1492
|
+
const response = await this.deps.aiRunner.run(prompt);
|
|
1479
1493
|
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
1480
1494
|
if (jsonMatch) {
|
|
1481
1495
|
return JSON.parse(jsonMatch[0]);
|
|
@@ -1538,6 +1552,66 @@ class DocumentFetcher {
|
|
|
1538
1552
|
}
|
|
1539
1553
|
}
|
|
1540
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
|
+
|
|
1541
1615
|
// src/core/prompt-builder.ts
|
|
1542
1616
|
var import_node_fs4 = require("node:fs");
|
|
1543
1617
|
var import_node_os2 = require("node:os");
|
|
@@ -1903,29 +1977,8 @@ class TaskExecutor {
|
|
|
1903
1977
|
taskContext: context
|
|
1904
1978
|
});
|
|
1905
1979
|
try {
|
|
1906
|
-
let plan = null;
|
|
1907
|
-
this.deps.log("Phase 1: Planning (CLI)...", "info");
|
|
1908
|
-
const planningPrompt = `${basePrompt}
|
|
1909
|
-
|
|
1910
|
-
## Phase 1: Planning
|
|
1911
|
-
Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
|
|
1912
|
-
plan = await this.deps.aiRunner.run(planningPrompt, true);
|
|
1913
1980
|
this.deps.log("Starting Execution...", "info");
|
|
1914
|
-
|
|
1915
|
-
if (plan != null) {
|
|
1916
|
-
executionPrompt += `
|
|
1917
|
-
|
|
1918
|
-
## Phase 2: Execution
|
|
1919
|
-
Based on the plan, execute the task:
|
|
1920
|
-
|
|
1921
|
-
${plan}`;
|
|
1922
|
-
} else {
|
|
1923
|
-
executionPrompt += `
|
|
1924
|
-
|
|
1925
|
-
## Execution
|
|
1926
|
-
Execute the task directly.`;
|
|
1927
|
-
}
|
|
1928
|
-
executionPrompt += `
|
|
1981
|
+
const executionPrompt = `${basePrompt}
|
|
1929
1982
|
|
|
1930
1983
|
When finished, output: <promise>COMPLETE</promise>`;
|
|
1931
1984
|
const output = await this.deps.aiRunner.run(executionPrompt);
|
|
@@ -1959,11 +2012,9 @@ class AgentWorker {
|
|
|
1959
2012
|
indexerService;
|
|
1960
2013
|
documentFetcher;
|
|
1961
2014
|
taskExecutor;
|
|
1962
|
-
|
|
1963
|
-
maxEmpty = 60;
|
|
2015
|
+
reviewService;
|
|
1964
2016
|
maxTasks = 50;
|
|
1965
2017
|
tasksCompleted = 0;
|
|
1966
|
-
pollInterval = 1e4;
|
|
1967
2018
|
constructor(config) {
|
|
1968
2019
|
this.config = config;
|
|
1969
2020
|
const projectPath = config.projectPath || process.cwd();
|
|
@@ -2000,6 +2051,11 @@ class AgentWorker {
|
|
|
2000
2051
|
projectPath,
|
|
2001
2052
|
log
|
|
2002
2053
|
});
|
|
2054
|
+
this.reviewService = new ReviewService({
|
|
2055
|
+
aiRunner: this.aiRunner,
|
|
2056
|
+
projectPath,
|
|
2057
|
+
log
|
|
2058
|
+
});
|
|
2003
2059
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
2004
2060
|
this.log(`Using ${providerLabel} CLI for all phases`, "info");
|
|
2005
2061
|
}
|
|
@@ -2045,33 +2101,42 @@ class AgentWorker {
|
|
|
2045
2101
|
await this.indexerService.reindex();
|
|
2046
2102
|
return result;
|
|
2047
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
|
+
}
|
|
2048
2125
|
async run() {
|
|
2049
2126
|
this.log(`Agent started in ${this.config.projectPath || process.cwd()}`, "success");
|
|
2050
2127
|
const sprint = await this.getActiveSprint();
|
|
2051
2128
|
if (sprint) {
|
|
2052
|
-
this.log(`Active sprint found: ${sprint.name}
|
|
2053
|
-
try {
|
|
2054
|
-
await this.client.sprints.triggerAIPlanning(sprint.id, this.config.workspaceId);
|
|
2055
|
-
this.log(`Sprint plan sync checked on server.`, "success");
|
|
2056
|
-
} catch (err) {
|
|
2057
|
-
this.log(`Sprint planning sync failed (non-critical): ${err instanceof Error ? err.message : String(err)}`, "warn");
|
|
2058
|
-
}
|
|
2129
|
+
this.log(`Active sprint found: ${sprint.name}`, "info");
|
|
2059
2130
|
} else {
|
|
2060
|
-
this.log("No active sprint found
|
|
2131
|
+
this.log("No active sprint found.", "warn");
|
|
2061
2132
|
}
|
|
2062
|
-
while (this.tasksCompleted < this.maxTasks
|
|
2133
|
+
while (this.tasksCompleted < this.maxTasks) {
|
|
2063
2134
|
const task = await this.getNextTask();
|
|
2064
2135
|
if (!task) {
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
this.consecutiveEmpty++;
|
|
2069
|
-
if (this.consecutiveEmpty >= this.maxEmpty)
|
|
2070
|
-
break;
|
|
2071
|
-
await new Promise((r) => setTimeout(r, this.pollInterval));
|
|
2072
|
-
continue;
|
|
2136
|
+
this.log("No tasks remaining. Running review on staged changes...", "info");
|
|
2137
|
+
await this.runStagedChangesReview(sprint);
|
|
2138
|
+
break;
|
|
2073
2139
|
}
|
|
2074
|
-
this.consecutiveEmpty = 0;
|
|
2075
2140
|
this.log(`Claimed: ${task.title}`, "success");
|
|
2076
2141
|
const result = await this.executeTask(task);
|
|
2077
2142
|
try {
|
|
@@ -2153,6 +2218,7 @@ __export(exports_index_node, {
|
|
|
2153
2218
|
TasksModule: () => TasksModule,
|
|
2154
2219
|
TaskExecutor: () => TaskExecutor,
|
|
2155
2220
|
SprintsModule: () => SprintsModule,
|
|
2221
|
+
ReviewService: () => ReviewService,
|
|
2156
2222
|
PromptBuilder: () => PromptBuilder,
|
|
2157
2223
|
PROVIDER: () => PROVIDER,
|
|
2158
2224
|
OrganizationsModule: () => OrganizationsModule,
|
|
@@ -2594,8 +2660,8 @@ class ExecEventEmitter {
|
|
|
2594
2660
|
}
|
|
2595
2661
|
}
|
|
2596
2662
|
// src/exec/history-manager.ts
|
|
2597
|
-
var
|
|
2598
|
-
var
|
|
2663
|
+
var import_node_fs6 = require("node:fs");
|
|
2664
|
+
var import_node_path8 = require("node:path");
|
|
2599
2665
|
var DEFAULT_MAX_SESSIONS = 30;
|
|
2600
2666
|
function generateSessionId2() {
|
|
2601
2667
|
const timestamp = Date.now().toString(36);
|
|
@@ -2607,30 +2673,30 @@ class HistoryManager {
|
|
|
2607
2673
|
historyDir;
|
|
2608
2674
|
maxSessions;
|
|
2609
2675
|
constructor(projectPath, options) {
|
|
2610
|
-
this.historyDir = options?.historyDir ??
|
|
2676
|
+
this.historyDir = options?.historyDir ?? import_node_path8.join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.sessionsDir);
|
|
2611
2677
|
this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
|
|
2612
2678
|
this.ensureHistoryDir();
|
|
2613
2679
|
}
|
|
2614
2680
|
ensureHistoryDir() {
|
|
2615
|
-
if (!
|
|
2616
|
-
|
|
2681
|
+
if (!import_node_fs6.existsSync(this.historyDir)) {
|
|
2682
|
+
import_node_fs6.mkdirSync(this.historyDir, { recursive: true });
|
|
2617
2683
|
}
|
|
2618
2684
|
}
|
|
2619
2685
|
getSessionPath(sessionId) {
|
|
2620
|
-
return
|
|
2686
|
+
return import_node_path8.join(this.historyDir, `${sessionId}.json`);
|
|
2621
2687
|
}
|
|
2622
2688
|
saveSession(session) {
|
|
2623
2689
|
const filePath = this.getSessionPath(session.id);
|
|
2624
2690
|
session.updatedAt = Date.now();
|
|
2625
|
-
|
|
2691
|
+
import_node_fs6.writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
2626
2692
|
}
|
|
2627
2693
|
loadSession(sessionId) {
|
|
2628
2694
|
const filePath = this.getSessionPath(sessionId);
|
|
2629
|
-
if (!
|
|
2695
|
+
if (!import_node_fs6.existsSync(filePath)) {
|
|
2630
2696
|
return null;
|
|
2631
2697
|
}
|
|
2632
2698
|
try {
|
|
2633
|
-
const content =
|
|
2699
|
+
const content = import_node_fs6.readFileSync(filePath, "utf-8");
|
|
2634
2700
|
return JSON.parse(content);
|
|
2635
2701
|
} catch {
|
|
2636
2702
|
return null;
|
|
@@ -2638,18 +2704,18 @@ class HistoryManager {
|
|
|
2638
2704
|
}
|
|
2639
2705
|
deleteSession(sessionId) {
|
|
2640
2706
|
const filePath = this.getSessionPath(sessionId);
|
|
2641
|
-
if (!
|
|
2707
|
+
if (!import_node_fs6.existsSync(filePath)) {
|
|
2642
2708
|
return false;
|
|
2643
2709
|
}
|
|
2644
2710
|
try {
|
|
2645
|
-
|
|
2711
|
+
import_node_fs6.rmSync(filePath);
|
|
2646
2712
|
return true;
|
|
2647
2713
|
} catch {
|
|
2648
2714
|
return false;
|
|
2649
2715
|
}
|
|
2650
2716
|
}
|
|
2651
2717
|
listSessions(options) {
|
|
2652
|
-
const files =
|
|
2718
|
+
const files = import_node_fs6.readdirSync(this.historyDir);
|
|
2653
2719
|
let sessions = [];
|
|
2654
2720
|
for (const file of files) {
|
|
2655
2721
|
if (file.endsWith(".json")) {
|
|
@@ -2722,11 +2788,11 @@ class HistoryManager {
|
|
|
2722
2788
|
return deleted;
|
|
2723
2789
|
}
|
|
2724
2790
|
getSessionCount() {
|
|
2725
|
-
const files =
|
|
2791
|
+
const files = import_node_fs6.readdirSync(this.historyDir);
|
|
2726
2792
|
return files.filter((f) => f.endsWith(".json")).length;
|
|
2727
2793
|
}
|
|
2728
2794
|
sessionExists(sessionId) {
|
|
2729
|
-
return
|
|
2795
|
+
return import_node_fs6.existsSync(this.getSessionPath(sessionId));
|
|
2730
2796
|
}
|
|
2731
2797
|
findSessionByPartialId(partialId) {
|
|
2732
2798
|
const sessions = this.listSessions();
|
|
@@ -2740,12 +2806,12 @@ class HistoryManager {
|
|
|
2740
2806
|
return this.historyDir;
|
|
2741
2807
|
}
|
|
2742
2808
|
clearAllSessions() {
|
|
2743
|
-
const files =
|
|
2809
|
+
const files = import_node_fs6.readdirSync(this.historyDir);
|
|
2744
2810
|
let deleted = 0;
|
|
2745
2811
|
for (const file of files) {
|
|
2746
2812
|
if (file.endsWith(".json")) {
|
|
2747
2813
|
try {
|
|
2748
|
-
|
|
2814
|
+
import_node_fs6.rmSync(import_node_path8.join(this.historyDir, file));
|
|
2749
2815
|
deleted++;
|
|
2750
2816
|
} catch {}
|
|
2751
2817
|
}
|
|
@@ -3010,9 +3076,9 @@ ${currentPrompt}`);
|
|
|
3010
3076
|
}
|
|
3011
3077
|
}
|
|
3012
3078
|
// src/orchestrator.ts
|
|
3013
|
-
var
|
|
3014
|
-
var
|
|
3015
|
-
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");
|
|
3016
3082
|
var import_node_url = require("node:url");
|
|
3017
3083
|
var import_shared3 = require("@locusai/shared");
|
|
3018
3084
|
var import_events4 = require("events");
|
|
@@ -3108,9 +3174,9 @@ ${c.success("✅ Orchestrator finished")}`);
|
|
|
3108
3174
|
`);
|
|
3109
3175
|
const potentialPaths = [];
|
|
3110
3176
|
const currentModulePath = import_node_url.fileURLToPath("file:///home/runner/work/locusai/locusai/packages/sdk/src/orchestrator.ts");
|
|
3111
|
-
const currentModuleDir =
|
|
3112
|
-
potentialPaths.push(
|
|
3113
|
-
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));
|
|
3114
3180
|
if (!workerPath) {
|
|
3115
3181
|
throw new Error(`Worker file not found. Checked: ${potentialPaths.join(", ")}. ` + `Make sure the SDK is properly built and installed.`);
|
|
3116
3182
|
}
|
|
@@ -3135,7 +3201,7 @@ ${c.success("✅ Orchestrator finished")}`);
|
|
|
3135
3201
|
if (this.resolvedSprintId) {
|
|
3136
3202
|
workerArgs.push("--sprint-id", this.resolvedSprintId);
|
|
3137
3203
|
}
|
|
3138
|
-
const agentProcess =
|
|
3204
|
+
const agentProcess = import_node_child_process4.spawn(process.execPath, [workerPath, ...workerArgs], {
|
|
3139
3205
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3140
3206
|
env: {
|
|
3141
3207
|
...process.env,
|
package/dist/index.js
CHANGED
|
@@ -386,6 +386,10 @@ class WorkspacesModule extends BaseModule {
|
|
|
386
386
|
const { data } = await this.api.get(`/workspaces/${id}/stats`);
|
|
387
387
|
return data;
|
|
388
388
|
}
|
|
389
|
+
async getManifestStatus(workspaceId) {
|
|
390
|
+
const { data } = await this.api.get(`/workspaces/${workspaceId}/manifest-status`);
|
|
391
|
+
return data;
|
|
392
|
+
}
|
|
389
393
|
async getActivity(id, limit) {
|
|
390
394
|
const { data } = await this.api.get(`/workspaces/${id}/activity`, {
|
|
391
395
|
params: { limit }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateWorkspace, Event, Task, UpdateWorkspace, Workspace, WorkspaceStats } from "@locusai/shared";
|
|
1
|
+
import { CreateWorkspace, Event, ManifestStatusResponse, Task, UpdateWorkspace, Workspace, WorkspaceStats } from "@locusai/shared";
|
|
2
2
|
import { BaseModule } from "./base.js";
|
|
3
3
|
export declare class WorkspacesModule extends BaseModule {
|
|
4
4
|
listAll(): Promise<Workspace[]>;
|
|
@@ -11,6 +11,11 @@ export declare class WorkspacesModule extends BaseModule {
|
|
|
11
11
|
update(id: string, body: UpdateWorkspace): Promise<Workspace>;
|
|
12
12
|
delete(id: string): Promise<void>;
|
|
13
13
|
getStats(id: string): Promise<WorkspaceStats>;
|
|
14
|
+
/**
|
|
15
|
+
* Get detailed manifest completion status for a workspace.
|
|
16
|
+
* Returns completion percentage and missing fields.
|
|
17
|
+
*/
|
|
18
|
+
getManifestStatus(workspaceId: string): Promise<ManifestStatusResponse>;
|
|
14
19
|
getActivity(id: string, limit?: number): Promise<Event[]>;
|
|
15
20
|
/**
|
|
16
21
|
* Dispatch a task from the workspace backlog to an agent.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/modules/workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EACf,KAAK,EACL,IAAI,EAEJ,eAAe,EACf,SAAS,EAET,cAAc,EAEf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,qBAAa,gBAAiB,SAAQ,UAAU;IACxC,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAK/B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAO9C,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC;IASrE,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAQ5D,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAKvC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAQ7D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/modules/workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EACf,KAAK,EACL,sBAAsB,EACtB,IAAI,EAEJ,eAAe,EACf,SAAS,EAET,cAAc,EAEf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,qBAAa,gBAAiB,SAAQ,UAAU;IACxC,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAK/B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAO9C,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC;IASrE,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAQ5D,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAKvC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAQ7D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAOnD;;;OAGG;IACG,iBAAiB,CACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,sBAAsB,CAAC;IAO5B,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAU/D;;;OAGG;IACH;;;OAGG;IACG,QAAQ,CACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAYV,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAO5D,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC;IAQrB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGtE;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/sdk",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
33
|
+
"@locusai/shared": "^0.8.1",
|
|
34
34
|
"axios": "^1.13.2",
|
|
35
35
|
"events": "^3.3.0",
|
|
36
36
|
"globby": "^14.0.2"
|