@locusai/cli 0.11.8 → 0.12.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/bin/agent/worker.js +135 -348
- package/bin/locus.js +458 -904
- package/package.json +2 -2
package/bin/locus.js
CHANGED
|
@@ -6320,43 +6320,42 @@ var init_indexer = __esm(() => {
|
|
|
6320
6320
|
// ../sdk/src/utils/json-extractor.ts
|
|
6321
6321
|
function extractJsonFromLLMOutput(raw) {
|
|
6322
6322
|
const trimmed = raw.trim();
|
|
6323
|
-
const
|
|
6324
|
-
if (
|
|
6325
|
-
return
|
|
6326
|
-
}
|
|
6327
|
-
const
|
|
6328
|
-
if (
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
continue;
|
|
6339
|
-
}
|
|
6340
|
-
if (inString) {
|
|
6323
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
6324
|
+
if (fenceMatch) {
|
|
6325
|
+
return fenceMatch[1].trim();
|
|
6326
|
+
}
|
|
6327
|
+
const start = trimmed.indexOf("{");
|
|
6328
|
+
if (start !== -1) {
|
|
6329
|
+
let depth = 0;
|
|
6330
|
+
let inString = false;
|
|
6331
|
+
let isEscape = false;
|
|
6332
|
+
for (let i = start;i < trimmed.length; i++) {
|
|
6333
|
+
const ch = trimmed[i];
|
|
6334
|
+
if (isEscape) {
|
|
6335
|
+
isEscape = false;
|
|
6336
|
+
continue;
|
|
6337
|
+
}
|
|
6341
6338
|
if (ch === "\\") {
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
inString = false;
|
|
6339
|
+
isEscape = true;
|
|
6340
|
+
continue;
|
|
6345
6341
|
}
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
if (
|
|
6355
|
-
|
|
6342
|
+
if (ch === '"') {
|
|
6343
|
+
inString = !inString;
|
|
6344
|
+
continue;
|
|
6345
|
+
}
|
|
6346
|
+
if (inString)
|
|
6347
|
+
continue;
|
|
6348
|
+
if (ch === "{")
|
|
6349
|
+
depth++;
|
|
6350
|
+
else if (ch === "}") {
|
|
6351
|
+
depth--;
|
|
6352
|
+
if (depth === 0) {
|
|
6353
|
+
return trimmed.slice(start, i + 1);
|
|
6354
|
+
}
|
|
6356
6355
|
}
|
|
6357
6356
|
}
|
|
6358
6357
|
}
|
|
6359
|
-
return trimmed
|
|
6358
|
+
return trimmed;
|
|
6360
6359
|
}
|
|
6361
6360
|
|
|
6362
6361
|
// ../sdk/src/agent/codebase-indexer-service.ts
|
|
@@ -6367,9 +6366,6 @@ var init_codebase_indexer_service = __esm(() => {
|
|
|
6367
6366
|
// ../sdk/src/core/config.ts
|
|
6368
6367
|
import { join as join2 } from "node:path";
|
|
6369
6368
|
function getLocusPath(projectPath, fileName) {
|
|
6370
|
-
if (fileName === "projectContextFile" || fileName === "projectProgressFile") {
|
|
6371
|
-
return join2(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.projectDir, LOCUS_CONFIG[fileName]);
|
|
6372
|
-
}
|
|
6373
6369
|
return join2(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG[fileName]);
|
|
6374
6370
|
}
|
|
6375
6371
|
var PROVIDER, DEFAULT_MODEL, LOCUS_SCHEMA_BASE_URL = "https://locusai.dev/schemas", LOCUS_SCHEMAS, LOCUS_CONFIG, LOCUS_GITIGNORE_PATTERNS;
|
|
@@ -6392,14 +6388,12 @@ var init_config = __esm(() => {
|
|
|
6392
6388
|
settingsFile: "settings.json",
|
|
6393
6389
|
indexFile: "codebase-index.json",
|
|
6394
6390
|
contextFile: "LOCUS.md",
|
|
6391
|
+
learningsFile: "LEARNINGS.md",
|
|
6395
6392
|
artifactsDir: "artifacts",
|
|
6396
6393
|
documentsDir: "documents",
|
|
6397
6394
|
sessionsDir: "sessions",
|
|
6398
6395
|
reviewsDir: "reviews",
|
|
6399
|
-
plansDir: "plans"
|
|
6400
|
-
projectDir: "project",
|
|
6401
|
-
projectContextFile: "context.md",
|
|
6402
|
-
projectProgressFile: "progress.md"
|
|
6396
|
+
plansDir: "plans"
|
|
6403
6397
|
};
|
|
6404
6398
|
LOCUS_GITIGNORE_PATTERNS = [
|
|
6405
6399
|
"# Locus AI - Session data (user-specific, can grow large)",
|
|
@@ -6417,11 +6411,8 @@ var init_config = __esm(() => {
|
|
|
6417
6411
|
"# Locus AI - Settings (contains API key, telegram config, etc.)",
|
|
6418
6412
|
".locus/settings.json",
|
|
6419
6413
|
"",
|
|
6420
|
-
"# Locus AI - Configuration (contains project context,
|
|
6421
|
-
".locus/config.json"
|
|
6422
|
-
"",
|
|
6423
|
-
"# Locus AI - Project progress (contains project progress, etc.)",
|
|
6424
|
-
".locus/project/progress.md"
|
|
6414
|
+
"# Locus AI - Configuration (contains project context, etc.)",
|
|
6415
|
+
".locus/config.json"
|
|
6425
6416
|
];
|
|
6426
6417
|
});
|
|
6427
6418
|
|
|
@@ -7111,17 +7102,22 @@ class ClaudeRunner {
|
|
|
7111
7102
|
});
|
|
7112
7103
|
});
|
|
7113
7104
|
}
|
|
7114
|
-
|
|
7105
|
+
buildCliArgs() {
|
|
7115
7106
|
const args = [
|
|
7116
|
-
"--dangerously-skip-permissions",
|
|
7117
7107
|
"--print",
|
|
7118
|
-
"--verbose",
|
|
7119
7108
|
"--output-format",
|
|
7120
7109
|
"stream-json",
|
|
7110
|
+
"--verbose",
|
|
7111
|
+
"--dangerously-skip-permissions",
|
|
7112
|
+
"--no-session-persistence",
|
|
7121
7113
|
"--include-partial-messages",
|
|
7122
7114
|
"--model",
|
|
7123
7115
|
this.model
|
|
7124
7116
|
];
|
|
7117
|
+
return args;
|
|
7118
|
+
}
|
|
7119
|
+
async* runStream(prompt) {
|
|
7120
|
+
const args = this.buildCliArgs();
|
|
7125
7121
|
const env = getAugmentedEnv({
|
|
7126
7122
|
FORCE_COLOR: "1",
|
|
7127
7123
|
TERM: "xterm-256color"
|
|
@@ -7361,16 +7357,7 @@ class ClaudeRunner {
|
|
|
7361
7357
|
}
|
|
7362
7358
|
executeRun(prompt) {
|
|
7363
7359
|
return new Promise((resolve2, reject) => {
|
|
7364
|
-
const args =
|
|
7365
|
-
"--dangerously-skip-permissions",
|
|
7366
|
-
"--print",
|
|
7367
|
-
"--verbose",
|
|
7368
|
-
"--output-format",
|
|
7369
|
-
"stream-json",
|
|
7370
|
-
"--include-partial-messages",
|
|
7371
|
-
"--model",
|
|
7372
|
-
this.model
|
|
7373
|
-
];
|
|
7360
|
+
const args = this.buildCliArgs();
|
|
7374
7361
|
const env = getAugmentedEnv({
|
|
7375
7362
|
FORCE_COLOR: "1",
|
|
7376
7363
|
TERM: "xterm-256color"
|
|
@@ -7495,7 +7482,7 @@ class CodexRunner {
|
|
|
7495
7482
|
eventEmitter;
|
|
7496
7483
|
currentToolName;
|
|
7497
7484
|
timeoutMs;
|
|
7498
|
-
constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CODEX], log,
|
|
7485
|
+
constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CODEX], log, reasoningEffort, timeoutMs) {
|
|
7499
7486
|
this.projectPath = projectPath;
|
|
7500
7487
|
this.model = model;
|
|
7501
7488
|
this.log = log;
|
|
@@ -7821,7 +7808,7 @@ function createAiRunner(provider, config) {
|
|
|
7821
7808
|
const model = config.model ?? DEFAULT_MODEL[resolvedProvider];
|
|
7822
7809
|
switch (resolvedProvider) {
|
|
7823
7810
|
case PROVIDER.CODEX:
|
|
7824
|
-
return new CodexRunner(config.projectPath, model, config.log, config.
|
|
7811
|
+
return new CodexRunner(config.projectPath, model, config.log, config.reasoningEffort ?? "high", config.timeoutMs);
|
|
7825
7812
|
default:
|
|
7826
7813
|
return new ClaudeRunner(config.projectPath, model, config.log, config.timeoutMs);
|
|
7827
7814
|
}
|
|
@@ -38736,106 +38723,6 @@ var init_src2 = __esm(() => {
|
|
|
38736
38723
|
init_workspaces();
|
|
38737
38724
|
});
|
|
38738
38725
|
|
|
38739
|
-
// ../sdk/src/project/knowledge-base.ts
|
|
38740
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
38741
|
-
import { dirname as dirname2 } from "node:path";
|
|
38742
|
-
|
|
38743
|
-
class KnowledgeBase {
|
|
38744
|
-
contextPath;
|
|
38745
|
-
progressPath;
|
|
38746
|
-
constructor(projectPath) {
|
|
38747
|
-
this.contextPath = getLocusPath(projectPath, "projectContextFile");
|
|
38748
|
-
this.progressPath = getLocusPath(projectPath, "projectProgressFile");
|
|
38749
|
-
}
|
|
38750
|
-
readContext() {
|
|
38751
|
-
if (!existsSync5(this.contextPath)) {
|
|
38752
|
-
return "";
|
|
38753
|
-
}
|
|
38754
|
-
return readFileSync5(this.contextPath, "utf-8");
|
|
38755
|
-
}
|
|
38756
|
-
readProgress() {
|
|
38757
|
-
if (!existsSync5(this.progressPath)) {
|
|
38758
|
-
return "";
|
|
38759
|
-
}
|
|
38760
|
-
return readFileSync5(this.progressPath, "utf-8");
|
|
38761
|
-
}
|
|
38762
|
-
updateContext(content) {
|
|
38763
|
-
this.ensureDir(this.contextPath);
|
|
38764
|
-
writeFileSync3(this.contextPath, content);
|
|
38765
|
-
}
|
|
38766
|
-
updateProgress(entry) {
|
|
38767
|
-
this.ensureDir(this.progressPath);
|
|
38768
|
-
const existing = this.readProgress();
|
|
38769
|
-
const timestamp = (entry.timestamp ?? new Date).toISOString();
|
|
38770
|
-
const label = entry.role === "user" ? "User" : "Assistant";
|
|
38771
|
-
const line = `**${label}** (${timestamp}):
|
|
38772
|
-
${entry.content}`;
|
|
38773
|
-
const updated = existing ? `${existing}
|
|
38774
|
-
|
|
38775
|
-
---
|
|
38776
|
-
|
|
38777
|
-
${line}` : `# Conversation History
|
|
38778
|
-
|
|
38779
|
-
${line}`;
|
|
38780
|
-
writeFileSync3(this.progressPath, updated);
|
|
38781
|
-
}
|
|
38782
|
-
getFullContext() {
|
|
38783
|
-
const context2 = this.readContext();
|
|
38784
|
-
const progress = this.readProgress();
|
|
38785
|
-
const parts = [];
|
|
38786
|
-
if (context2.trim()) {
|
|
38787
|
-
parts.push(context2.trim());
|
|
38788
|
-
}
|
|
38789
|
-
if (progress.trim()) {
|
|
38790
|
-
parts.push(progress.trim());
|
|
38791
|
-
}
|
|
38792
|
-
return parts.join(`
|
|
38793
|
-
|
|
38794
|
-
---
|
|
38795
|
-
|
|
38796
|
-
`);
|
|
38797
|
-
}
|
|
38798
|
-
initialize(info) {
|
|
38799
|
-
this.ensureDir(this.contextPath);
|
|
38800
|
-
this.ensureDir(this.progressPath);
|
|
38801
|
-
const techStackList = info.techStack.map((t) => `- ${t}`).join(`
|
|
38802
|
-
`);
|
|
38803
|
-
const contextContent = `# Project: ${info.name}
|
|
38804
|
-
|
|
38805
|
-
## Mission
|
|
38806
|
-
${info.mission}
|
|
38807
|
-
|
|
38808
|
-
## Tech Stack
|
|
38809
|
-
${techStackList}
|
|
38810
|
-
|
|
38811
|
-
## Architecture
|
|
38812
|
-
<!-- Describe your high-level architecture here -->
|
|
38813
|
-
|
|
38814
|
-
## Key Decisions
|
|
38815
|
-
<!-- Document important technical decisions and their rationale -->
|
|
38816
|
-
|
|
38817
|
-
## Feature Areas
|
|
38818
|
-
<!-- List your main feature areas and their status -->
|
|
38819
|
-
`;
|
|
38820
|
-
const progressContent = `# Conversation History
|
|
38821
|
-
`;
|
|
38822
|
-
writeFileSync3(this.contextPath, contextContent);
|
|
38823
|
-
writeFileSync3(this.progressPath, progressContent);
|
|
38824
|
-
}
|
|
38825
|
-
get exists() {
|
|
38826
|
-
return existsSync5(this.contextPath) || existsSync5(this.progressPath);
|
|
38827
|
-
}
|
|
38828
|
-
ensureDir(filePath) {
|
|
38829
|
-
const dir = dirname2(filePath);
|
|
38830
|
-
if (!existsSync5(dir)) {
|
|
38831
|
-
mkdirSync3(dir, { recursive: true });
|
|
38832
|
-
}
|
|
38833
|
-
}
|
|
38834
|
-
}
|
|
38835
|
-
var init_knowledge_base = __esm(() => {
|
|
38836
|
-
init_config();
|
|
38837
|
-
});
|
|
38838
|
-
|
|
38839
38726
|
// ../sdk/src/agent/reviewer-worker.ts
|
|
38840
38727
|
function resolveProvider(value) {
|
|
38841
38728
|
if (!value || value.startsWith("--"))
|
|
@@ -38850,7 +38737,6 @@ class ReviewerWorker {
|
|
|
38850
38737
|
client;
|
|
38851
38738
|
aiRunner;
|
|
38852
38739
|
prService;
|
|
38853
|
-
knowledgeBase;
|
|
38854
38740
|
heartbeatInterval = null;
|
|
38855
38741
|
currentTaskId = null;
|
|
38856
38742
|
maxReviews = 50;
|
|
@@ -38876,7 +38762,6 @@ class ReviewerWorker {
|
|
|
38876
38762
|
log
|
|
38877
38763
|
});
|
|
38878
38764
|
this.prService = new PrService(projectPath, log);
|
|
38879
|
-
this.knowledgeBase = new KnowledgeBase(projectPath);
|
|
38880
38765
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
38881
38766
|
this.log(`Reviewer agent using ${providerLabel} CLI`, "info");
|
|
38882
38767
|
}
|
|
@@ -38992,17 +38877,6 @@ ${summary}`;
|
|
|
38992
38877
|
this.sendHeartbeat();
|
|
38993
38878
|
const result = await this.reviewPr(pr);
|
|
38994
38879
|
if (result.reviewed) {
|
|
38995
|
-
const status = result.approved ? "APPROVED" : "CHANGES REQUESTED";
|
|
38996
|
-
try {
|
|
38997
|
-
this.knowledgeBase.updateProgress({
|
|
38998
|
-
role: "user",
|
|
38999
|
-
content: `Review PR #${pr.number}: ${pr.title}`
|
|
39000
|
-
});
|
|
39001
|
-
this.knowledgeBase.updateProgress({
|
|
39002
|
-
role: "assistant",
|
|
39003
|
-
content: `${status}: ${result.summary}`
|
|
39004
|
-
});
|
|
39005
|
-
} catch {}
|
|
39006
38880
|
this.reviewsCompleted++;
|
|
39007
38881
|
} else {
|
|
39008
38882
|
this.log(`Review skipped: ${result.summary}`, "warn");
|
|
@@ -39021,7 +38895,6 @@ var init_reviewer_worker = __esm(() => {
|
|
|
39021
38895
|
init_git_utils();
|
|
39022
38896
|
init_pr_service();
|
|
39023
38897
|
init_src2();
|
|
39024
|
-
init_knowledge_base();
|
|
39025
38898
|
init_colors();
|
|
39026
38899
|
reviewerEntrypoint = process.argv[1]?.split(/[\\/]/).pop();
|
|
39027
38900
|
if (reviewerEntrypoint === "reviewer-worker.js" || reviewerEntrypoint === "reviewer-worker.ts") {
|
|
@@ -39064,7 +38937,7 @@ var init_reviewer_worker = __esm(() => {
|
|
|
39064
38937
|
});
|
|
39065
38938
|
|
|
39066
38939
|
// ../sdk/src/core/prompt-builder.ts
|
|
39067
|
-
import { existsSync as
|
|
38940
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
39068
38941
|
import { join as join6 } from "node:path";
|
|
39069
38942
|
|
|
39070
38943
|
class PromptBuilder {
|
|
@@ -39072,239 +38945,157 @@ class PromptBuilder {
|
|
|
39072
38945
|
constructor(projectPath) {
|
|
39073
38946
|
this.projectPath = projectPath;
|
|
39074
38947
|
}
|
|
39075
|
-
async build(task2
|
|
39076
|
-
let prompt = `# Task: ${task2.title}
|
|
39077
|
-
|
|
39078
|
-
`;
|
|
38948
|
+
async build(task2) {
|
|
39079
38949
|
const roleText = this.roleToText(task2.assigneeRole);
|
|
38950
|
+
const description = task2.description || "No description provided.";
|
|
38951
|
+
const context2 = this.getProjectContext();
|
|
38952
|
+
const learnings = this.getLearningsContent();
|
|
38953
|
+
const knowledgeBase = this.getKnowledgeBaseSection();
|
|
38954
|
+
let sections = "";
|
|
39080
38955
|
if (roleText) {
|
|
39081
|
-
|
|
38956
|
+
sections += `
|
|
38957
|
+
<role>
|
|
39082
38958
|
You are acting as a ${roleText}.
|
|
39083
|
-
|
|
38959
|
+
</role>
|
|
39084
38960
|
`;
|
|
39085
38961
|
}
|
|
39086
|
-
|
|
39087
|
-
|
|
39088
|
-
|
|
39089
|
-
`;
|
|
39090
|
-
const projectConfig = this.getProjectConfig();
|
|
39091
|
-
if (projectConfig) {
|
|
39092
|
-
prompt += `## Project Metadata
|
|
39093
|
-
`;
|
|
39094
|
-
prompt += `- Version: ${projectConfig.version || "Unknown"}
|
|
39095
|
-
`;
|
|
39096
|
-
prompt += `- Created At: ${projectConfig.createdAt || "Unknown"}
|
|
39097
|
-
|
|
39098
|
-
`;
|
|
39099
|
-
}
|
|
39100
|
-
let serverContext = null;
|
|
39101
|
-
if (options.taskContext) {
|
|
39102
|
-
try {
|
|
39103
|
-
serverContext = JSON.parse(options.taskContext);
|
|
39104
|
-
} catch {
|
|
39105
|
-
serverContext = { context: options.taskContext };
|
|
39106
|
-
}
|
|
39107
|
-
}
|
|
39108
|
-
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
39109
|
-
let hasLocalContext = false;
|
|
39110
|
-
if (existsSync6(contextPath)) {
|
|
39111
|
-
try {
|
|
39112
|
-
const context2 = readFileSync6(contextPath, "utf-8");
|
|
39113
|
-
if (context2.trim().length > 20) {
|
|
39114
|
-
prompt += `## Project Context (Local)
|
|
38962
|
+
if (context2) {
|
|
38963
|
+
sections += `
|
|
38964
|
+
<project_context>
|
|
39115
38965
|
${context2}
|
|
39116
|
-
|
|
38966
|
+
</project_context>
|
|
39117
38967
|
`;
|
|
39118
|
-
hasLocalContext = true;
|
|
39119
|
-
}
|
|
39120
|
-
} catch (err) {
|
|
39121
|
-
console.warn(`Warning: Could not read context file: ${err}`);
|
|
39122
|
-
}
|
|
39123
38968
|
}
|
|
39124
|
-
|
|
39125
|
-
|
|
39126
|
-
|
|
39127
|
-
|
|
39128
|
-
${fallback}
|
|
39129
|
-
|
|
38969
|
+
sections += `
|
|
38970
|
+
<knowledge_base>
|
|
38971
|
+
${knowledgeBase}
|
|
38972
|
+
</knowledge_base>
|
|
39130
38973
|
`;
|
|
39131
|
-
|
|
39132
|
-
|
|
39133
|
-
|
|
39134
|
-
|
|
39135
|
-
|
|
39136
|
-
|
|
39137
|
-
if (project) {
|
|
39138
|
-
prompt += `- Project: ${project.name || "Unknown"}
|
|
39139
|
-
`;
|
|
39140
|
-
if (!hasLocalContext && project.techStack?.length) {
|
|
39141
|
-
prompt += `- Tech Stack: ${project.techStack.join(", ")}
|
|
39142
|
-
`;
|
|
39143
|
-
}
|
|
39144
|
-
}
|
|
39145
|
-
if (serverContext.context) {
|
|
39146
|
-
prompt += `
|
|
39147
|
-
${serverContext.context}
|
|
39148
|
-
`;
|
|
39149
|
-
}
|
|
39150
|
-
prompt += `
|
|
39151
|
-
`;
|
|
39152
|
-
}
|
|
39153
|
-
prompt += this.getProjectStructure();
|
|
39154
|
-
prompt += `## Project Knowledge Base
|
|
39155
|
-
`;
|
|
39156
|
-
prompt += `You have access to the following documentation directories for context:
|
|
39157
|
-
`;
|
|
39158
|
-
prompt += `- Artifacts: \`.locus/artifacts\`
|
|
39159
|
-
`;
|
|
39160
|
-
prompt += `- Documents: \`.locus/documents\`
|
|
39161
|
-
`;
|
|
39162
|
-
prompt += `If you need more information about the project strategies, plans, or architecture, please read files in these directories.
|
|
39163
|
-
|
|
39164
|
-
`;
|
|
39165
|
-
const indexPath = getLocusPath(this.projectPath, "indexFile");
|
|
39166
|
-
if (existsSync6(indexPath)) {
|
|
39167
|
-
prompt += `## Codebase Overview
|
|
39168
|
-
There is an index file in the .locus/codebase-index.json and if you need you can check it.
|
|
39169
|
-
|
|
38974
|
+
if (learnings) {
|
|
38975
|
+
sections += `
|
|
38976
|
+
<learnings>
|
|
38977
|
+
These are accumulated lessons from past tasks. Follow them to avoid repeating mistakes:
|
|
38978
|
+
${learnings}
|
|
38979
|
+
</learnings>
|
|
39170
38980
|
`;
|
|
39171
38981
|
}
|
|
39172
38982
|
if (task2.docs && task2.docs.length > 0) {
|
|
39173
|
-
|
|
39174
|
-
`;
|
|
39175
|
-
prompt += `> Full content available on server. Rely on Task Description for specific requirements.
|
|
39176
|
-
|
|
39177
|
-
`;
|
|
38983
|
+
let docsContent = "";
|
|
39178
38984
|
for (const doc3 of task2.docs) {
|
|
39179
38985
|
const content = doc3.content || "";
|
|
39180
38986
|
const limit = 800;
|
|
39181
38987
|
const preview = content.slice(0, limit);
|
|
39182
38988
|
const isTruncated = content.length > limit;
|
|
39183
|
-
|
|
38989
|
+
docsContent += `### ${doc3.title}
|
|
39184
38990
|
${preview}${isTruncated ? `
|
|
39185
38991
|
...(truncated)...` : ""}
|
|
39186
38992
|
|
|
39187
38993
|
`;
|
|
39188
38994
|
}
|
|
38995
|
+
sections += `
|
|
38996
|
+
<documents>
|
|
38997
|
+
${docsContent.trimEnd()}
|
|
38998
|
+
</documents>
|
|
38999
|
+
`;
|
|
39189
39000
|
}
|
|
39190
39001
|
if (task2.acceptanceChecklist && task2.acceptanceChecklist.length > 0) {
|
|
39191
|
-
|
|
39192
|
-
`;
|
|
39002
|
+
let criteria = "";
|
|
39193
39003
|
for (const item of task2.acceptanceChecklist) {
|
|
39194
|
-
|
|
39004
|
+
criteria += `- ${item.done ? "[x]" : "[ ]"} ${item.text}
|
|
39195
39005
|
`;
|
|
39196
39006
|
}
|
|
39197
|
-
|
|
39007
|
+
sections += `
|
|
39008
|
+
<acceptance_criteria>
|
|
39009
|
+
${criteria.trimEnd()}
|
|
39010
|
+
</acceptance_criteria>
|
|
39198
39011
|
`;
|
|
39199
39012
|
}
|
|
39200
39013
|
if (task2.comments && task2.comments.length > 0) {
|
|
39201
|
-
const
|
|
39202
|
-
|
|
39014
|
+
const filteredComments = task2.comments.filter((comment) => comment.author !== "system");
|
|
39015
|
+
const comments = filteredComments.slice(0, 3);
|
|
39016
|
+
if (comments.length > 0) {
|
|
39017
|
+
let commentsContent = "";
|
|
39018
|
+
for (const comment of comments) {
|
|
39019
|
+
const date5 = new Date(comment.createdAt).toLocaleString();
|
|
39020
|
+
commentsContent += `- ${comment.author} (${date5}): ${comment.text}
|
|
39203
39021
|
`;
|
|
39204
|
-
|
|
39205
|
-
|
|
39206
|
-
|
|
39207
|
-
|
|
39208
|
-
|
|
39209
|
-
prompt += `### ${comment.author} (${date5})
|
|
39210
|
-
${comment.text}
|
|
39211
|
-
|
|
39022
|
+
}
|
|
39023
|
+
sections += `
|
|
39024
|
+
<feedback>
|
|
39025
|
+
${commentsContent.trimEnd()}
|
|
39026
|
+
</feedback>
|
|
39212
39027
|
`;
|
|
39213
39028
|
}
|
|
39214
39029
|
}
|
|
39215
|
-
|
|
39216
|
-
|
|
39217
|
-
|
|
39218
|
-
|
|
39219
|
-
|
|
39220
|
-
|
|
39221
|
-
|
|
39030
|
+
return `<task_execution>
|
|
39031
|
+
Complete this task: ${task2.title}
|
|
39032
|
+
|
|
39033
|
+
<description>
|
|
39034
|
+
${description}
|
|
39035
|
+
</description>
|
|
39036
|
+
${sections}
|
|
39037
|
+
<rules>
|
|
39038
|
+
- Complete the task as described
|
|
39039
|
+
- Save any high-level documentation (PRDs, technical drafts, architecture docs) in \`.locus/artifacts/\`
|
|
39040
|
+
- Use relative paths from the project root at all times — no absolute local paths
|
|
39041
|
+
- Do NOT run \`git add\`, \`git commit\`, \`git push\`, or create branches — Locus handles git automatically
|
|
39042
|
+
</rules>
|
|
39043
|
+
</task_execution>`;
|
|
39222
39044
|
}
|
|
39223
39045
|
async buildGenericPrompt(query) {
|
|
39224
|
-
|
|
39225
|
-
|
|
39226
|
-
|
|
39227
|
-
|
|
39228
|
-
|
|
39229
|
-
|
|
39046
|
+
const context2 = this.getProjectContext();
|
|
39047
|
+
const learnings = this.getLearningsContent();
|
|
39048
|
+
const knowledgeBase = this.getKnowledgeBaseSection();
|
|
39049
|
+
let sections = "";
|
|
39050
|
+
if (context2) {
|
|
39051
|
+
sections += `
|
|
39052
|
+
<project_context>
|
|
39053
|
+
${context2}
|
|
39054
|
+
</project_context>
|
|
39230
39055
|
`;
|
|
39231
|
-
|
|
39232
|
-
|
|
39233
|
-
|
|
39056
|
+
}
|
|
39057
|
+
sections += `
|
|
39058
|
+
<knowledge_base>
|
|
39059
|
+
${knowledgeBase}
|
|
39060
|
+
</knowledge_base>
|
|
39234
39061
|
`;
|
|
39235
|
-
|
|
39236
|
-
|
|
39237
|
-
|
|
39238
|
-
|
|
39062
|
+
if (learnings) {
|
|
39063
|
+
sections += `
|
|
39064
|
+
<learnings>
|
|
39065
|
+
These are accumulated lessons from past tasks. Follow them to avoid repeating mistakes:
|
|
39066
|
+
${learnings}
|
|
39067
|
+
</learnings>
|
|
39239
39068
|
`;
|
|
39240
39069
|
}
|
|
39070
|
+
return `<direct_execution>
|
|
39071
|
+
Execute this prompt: ${query}
|
|
39072
|
+
${sections}
|
|
39073
|
+
<rules>
|
|
39074
|
+
- Execute the prompt based on the provided project context
|
|
39075
|
+
- Use relative paths from the project root at all times — no absolute local paths
|
|
39076
|
+
- Do NOT run \`git add\`, \`git commit\`, \`git push\`, or create branches — Locus handles git automatically
|
|
39077
|
+
</rules>
|
|
39078
|
+
</direct_execution>`;
|
|
39079
|
+
}
|
|
39080
|
+
getProjectContext() {
|
|
39241
39081
|
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
39242
|
-
|
|
39243
|
-
if (existsSync6(contextPath)) {
|
|
39082
|
+
if (existsSync5(contextPath)) {
|
|
39244
39083
|
try {
|
|
39245
|
-
const context2 =
|
|
39084
|
+
const context2 = readFileSync5(contextPath, "utf-8");
|
|
39246
39085
|
if (context2.trim().length > 20) {
|
|
39247
|
-
|
|
39248
|
-
${context2}
|
|
39249
|
-
|
|
39250
|
-
`;
|
|
39251
|
-
hasLocalContext = true;
|
|
39086
|
+
return context2;
|
|
39252
39087
|
}
|
|
39253
39088
|
} catch (err) {
|
|
39254
39089
|
console.warn(`Warning: Could not read context file: ${err}`);
|
|
39255
39090
|
}
|
|
39256
39091
|
}
|
|
39257
|
-
|
|
39258
|
-
const fallback = this.getFallbackContext();
|
|
39259
|
-
if (fallback) {
|
|
39260
|
-
prompt += `## Project Context (README Fallback)
|
|
39261
|
-
${fallback}
|
|
39262
|
-
|
|
39263
|
-
`;
|
|
39264
|
-
}
|
|
39265
|
-
}
|
|
39266
|
-
prompt += this.getProjectStructure();
|
|
39267
|
-
prompt += `## Project Knowledge Base
|
|
39268
|
-
`;
|
|
39269
|
-
prompt += `You have access to the following documentation directories for context:
|
|
39270
|
-
`;
|
|
39271
|
-
prompt += `- Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
|
|
39272
|
-
`;
|
|
39273
|
-
prompt += `- Documents: \`.locus/documents\` (synced from cloud)
|
|
39274
|
-
`;
|
|
39275
|
-
prompt += `If you need more information about the project strategies, plans, or architecture, please read files in these directories.
|
|
39276
|
-
|
|
39277
|
-
`;
|
|
39278
|
-
const indexPath = getLocusPath(this.projectPath, "indexFile");
|
|
39279
|
-
if (existsSync6(indexPath)) {
|
|
39280
|
-
prompt += `## Codebase Overview
|
|
39281
|
-
There is an index file in the .locus/codebase-index.json and if you need you can check it.
|
|
39282
|
-
|
|
39283
|
-
`;
|
|
39284
|
-
}
|
|
39285
|
-
prompt += `## Instructions
|
|
39286
|
-
1. Execute the prompt based on the provided project context.
|
|
39287
|
-
2. **Paths**: Use relative paths from the project root at all times. Do NOT use absolute local paths (e.g., /Users/...).
|
|
39288
|
-
3. **Git**: Do NOT run \`git add\`, \`git commit\`, \`git push\`, or create branches. The Locus system handles all git operations automatically after your execution completes.
|
|
39289
|
-
4. **Progress**: Do NOT modify \`.locus/project/progress.md\`. The system updates it automatically.`;
|
|
39290
|
-
return prompt;
|
|
39291
|
-
}
|
|
39292
|
-
getProjectConfig() {
|
|
39293
|
-
const configPath = getLocusPath(this.projectPath, "configFile");
|
|
39294
|
-
if (existsSync6(configPath)) {
|
|
39295
|
-
try {
|
|
39296
|
-
return JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
39297
|
-
} catch {
|
|
39298
|
-
return null;
|
|
39299
|
-
}
|
|
39300
|
-
}
|
|
39301
|
-
return null;
|
|
39092
|
+
return this.getFallbackContext() || null;
|
|
39302
39093
|
}
|
|
39303
39094
|
getFallbackContext() {
|
|
39304
39095
|
const readmePath = join6(this.projectPath, "README.md");
|
|
39305
|
-
if (
|
|
39096
|
+
if (existsSync5(readmePath)) {
|
|
39306
39097
|
try {
|
|
39307
|
-
const content =
|
|
39098
|
+
const content = readFileSync5(readmePath, "utf-8");
|
|
39308
39099
|
const limit = 1000;
|
|
39309
39100
|
return content.slice(0, limit) + (content.length > limit ? `
|
|
39310
39101
|
...(truncated)...` : "");
|
|
@@ -39314,32 +39105,28 @@ There is an index file in the .locus/codebase-index.json and if you need you can
|
|
|
39314
39105
|
}
|
|
39315
39106
|
return "";
|
|
39316
39107
|
}
|
|
39317
|
-
|
|
39108
|
+
getKnowledgeBaseSection() {
|
|
39109
|
+
return `You have access to the following documentation directories for context:
|
|
39110
|
+
- Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
|
|
39111
|
+
- Documents: \`.locus/documents\` (synced from cloud)
|
|
39112
|
+
If you need more information about the project strategies, plans, or architecture, read files in these directories.`;
|
|
39113
|
+
}
|
|
39114
|
+
getLearningsContent() {
|
|
39115
|
+
const learningsPath = getLocusPath(this.projectPath, "learningsFile");
|
|
39116
|
+
if (!existsSync5(learningsPath)) {
|
|
39117
|
+
return null;
|
|
39118
|
+
}
|
|
39318
39119
|
try {
|
|
39319
|
-
const
|
|
39320
|
-
const
|
|
39321
|
-
|
|
39322
|
-
|
|
39323
|
-
|
|
39324
|
-
return statSync(join6(this.projectPath, e)).isDirectory();
|
|
39325
|
-
} catch {
|
|
39326
|
-
return false;
|
|
39327
|
-
}
|
|
39328
|
-
});
|
|
39329
|
-
if (folders.length === 0)
|
|
39330
|
-
return "";
|
|
39331
|
-
let structure = `## Project Structure
|
|
39332
|
-
`;
|
|
39333
|
-
structure += `Key directories in this project:
|
|
39334
|
-
`;
|
|
39335
|
-
for (const folder of folders) {
|
|
39336
|
-
structure += `- \`${folder}/\`
|
|
39337
|
-
`;
|
|
39120
|
+
const content = readFileSync5(learningsPath, "utf-8");
|
|
39121
|
+
const lines = content.split(`
|
|
39122
|
+
`).filter((l) => l.startsWith("- "));
|
|
39123
|
+
if (lines.length === 0) {
|
|
39124
|
+
return null;
|
|
39338
39125
|
}
|
|
39339
|
-
return
|
|
39340
|
-
|
|
39126
|
+
return lines.join(`
|
|
39127
|
+
`);
|
|
39341
39128
|
} catch {
|
|
39342
|
-
return
|
|
39129
|
+
return null;
|
|
39343
39130
|
}
|
|
39344
39131
|
}
|
|
39345
39132
|
roleToText(role) {
|
|
@@ -39475,7 +39262,6 @@ class AgentWorker {
|
|
|
39475
39262
|
client;
|
|
39476
39263
|
aiRunner;
|
|
39477
39264
|
taskExecutor;
|
|
39478
|
-
knowledgeBase;
|
|
39479
39265
|
gitWorkflow;
|
|
39480
39266
|
maxTasks = 50;
|
|
39481
39267
|
tasksCompleted = 0;
|
|
@@ -39515,7 +39301,6 @@ class AgentWorker {
|
|
|
39515
39301
|
projectPath,
|
|
39516
39302
|
log
|
|
39517
39303
|
});
|
|
39518
|
-
this.knowledgeBase = new KnowledgeBase(projectPath);
|
|
39519
39304
|
this.gitWorkflow = new GitWorkflow(config2, log);
|
|
39520
39305
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
39521
39306
|
this.log(`Using ${providerLabel} CLI for all phases`, "info");
|
|
@@ -39589,20 +39374,6 @@ class AgentWorker {
|
|
|
39589
39374
|
};
|
|
39590
39375
|
}
|
|
39591
39376
|
}
|
|
39592
|
-
updateProgress(task2, summary) {
|
|
39593
|
-
try {
|
|
39594
|
-
this.knowledgeBase.updateProgress({
|
|
39595
|
-
role: "user",
|
|
39596
|
-
content: task2.title
|
|
39597
|
-
});
|
|
39598
|
-
this.knowledgeBase.updateProgress({
|
|
39599
|
-
role: "assistant",
|
|
39600
|
-
content: summary
|
|
39601
|
-
});
|
|
39602
|
-
} catch (err) {
|
|
39603
|
-
this.log(`Failed to update progress: ${err instanceof Error ? err.message : String(err)}`, "warn");
|
|
39604
|
-
}
|
|
39605
|
-
}
|
|
39606
39377
|
startHeartbeat() {
|
|
39607
39378
|
this.sendHeartbeat();
|
|
39608
39379
|
this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), 60000);
|
|
@@ -39656,7 +39427,7 @@ class AgentWorker {
|
|
|
39656
39427
|
assignedTo: null
|
|
39657
39428
|
});
|
|
39658
39429
|
await this.client.tasks.addComment(task2.id, this.config.workspaceId, {
|
|
39659
|
-
author:
|
|
39430
|
+
author: "system",
|
|
39660
39431
|
text: `⚠️ Agent execution finished with no file changes, so no commit was created.
|
|
39661
39432
|
|
|
39662
39433
|
${result.summary}`
|
|
@@ -39671,13 +39442,12 @@ ${result.summary}`
|
|
|
39671
39442
|
|
|
39672
39443
|
Branch: \`${result.branch}\`` : "";
|
|
39673
39444
|
await this.client.tasks.addComment(task2.id, this.config.workspaceId, {
|
|
39674
|
-
author:
|
|
39445
|
+
author: "system",
|
|
39675
39446
|
text: `✅ ${result.summary}${branchInfo}`
|
|
39676
39447
|
});
|
|
39677
39448
|
this.tasksCompleted++;
|
|
39678
39449
|
this.completedTaskList.push({ title: task2.title, id: task2.id });
|
|
39679
39450
|
this.taskSummaries.push(result.summary);
|
|
39680
|
-
this.updateProgress(task2, result.summary);
|
|
39681
39451
|
}
|
|
39682
39452
|
} else {
|
|
39683
39453
|
this.log(`Failed: ${task2.title} - ${result.summary}`, "error");
|
|
@@ -39686,7 +39456,7 @@ Branch: \`${result.branch}\`` : "";
|
|
|
39686
39456
|
assignedTo: null
|
|
39687
39457
|
});
|
|
39688
39458
|
await this.client.tasks.addComment(task2.id, this.config.workspaceId, {
|
|
39689
|
-
author:
|
|
39459
|
+
author: "system",
|
|
39690
39460
|
text: `❌ ${result.summary}`
|
|
39691
39461
|
});
|
|
39692
39462
|
}
|
|
@@ -39723,7 +39493,6 @@ var init_worker = __esm(() => {
|
|
|
39723
39493
|
init_config();
|
|
39724
39494
|
init_git_utils();
|
|
39725
39495
|
init_src2();
|
|
39726
|
-
init_knowledge_base();
|
|
39727
39496
|
init_colors();
|
|
39728
39497
|
init_git_workflow();
|
|
39729
39498
|
init_task_executor();
|
|
@@ -40173,12 +39942,12 @@ var init_event_emitter = __esm(() => {
|
|
|
40173
39942
|
|
|
40174
39943
|
// ../sdk/src/exec/history-manager.ts
|
|
40175
39944
|
import {
|
|
40176
|
-
existsSync as
|
|
40177
|
-
mkdirSync as
|
|
40178
|
-
readdirSync as
|
|
40179
|
-
readFileSync as
|
|
39945
|
+
existsSync as existsSync6,
|
|
39946
|
+
mkdirSync as mkdirSync3,
|
|
39947
|
+
readdirSync as readdirSync2,
|
|
39948
|
+
readFileSync as readFileSync6,
|
|
40180
39949
|
rmSync,
|
|
40181
|
-
writeFileSync as
|
|
39950
|
+
writeFileSync as writeFileSync3
|
|
40182
39951
|
} from "node:fs";
|
|
40183
39952
|
import { join as join7 } from "node:path";
|
|
40184
39953
|
function generateSessionId2() {
|
|
@@ -40196,8 +39965,8 @@ class HistoryManager {
|
|
|
40196
39965
|
this.ensureHistoryDir();
|
|
40197
39966
|
}
|
|
40198
39967
|
ensureHistoryDir() {
|
|
40199
|
-
if (!
|
|
40200
|
-
|
|
39968
|
+
if (!existsSync6(this.historyDir)) {
|
|
39969
|
+
mkdirSync3(this.historyDir, { recursive: true });
|
|
40201
39970
|
}
|
|
40202
39971
|
}
|
|
40203
39972
|
getSessionPath(sessionId) {
|
|
@@ -40206,15 +39975,15 @@ class HistoryManager {
|
|
|
40206
39975
|
saveSession(session2) {
|
|
40207
39976
|
const filePath = this.getSessionPath(session2.id);
|
|
40208
39977
|
session2.updatedAt = Date.now();
|
|
40209
|
-
|
|
39978
|
+
writeFileSync3(filePath, JSON.stringify(session2, null, 2), "utf-8");
|
|
40210
39979
|
}
|
|
40211
39980
|
loadSession(sessionId) {
|
|
40212
39981
|
const filePath = this.getSessionPath(sessionId);
|
|
40213
|
-
if (!
|
|
39982
|
+
if (!existsSync6(filePath)) {
|
|
40214
39983
|
return null;
|
|
40215
39984
|
}
|
|
40216
39985
|
try {
|
|
40217
|
-
const content =
|
|
39986
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
40218
39987
|
return JSON.parse(content);
|
|
40219
39988
|
} catch {
|
|
40220
39989
|
return null;
|
|
@@ -40222,7 +39991,7 @@ class HistoryManager {
|
|
|
40222
39991
|
}
|
|
40223
39992
|
deleteSession(sessionId) {
|
|
40224
39993
|
const filePath = this.getSessionPath(sessionId);
|
|
40225
|
-
if (!
|
|
39994
|
+
if (!existsSync6(filePath)) {
|
|
40226
39995
|
return false;
|
|
40227
39996
|
}
|
|
40228
39997
|
try {
|
|
@@ -40233,7 +40002,7 @@ class HistoryManager {
|
|
|
40233
40002
|
}
|
|
40234
40003
|
}
|
|
40235
40004
|
listSessions(options) {
|
|
40236
|
-
const files =
|
|
40005
|
+
const files = readdirSync2(this.historyDir);
|
|
40237
40006
|
let sessions = [];
|
|
40238
40007
|
for (const file2 of files) {
|
|
40239
40008
|
if (file2.endsWith(".json")) {
|
|
@@ -40306,11 +40075,11 @@ class HistoryManager {
|
|
|
40306
40075
|
return deleted;
|
|
40307
40076
|
}
|
|
40308
40077
|
getSessionCount() {
|
|
40309
|
-
const files =
|
|
40078
|
+
const files = readdirSync2(this.historyDir);
|
|
40310
40079
|
return files.filter((f) => f.endsWith(".json")).length;
|
|
40311
40080
|
}
|
|
40312
40081
|
sessionExists(sessionId) {
|
|
40313
|
-
return
|
|
40082
|
+
return existsSync6(this.getSessionPath(sessionId));
|
|
40314
40083
|
}
|
|
40315
40084
|
findSessionByPartialId(partialId) {
|
|
40316
40085
|
const sessions = this.listSessions();
|
|
@@ -40324,7 +40093,7 @@ class HistoryManager {
|
|
|
40324
40093
|
return this.historyDir;
|
|
40325
40094
|
}
|
|
40326
40095
|
clearAllSessions() {
|
|
40327
|
-
const files =
|
|
40096
|
+
const files = readdirSync2(this.historyDir);
|
|
40328
40097
|
let deleted = 0;
|
|
40329
40098
|
for (const file2 of files) {
|
|
40330
40099
|
if (file2.endsWith(".json")) {
|
|
@@ -40618,8 +40387,8 @@ var init_git = __esm(() => {
|
|
|
40618
40387
|
|
|
40619
40388
|
// ../sdk/src/orchestrator/index.ts
|
|
40620
40389
|
import { spawn as spawn3 } from "node:child_process";
|
|
40621
|
-
import { existsSync as
|
|
40622
|
-
import { dirname as
|
|
40390
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
40391
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
40623
40392
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
40624
40393
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
40625
40394
|
function killProcessTree(proc) {
|
|
@@ -40906,18 +40675,21 @@ ${agentId} finished (exit code: ${code})`);
|
|
|
40906
40675
|
}
|
|
40907
40676
|
resolveWorkerPath() {
|
|
40908
40677
|
const currentModulePath = fileURLToPath2(import.meta.url);
|
|
40909
|
-
const currentModuleDir =
|
|
40678
|
+
const currentModuleDir = dirname2(currentModulePath);
|
|
40910
40679
|
const potentialPaths = [
|
|
40911
40680
|
join8(currentModuleDir, "..", "agent", "worker.js"),
|
|
40912
40681
|
join8(currentModuleDir, "agent", "worker.js"),
|
|
40913
40682
|
join8(currentModuleDir, "worker.js"),
|
|
40914
40683
|
join8(currentModuleDir, "..", "agent", "worker.ts")
|
|
40915
40684
|
];
|
|
40916
|
-
return potentialPaths.find((p) =>
|
|
40685
|
+
return potentialPaths.find((p) => existsSync7(p));
|
|
40917
40686
|
}
|
|
40918
40687
|
};
|
|
40919
40688
|
});
|
|
40920
40689
|
|
|
40690
|
+
// ../sdk/src/utils/structured-output.ts
|
|
40691
|
+
var init_structured_output = () => {};
|
|
40692
|
+
|
|
40921
40693
|
// ../sdk/src/planning/sprint-plan.ts
|
|
40922
40694
|
function sprintPlanToMarkdown(plan) {
|
|
40923
40695
|
const lines = [];
|
|
@@ -40992,68 +40764,48 @@ function plannedTasksToCreatePayloads(plan, sprintId) {
|
|
|
40992
40764
|
}))
|
|
40993
40765
|
}));
|
|
40994
40766
|
}
|
|
40995
|
-
|
|
40996
|
-
const jsonStr = extractJsonFromLLMOutput(raw);
|
|
40997
|
-
let parsed;
|
|
40998
|
-
try {
|
|
40999
|
-
parsed = JSON.parse(jsonStr);
|
|
41000
|
-
} catch (err) {
|
|
41001
|
-
const preview = jsonStr.slice(0, 200);
|
|
41002
|
-
throw new Error(`Failed to parse sprint plan JSON: ${err instanceof Error ? err.message : String(err)}
|
|
41003
|
-
Extracted JSON preview: ${preview}`);
|
|
41004
|
-
}
|
|
41005
|
-
if (parsed.revisedPlan) {
|
|
41006
|
-
parsed = parsed.revisedPlan;
|
|
41007
|
-
}
|
|
41008
|
-
const now = new Date().toISOString();
|
|
41009
|
-
const id = `plan-${Date.now()}`;
|
|
41010
|
-
const tasks2 = (parsed.tasks || []).map((t, i) => ({
|
|
41011
|
-
index: i + 1,
|
|
41012
|
-
title: t.title || `Task ${i + 1}`,
|
|
41013
|
-
description: t.description || "",
|
|
41014
|
-
assigneeRole: t.assigneeRole || "BACKEND",
|
|
41015
|
-
priority: t.priority || "MEDIUM",
|
|
41016
|
-
complexity: t.complexity || 3,
|
|
41017
|
-
acceptanceCriteria: t.acceptanceCriteria || [],
|
|
41018
|
-
labels: t.labels || []
|
|
41019
|
-
}));
|
|
41020
|
-
return {
|
|
41021
|
-
id,
|
|
41022
|
-
name: parsed.name || "Unnamed Sprint",
|
|
41023
|
-
goal: parsed.goal || directive,
|
|
41024
|
-
directive,
|
|
41025
|
-
tasks: tasks2,
|
|
41026
|
-
risks: (parsed.risks || []).map((r) => ({
|
|
41027
|
-
description: r.description || "",
|
|
41028
|
-
mitigation: r.mitigation || "",
|
|
41029
|
-
severity: r.severity || "medium"
|
|
41030
|
-
})),
|
|
41031
|
-
estimatedDays: parsed.estimatedDays || 1,
|
|
41032
|
-
status: "pending",
|
|
41033
|
-
createdAt: now,
|
|
41034
|
-
updatedAt: now
|
|
41035
|
-
};
|
|
41036
|
-
}
|
|
40767
|
+
var PlannedTaskSchema, SprintPlanRiskSchema, PlannerOutputSchema;
|
|
41037
40768
|
var init_sprint_plan = __esm(() => {
|
|
41038
40769
|
init_src();
|
|
40770
|
+
init_zod();
|
|
40771
|
+
init_structured_output();
|
|
40772
|
+
PlannedTaskSchema = exports_external.object({
|
|
40773
|
+
title: exports_external.string().default("Untitled Task"),
|
|
40774
|
+
description: exports_external.string().default(""),
|
|
40775
|
+
assigneeRole: exports_external.enum(["BACKEND", "FRONTEND", "QA", "PM", "DESIGN"]).default("BACKEND"),
|
|
40776
|
+
priority: exports_external.enum(["CRITICAL", "HIGH", "MEDIUM", "LOW"]).default("MEDIUM"),
|
|
40777
|
+
complexity: exports_external.number().min(1).max(5).default(3),
|
|
40778
|
+
acceptanceCriteria: exports_external.array(exports_external.string()).default([]),
|
|
40779
|
+
labels: exports_external.array(exports_external.string()).default([])
|
|
40780
|
+
});
|
|
40781
|
+
SprintPlanRiskSchema = exports_external.object({
|
|
40782
|
+
description: exports_external.string().default(""),
|
|
40783
|
+
mitigation: exports_external.string().default(""),
|
|
40784
|
+
severity: exports_external.enum(["low", "medium", "high"]).default("medium")
|
|
40785
|
+
});
|
|
40786
|
+
PlannerOutputSchema = exports_external.object({
|
|
40787
|
+
name: exports_external.string().default("Unnamed Sprint"),
|
|
40788
|
+
goal: exports_external.string().default(""),
|
|
40789
|
+
estimatedDays: exports_external.number().default(1),
|
|
40790
|
+
tasks: exports_external.array(PlannedTaskSchema).default([]),
|
|
40791
|
+
risks: exports_external.array(SprintPlanRiskSchema).default([])
|
|
40792
|
+
});
|
|
41039
40793
|
});
|
|
41040
40794
|
|
|
41041
40795
|
// ../sdk/src/planning/plan-manager.ts
|
|
41042
40796
|
import {
|
|
41043
|
-
existsSync as
|
|
41044
|
-
mkdirSync as
|
|
41045
|
-
readdirSync as
|
|
41046
|
-
readFileSync as
|
|
40797
|
+
existsSync as existsSync8,
|
|
40798
|
+
mkdirSync as mkdirSync4,
|
|
40799
|
+
readdirSync as readdirSync3,
|
|
40800
|
+
readFileSync as readFileSync7,
|
|
41047
40801
|
unlinkSync as unlinkSync2,
|
|
41048
|
-
writeFileSync as
|
|
40802
|
+
writeFileSync as writeFileSync4
|
|
41049
40803
|
} from "node:fs";
|
|
41050
40804
|
import { join as join9 } from "node:path";
|
|
41051
40805
|
|
|
41052
40806
|
class PlanManager {
|
|
41053
|
-
projectPath;
|
|
41054
40807
|
plansDir;
|
|
41055
40808
|
constructor(projectPath) {
|
|
41056
|
-
this.projectPath = projectPath;
|
|
41057
40809
|
this.plansDir = getLocusPath(projectPath, "plansDir");
|
|
41058
40810
|
}
|
|
41059
40811
|
save(plan) {
|
|
@@ -41061,17 +40813,17 @@ class PlanManager {
|
|
|
41061
40813
|
const slug = this.slugify(plan.name);
|
|
41062
40814
|
const jsonPath = join9(this.plansDir, `${slug}.json`);
|
|
41063
40815
|
const mdPath = join9(this.plansDir, `sprint-${slug}.md`);
|
|
41064
|
-
|
|
41065
|
-
|
|
40816
|
+
writeFileSync4(jsonPath, JSON.stringify(plan, null, 2), "utf-8");
|
|
40817
|
+
writeFileSync4(mdPath, sprintPlanToMarkdown(plan), "utf-8");
|
|
41066
40818
|
return plan.id;
|
|
41067
40819
|
}
|
|
41068
40820
|
load(idOrSlug) {
|
|
41069
40821
|
this.ensurePlansDir();
|
|
41070
|
-
const files =
|
|
40822
|
+
const files = readdirSync3(this.plansDir).filter((f) => f.endsWith(".json"));
|
|
41071
40823
|
for (const file2 of files) {
|
|
41072
40824
|
const filePath = join9(this.plansDir, file2);
|
|
41073
40825
|
try {
|
|
41074
|
-
const plan = JSON.parse(
|
|
40826
|
+
const plan = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
41075
40827
|
if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
|
|
41076
40828
|
return plan;
|
|
41077
40829
|
}
|
|
@@ -41081,11 +40833,11 @@ class PlanManager {
|
|
|
41081
40833
|
}
|
|
41082
40834
|
list(status) {
|
|
41083
40835
|
this.ensurePlansDir();
|
|
41084
|
-
const files =
|
|
40836
|
+
const files = readdirSync3(this.plansDir).filter((f) => f.endsWith(".json"));
|
|
41085
40837
|
const plans = [];
|
|
41086
40838
|
for (const file2 of files) {
|
|
41087
40839
|
try {
|
|
41088
|
-
const plan = JSON.parse(
|
|
40840
|
+
const plan = JSON.parse(readFileSync7(join9(this.plansDir, file2), "utf-8"));
|
|
41089
40841
|
if (!status || plan.status === status) {
|
|
41090
40842
|
plans.push(plan);
|
|
41091
40843
|
}
|
|
@@ -41115,15 +40867,6 @@ class PlanManager {
|
|
|
41115
40867
|
plan.status = "approved";
|
|
41116
40868
|
plan.updatedAt = new Date().toISOString();
|
|
41117
40869
|
this.save(plan);
|
|
41118
|
-
const kb = new KnowledgeBase(this.projectPath);
|
|
41119
|
-
kb.updateProgress({
|
|
41120
|
-
role: "user",
|
|
41121
|
-
content: `Start sprint: ${plan.name}`
|
|
41122
|
-
});
|
|
41123
|
-
kb.updateProgress({
|
|
41124
|
-
role: "assistant",
|
|
41125
|
-
content: `Sprint started with ${tasks2.length} tasks. Goal: ${plan.goal}`
|
|
41126
|
-
});
|
|
41127
40870
|
return { sprint: sprint2, tasks: tasks2 };
|
|
41128
40871
|
}
|
|
41129
40872
|
reject(idOrSlug, feedback) {
|
|
@@ -41151,17 +40894,17 @@ class PlanManager {
|
|
|
41151
40894
|
}
|
|
41152
40895
|
delete(idOrSlug) {
|
|
41153
40896
|
this.ensurePlansDir();
|
|
41154
|
-
const files =
|
|
40897
|
+
const files = readdirSync3(this.plansDir);
|
|
41155
40898
|
for (const file2 of files) {
|
|
41156
40899
|
const filePath = join9(this.plansDir, file2);
|
|
41157
40900
|
if (!file2.endsWith(".json"))
|
|
41158
40901
|
continue;
|
|
41159
40902
|
try {
|
|
41160
|
-
const plan = JSON.parse(
|
|
40903
|
+
const plan = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
41161
40904
|
if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
|
|
41162
40905
|
unlinkSync2(filePath);
|
|
41163
40906
|
const mdPath = join9(this.plansDir, `sprint-${this.slugify(plan.name)}.md`);
|
|
41164
|
-
if (
|
|
40907
|
+
if (existsSync8(mdPath)) {
|
|
41165
40908
|
unlinkSync2(mdPath);
|
|
41166
40909
|
}
|
|
41167
40910
|
return;
|
|
@@ -41176,8 +40919,8 @@ class PlanManager {
|
|
|
41176
40919
|
return sprintPlanToMarkdown(plan);
|
|
41177
40920
|
}
|
|
41178
40921
|
ensurePlansDir() {
|
|
41179
|
-
if (!
|
|
41180
|
-
|
|
40922
|
+
if (!existsSync8(this.plansDir)) {
|
|
40923
|
+
mkdirSync4(this.plansDir, { recursive: true });
|
|
41181
40924
|
}
|
|
41182
40925
|
}
|
|
41183
40926
|
slugify(name) {
|
|
@@ -41186,231 +40929,81 @@ class PlanManager {
|
|
|
41186
40929
|
}
|
|
41187
40930
|
var init_plan_manager = __esm(() => {
|
|
41188
40931
|
init_config();
|
|
41189
|
-
init_knowledge_base();
|
|
41190
40932
|
init_sprint_plan();
|
|
41191
40933
|
});
|
|
41192
40934
|
|
|
41193
|
-
// ../sdk/src/planning/agents/cross-task-reviewer.ts
|
|
41194
|
-
function buildCrossTaskReviewerPrompt(input) {
|
|
41195
|
-
let prompt = `# Role: Cross-Task Reviewer (Architect + Engineer + Planner)
|
|
41196
|
-
|
|
41197
|
-
You are a combined Architect, Senior Engineer, and Sprint Planner performing a FINAL review of a sprint plan. Your focus is ensuring that tasks are correctly ordered, well-scoped, and will execute successfully in sequence.
|
|
41198
|
-
|
|
41199
|
-
## Context
|
|
41200
|
-
|
|
41201
|
-
In this system, tasks are executed SEQUENTIALLY by a single agent on ONE branch:
|
|
41202
|
-
- Tasks run one at a time, in the order they appear in the array
|
|
41203
|
-
- Each task's changes are committed before the next task starts
|
|
41204
|
-
- Later tasks can see and build on earlier tasks' work
|
|
41205
|
-
- The final result is a single branch with all changes, which becomes a pull request
|
|
41206
|
-
|
|
41207
|
-
This means:
|
|
41208
|
-
- Task ordering is critical — a task must NOT depend on a later task's output
|
|
41209
|
-
- Foundation work (config, schemas, shared code) must come first
|
|
41210
|
-
- Each task should be a focused, logical unit of work
|
|
41211
|
-
|
|
41212
|
-
## CEO Directive
|
|
41213
|
-
> ${input.directive}
|
|
41214
|
-
`;
|
|
41215
|
-
if (input.feedback) {
|
|
41216
|
-
prompt += `
|
|
41217
|
-
## CEO Feedback on Previous Plan
|
|
41218
|
-
> ${input.feedback}
|
|
41219
|
-
|
|
41220
|
-
IMPORTANT: Ensure the reviewed plan still addresses this feedback.
|
|
41221
|
-
`;
|
|
41222
|
-
}
|
|
41223
|
-
prompt += `
|
|
41224
|
-
## Project Context
|
|
41225
|
-
${input.projectContext || "No project context available."}
|
|
41226
|
-
|
|
41227
|
-
## Sprint Plan to Review
|
|
41228
|
-
${input.plannerOutput}
|
|
41229
|
-
|
|
41230
|
-
## Your Review Checklist
|
|
41231
|
-
|
|
41232
|
-
Go through EACH task and check for:
|
|
41233
|
-
|
|
41234
|
-
### 1. Ordering & Dependency Analysis
|
|
41235
|
-
For each task, verify:
|
|
41236
|
-
- Does it depend on any task that appears LATER in the list? If so, reorder.
|
|
41237
|
-
- Are foundational tasks (config, schemas, shared code) at the beginning?
|
|
41238
|
-
- Is the overall execution order logical?
|
|
41239
|
-
|
|
41240
|
-
### 2. Scope & Completeness
|
|
41241
|
-
For each task, verify:
|
|
41242
|
-
- Is the task well-scoped? Not too large, not too trivial?
|
|
41243
|
-
- Does it include ALL changes needed for its goal (given earlier tasks are done)?
|
|
41244
|
-
- Are there any missing tasks that should be added?
|
|
41245
|
-
|
|
41246
|
-
### 3. Description Quality Validation
|
|
41247
|
-
For each task, verify the description is a clear, actionable implementation guide. Each description must specify:
|
|
41248
|
-
- **What to do** — the specific goal and expected behavior/outcome
|
|
41249
|
-
- **Where to do it** — specific files, modules, or directories to modify or create
|
|
41250
|
-
- **How to do it** — implementation approach, patterns to follow, existing utilities to use
|
|
41251
|
-
- **Boundaries** — what is NOT in scope for this task
|
|
41252
|
-
|
|
41253
|
-
If any description is vague (e.g., "Add authentication", "Update the API", "Fix the frontend"), rewrite it with concrete implementation details. The executing agent receives ONLY the task title, description, and acceptance criteria as its instructions.
|
|
41254
|
-
|
|
41255
|
-
### 4. Risk Assessment
|
|
41256
|
-
- Are there tasks that might fail or have unknowns?
|
|
41257
|
-
- Is the sprint scope realistic for sequential execution?
|
|
41258
|
-
|
|
41259
|
-
## Output Format
|
|
41260
|
-
|
|
41261
|
-
Your entire response must be a single JSON object — no text before it, no text after it, no markdown code blocks, no explanation. Start your response with the "{" character:
|
|
41262
|
-
|
|
41263
|
-
{
|
|
41264
|
-
"hasIssues": true | false,
|
|
41265
|
-
"issues": [
|
|
41266
|
-
{
|
|
41267
|
-
"type": "wrong_order" | "missing_task" | "scope_issue" | "vague_description",
|
|
41268
|
-
"description": "string describing the specific issue",
|
|
41269
|
-
"affectedTasks": ["Task Title 1", "Task Title 2"],
|
|
41270
|
-
"resolution": "string describing how to fix it"
|
|
41271
|
-
}
|
|
41272
|
-
],
|
|
41273
|
-
"revisedPlan": {
|
|
41274
|
-
"name": "string (2-4 words)",
|
|
41275
|
-
"goal": "string (1 paragraph)",
|
|
41276
|
-
"estimatedDays": 3,
|
|
41277
|
-
"tasks": [
|
|
41278
|
-
{
|
|
41279
|
-
"title": "string",
|
|
41280
|
-
"description": "string (detailed implementation guide: what to do, where to do it, how to do it, and boundaries)",
|
|
41281
|
-
"assigneeRole": "BACKEND | FRONTEND | QA | PM | DESIGN",
|
|
41282
|
-
"priority": "CRITICAL | HIGH | MEDIUM | LOW",
|
|
41283
|
-
"labels": ["string"],
|
|
41284
|
-
"acceptanceCriteria": ["string"],
|
|
41285
|
-
"complexity": 3
|
|
41286
|
-
}
|
|
41287
|
-
],
|
|
41288
|
-
"risks": [
|
|
41289
|
-
{
|
|
41290
|
-
"description": "string",
|
|
41291
|
-
"mitigation": "string",
|
|
41292
|
-
"severity": "low | medium | high"
|
|
41293
|
-
}
|
|
41294
|
-
]
|
|
41295
|
-
}
|
|
41296
|
-
}
|
|
41297
|
-
|
|
41298
|
-
IMPORTANT:
|
|
41299
|
-
- If hasIssues is true, the revisedPlan MUST contain the corrected task list with issues resolved (reordered, descriptions rewritten, missing tasks added, etc.)
|
|
41300
|
-
- If hasIssues is false, the revisedPlan should be identical to the input plan (no changes needed)
|
|
41301
|
-
- The revisedPlan is ALWAYS required — it becomes the final plan
|
|
41302
|
-
- Ensure every task description is a detailed implementation guide (what, where, how, boundaries) — rewrite vague descriptions
|
|
41303
|
-
- Tasks execute sequentially — the array order IS the execution order`;
|
|
41304
|
-
return prompt;
|
|
41305
|
-
}
|
|
41306
|
-
|
|
41307
40935
|
// ../sdk/src/planning/agents/planner.ts
|
|
41308
40936
|
function buildPlannerPrompt(input) {
|
|
41309
|
-
let
|
|
41310
|
-
|
|
41311
|
-
You are a Sprint Planner — an expert engineer, architect, and project organizer rolled into one. Your job is to take a CEO directive and produce a complete, ready-to-execute sprint plan in a single pass.
|
|
41312
|
-
|
|
41313
|
-
## CEO Directive
|
|
41314
|
-
> ${input.directive}
|
|
41315
|
-
`;
|
|
40937
|
+
let feedbackSection = "";
|
|
41316
40938
|
if (input.feedback) {
|
|
41317
|
-
|
|
41318
|
-
|
|
41319
|
-
|
|
41320
|
-
|
|
41321
|
-
|
|
40939
|
+
feedbackSection = `
|
|
40940
|
+
<ceo_feedback>
|
|
40941
|
+
The CEO has reviewed a previous plan and wants changes. Incorporate this feedback:
|
|
40942
|
+
${input.feedback}
|
|
40943
|
+
</ceo_feedback>
|
|
41322
40944
|
`;
|
|
41323
40945
|
}
|
|
41324
|
-
|
|
41325
|
-
|
|
41326
|
-
|
|
41327
|
-
|
|
41328
|
-
|
|
41329
|
-
|
|
41330
|
-
|
|
41331
|
-
|
|
41332
|
-
|
|
41333
|
-
|
|
41334
|
-
|
|
41335
|
-
|
|
41336
|
-
|
|
41337
|
-
|
|
41338
|
-
|
|
41339
|
-
|
|
41340
|
-
|
|
41341
|
-
|
|
41342
|
-
|
|
41343
|
-
For each task, provide:
|
|
41344
|
-
- **Title** — Clear, action-oriented (e.g., "Implement user registration API endpoint")
|
|
41345
|
-
- **Description** — A detailed, actionable implementation guide (see below)
|
|
41346
|
-
- **Assignee Role** — BACKEND, FRONTEND, QA, PM, or DESIGN
|
|
41347
|
-
- **Priority** — CRITICAL, HIGH, MEDIUM, or LOW
|
|
41348
|
-
- **Complexity** — 1 (trivial) to 5 (very complex)
|
|
41349
|
-
- **Labels** — Relevant tags (e.g., "api", "database", "ui", "auth")
|
|
41350
|
-
- **Acceptance Criteria** — Specific, testable conditions for completion
|
|
41351
|
-
|
|
41352
|
-
### CRITICAL: Task Description Requirements
|
|
41353
|
-
|
|
41354
|
-
Each task description will be handed to an INDEPENDENT agent as its primary instruction. The agent will have access to the codebase but NO context about the planning meeting. Each description MUST include:
|
|
41355
|
-
|
|
41356
|
-
1. **What to do** — Clearly state the goal and expected behavior/outcome
|
|
41357
|
-
2. **Where to do it** — List specific files, modules, or directories to modify or create. Reference existing code paths when extending functionality
|
|
41358
|
-
3. **How to do it** — Key implementation details: which patterns to follow, which existing utilities or services to use, what the data flow looks like
|
|
41359
|
-
4. **Boundaries** — What is NOT in scope for this task to prevent overlap with other tasks
|
|
41360
|
-
|
|
41361
|
-
Bad example: "Add authentication to the API."
|
|
41362
|
-
Good example: "Implement JWT-based authentication middleware in src/middleware/auth.ts. Create a verifyToken middleware that extracts the Bearer token from the Authorization header, validates it using the existing JWT_SECRET from env config, and attaches the decoded user payload to req.user. Apply this middleware to all routes in src/routes/protected/. This task does NOT include user registration or password reset — those are handled separately."
|
|
41363
|
-
|
|
41364
|
-
### CRITICAL: Task Ordering Rules
|
|
41365
|
-
|
|
41366
|
-
Tasks are executed SEQUENTIALLY by a single agent on ONE branch. The agent works through tasks in array order. Therefore:
|
|
41367
|
-
|
|
41368
|
-
1. **Foundation first.** Place foundational tasks (schemas, config, shared code) at the beginning. Later tasks can build on earlier ones since they run in sequence.
|
|
41369
|
-
2. **No forward dependencies.** A task must NOT depend on a task that appears later in the list.
|
|
41370
|
-
3. **Each task is self-contained for its scope.** A task can depend on earlier tasks but must include all changes needed for its own goal.
|
|
41371
|
-
4. **Keep tasks focused.** Each task should do one logical unit of work. Avoid trivially small or overly large tasks.
|
|
41372
|
-
5. **Merge related trivial work.** If two pieces of work are trivially small and tightly related, combine them into one task.
|
|
41373
|
-
|
|
41374
|
-
### Sprint Scope Guidelines
|
|
41375
|
-
|
|
41376
|
-
- If the sprint would exceed 12 tasks, reduce scope or merge related tasks
|
|
41377
|
-
- Ensure acceptance criteria are specific and testable
|
|
41378
|
-
- Keep the sprint focused on the directive — avoid scope creep
|
|
41379
|
-
|
|
41380
|
-
## Output Format
|
|
41381
|
-
|
|
41382
|
-
Your entire response must be a single JSON object — no text before it, no text after it, no markdown code blocks, no explanation. Start your response with the "{" character:
|
|
41383
|
-
|
|
40946
|
+
const now = new Date().toISOString();
|
|
40947
|
+
return `<sprint_planning>
|
|
40948
|
+
Create a sprint plan for this directive: ${input.directive}
|
|
40949
|
+
${feedbackSection}
|
|
40950
|
+
<rules>
|
|
40951
|
+
- Tasks execute sequentially by one agent on one branch
|
|
40952
|
+
- Each task must be self-contained with clear What/Where/How
|
|
40953
|
+
- No forward dependencies (task N can't need task N+1)
|
|
40954
|
+
- Foundation first (shared code, types, schemas before features)
|
|
40955
|
+
- Be specific: exact file paths, function names, implementation details
|
|
40956
|
+
- Each task description is the ONLY instruction an independent agent receives — include all context it needs
|
|
40957
|
+
- Merge trivially small related work into one task
|
|
40958
|
+
- Assign appropriate roles: BACKEND, FRONTEND, QA, PM, or DESIGN
|
|
40959
|
+
</rules>
|
|
40960
|
+
|
|
40961
|
+
<output>
|
|
40962
|
+
Write the sprint plan as a JSON file to: ${input.plansDir}/${input.fileName}.json
|
|
40963
|
+
|
|
40964
|
+
The JSON file must contain this exact structure:
|
|
41384
40965
|
{
|
|
41385
|
-
"
|
|
41386
|
-
"
|
|
41387
|
-
"
|
|
40966
|
+
"id": "${input.planId}",
|
|
40967
|
+
"name": "2-4 words",
|
|
40968
|
+
"goal": "One paragraph of what this delivers",
|
|
40969
|
+
"directive": ${JSON.stringify(input.directive)},
|
|
40970
|
+
"estimatedDays": number,
|
|
40971
|
+
"status": "pending",
|
|
40972
|
+
"createdAt": "${now}",
|
|
40973
|
+
"updatedAt": "${now}",
|
|
41388
40974
|
"tasks": [
|
|
41389
40975
|
{
|
|
41390
|
-
"
|
|
41391
|
-
"
|
|
41392
|
-
"
|
|
41393
|
-
"
|
|
41394
|
-
"
|
|
41395
|
-
"
|
|
41396
|
-
"
|
|
40976
|
+
"index": 1,
|
|
40977
|
+
"title": "Action-oriented title",
|
|
40978
|
+
"description": "What: goal\\nWhere: files to modify\\nHow: implementation details\\nBoundaries: what's excluded",
|
|
40979
|
+
"assigneeRole": "BACKEND|FRONTEND|QA|PM|DESIGN",
|
|
40980
|
+
"priority": "CRITICAL|HIGH|MEDIUM|LOW",
|
|
40981
|
+
"labels": ["tags"],
|
|
40982
|
+
"acceptanceCriteria": ["testable conditions"],
|
|
40983
|
+
"complexity": 1-5
|
|
41397
40984
|
}
|
|
41398
40985
|
],
|
|
41399
40986
|
"risks": [
|
|
41400
40987
|
{
|
|
41401
|
-
"description": "
|
|
41402
|
-
"mitigation": "
|
|
41403
|
-
"severity": "low
|
|
40988
|
+
"description": "What could go wrong",
|
|
40989
|
+
"mitigation": "How to handle it",
|
|
40990
|
+
"severity": "low|medium|high"
|
|
41404
40991
|
}
|
|
41405
40992
|
]
|
|
41406
40993
|
}
|
|
41407
40994
|
|
|
41408
|
-
IMPORTANT:
|
|
41409
|
-
|
|
40995
|
+
IMPORTANT:
|
|
40996
|
+
- Write the file directly using your file writing tool. Do NOT output the JSON as text.
|
|
40997
|
+
- Tasks must have sequential "index" values starting at 1.
|
|
40998
|
+
- The file must be valid JSON with no comments or trailing commas.
|
|
40999
|
+
- Do not create any other files. Only create the single JSON file specified above.
|
|
41000
|
+
</output>
|
|
41001
|
+
</sprint_planning>`;
|
|
41410
41002
|
}
|
|
41411
41003
|
|
|
41412
41004
|
// ../sdk/src/planning/planning-meeting.ts
|
|
41413
|
-
import { existsSync as
|
|
41005
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync8 } from "node:fs";
|
|
41006
|
+
import { join as join10 } from "node:path";
|
|
41414
41007
|
|
|
41415
41008
|
class PlanningMeeting {
|
|
41416
41009
|
projectPath;
|
|
@@ -41424,72 +41017,44 @@ class PlanningMeeting {
|
|
|
41424
41017
|
});
|
|
41425
41018
|
}
|
|
41426
41019
|
async run(directive, feedback) {
|
|
41427
|
-
|
|
41428
|
-
const
|
|
41429
|
-
|
|
41430
|
-
|
|
41020
|
+
this.log("Planning sprint...", "info");
|
|
41021
|
+
const plansDir = getLocusPath(this.projectPath, "plansDir");
|
|
41022
|
+
if (!existsSync9(plansDir)) {
|
|
41023
|
+
mkdirSync5(plansDir, { recursive: true });
|
|
41024
|
+
}
|
|
41025
|
+
const ts = Date.now();
|
|
41026
|
+
const planId = `plan-${ts}`;
|
|
41027
|
+
const fileName = `plan-${ts}`;
|
|
41028
|
+
const prompt = buildPlannerPrompt({
|
|
41431
41029
|
directive,
|
|
41432
|
-
|
|
41433
|
-
|
|
41434
|
-
|
|
41030
|
+
feedback,
|
|
41031
|
+
plansDir,
|
|
41032
|
+
planId,
|
|
41033
|
+
fileName
|
|
41435
41034
|
});
|
|
41436
|
-
const
|
|
41437
|
-
this.log("
|
|
41438
|
-
|
|
41439
|
-
|
|
41440
|
-
|
|
41441
|
-
|
|
41442
|
-
|
|
41443
|
-
|
|
41444
|
-
}
|
|
41445
|
-
|
|
41446
|
-
|
|
41447
|
-
|
|
41035
|
+
const response = await this.aiRunner.run(prompt);
|
|
41036
|
+
this.log("Planning meeting complete.", "success");
|
|
41037
|
+
const expectedPath = join10(plansDir, `${fileName}.json`);
|
|
41038
|
+
let plan = null;
|
|
41039
|
+
if (existsSync9(expectedPath)) {
|
|
41040
|
+
try {
|
|
41041
|
+
plan = JSON.parse(readFileSync8(expectedPath, "utf-8"));
|
|
41042
|
+
} catch {}
|
|
41043
|
+
}
|
|
41044
|
+
if (!plan) {
|
|
41045
|
+
throw new Error("Planning agent did not create the expected plan JSON file. " + "Check the agent output for errors.");
|
|
41046
|
+
}
|
|
41448
41047
|
if (feedback) {
|
|
41449
41048
|
plan.feedback = feedback;
|
|
41450
41049
|
}
|
|
41451
41050
|
return {
|
|
41452
41051
|
plan,
|
|
41453
|
-
|
|
41454
|
-
planner: plannerOutput,
|
|
41455
|
-
review: reviewOutput
|
|
41456
|
-
}
|
|
41052
|
+
rawOutput: response
|
|
41457
41053
|
};
|
|
41458
41054
|
}
|
|
41459
|
-
getProjectContext() {
|
|
41460
|
-
const kb = new KnowledgeBase(this.projectPath);
|
|
41461
|
-
return kb.getFullContext();
|
|
41462
|
-
}
|
|
41463
|
-
getCodebaseIndex() {
|
|
41464
|
-
const indexPath = getLocusPath(this.projectPath, "indexFile");
|
|
41465
|
-
if (!existsSync10(indexPath)) {
|
|
41466
|
-
return "";
|
|
41467
|
-
}
|
|
41468
|
-
try {
|
|
41469
|
-
const raw = readFileSync9(indexPath, "utf-8");
|
|
41470
|
-
const index = JSON.parse(raw);
|
|
41471
|
-
const parts = [];
|
|
41472
|
-
if (index.responsibilities) {
|
|
41473
|
-
parts.push("### File Responsibilities");
|
|
41474
|
-
const entries = Object.entries(index.responsibilities);
|
|
41475
|
-
for (const [file2, summary] of entries.slice(0, 50)) {
|
|
41476
|
-
parts.push(`- \`${file2}\`: ${summary}`);
|
|
41477
|
-
}
|
|
41478
|
-
if (entries.length > 50) {
|
|
41479
|
-
parts.push(`... and ${entries.length - 50} more files`);
|
|
41480
|
-
}
|
|
41481
|
-
}
|
|
41482
|
-
return parts.join(`
|
|
41483
|
-
`);
|
|
41484
|
-
} catch {
|
|
41485
|
-
return "";
|
|
41486
|
-
}
|
|
41487
|
-
}
|
|
41488
41055
|
}
|
|
41489
41056
|
var init_planning_meeting = __esm(() => {
|
|
41490
41057
|
init_config();
|
|
41491
|
-
init_knowledge_base();
|
|
41492
|
-
init_sprint_plan();
|
|
41493
41058
|
});
|
|
41494
41059
|
|
|
41495
41060
|
// ../sdk/src/planning/index.ts
|
|
@@ -41503,8 +41068,8 @@ var init_planning = __esm(() => {
|
|
|
41503
41068
|
var init_index_node = __esm(() => {
|
|
41504
41069
|
init_prompt_builder();
|
|
41505
41070
|
init_orchestrator();
|
|
41506
|
-
init_knowledge_base();
|
|
41507
41071
|
init_colors();
|
|
41072
|
+
init_structured_output();
|
|
41508
41073
|
init_agent2();
|
|
41509
41074
|
init_ai();
|
|
41510
41075
|
init_core3();
|
|
@@ -42323,7 +41888,6 @@ class InteractiveSession {
|
|
|
42323
41888
|
isProcessing = false;
|
|
42324
41889
|
conversationHistory = [];
|
|
42325
41890
|
historyManager;
|
|
42326
|
-
knowledgeBase;
|
|
42327
41891
|
currentSession;
|
|
42328
41892
|
projectPath;
|
|
42329
41893
|
model;
|
|
@@ -42339,7 +41903,6 @@ class InteractiveSession {
|
|
|
42339
41903
|
this.promptBuilder = new PromptBuilder(options.projectPath);
|
|
42340
41904
|
this.renderer = new ProgressRenderer({ animated: true });
|
|
42341
41905
|
this.historyManager = new HistoryManager(options.projectPath);
|
|
42342
|
-
this.knowledgeBase = new KnowledgeBase(options.projectPath);
|
|
42343
41906
|
this.projectPath = options.projectPath;
|
|
42344
41907
|
this.model = options.model;
|
|
42345
41908
|
this.provider = options.provider;
|
|
@@ -42483,18 +42046,6 @@ class InteractiveSession {
|
|
|
42483
42046
|
}
|
|
42484
42047
|
}
|
|
42485
42048
|
this.saveSession();
|
|
42486
|
-
try {
|
|
42487
|
-
this.knowledgeBase.updateProgress({
|
|
42488
|
-
role: "user",
|
|
42489
|
-
content: prompt
|
|
42490
|
-
});
|
|
42491
|
-
if (responseContent.trim()) {
|
|
42492
|
-
this.knowledgeBase.updateProgress({
|
|
42493
|
-
role: "assistant",
|
|
42494
|
-
content: responseContent.trim()
|
|
42495
|
-
});
|
|
42496
|
-
}
|
|
42497
|
-
} catch {}
|
|
42498
42049
|
} catch (error48) {
|
|
42499
42050
|
console.error(c.error(`Error: ${error48 instanceof Error ? error48.message : String(error48)}`));
|
|
42500
42051
|
} finally {
|
|
@@ -42570,10 +42121,10 @@ import { createInterface } from "node:readline";
|
|
|
42570
42121
|
|
|
42571
42122
|
// src/settings-manager.ts
|
|
42572
42123
|
init_index_node();
|
|
42573
|
-
import { existsSync as
|
|
42574
|
-
import { join as
|
|
42124
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
42125
|
+
import { join as join11 } from "node:path";
|
|
42575
42126
|
function getSettingsPath(projectPath) {
|
|
42576
|
-
return
|
|
42127
|
+
return join11(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
42577
42128
|
}
|
|
42578
42129
|
|
|
42579
42130
|
class SettingsManager {
|
|
@@ -42583,16 +42134,16 @@ class SettingsManager {
|
|
|
42583
42134
|
}
|
|
42584
42135
|
load() {
|
|
42585
42136
|
const settingsPath = getSettingsPath(this.projectPath);
|
|
42586
|
-
if (!
|
|
42137
|
+
if (!existsSync10(settingsPath)) {
|
|
42587
42138
|
return {};
|
|
42588
42139
|
}
|
|
42589
|
-
return JSON.parse(
|
|
42140
|
+
return JSON.parse(readFileSync9(settingsPath, "utf-8"));
|
|
42590
42141
|
}
|
|
42591
42142
|
save(settings) {
|
|
42592
42143
|
const { $schema: _2, ...rest } = settings;
|
|
42593
42144
|
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
42594
42145
|
const settingsPath = getSettingsPath(this.projectPath);
|
|
42595
|
-
|
|
42146
|
+
writeFileSync5(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
42596
42147
|
}
|
|
42597
42148
|
get(key) {
|
|
42598
42149
|
return this.load()[key];
|
|
@@ -42604,12 +42155,12 @@ class SettingsManager {
|
|
|
42604
42155
|
}
|
|
42605
42156
|
remove() {
|
|
42606
42157
|
const settingsPath = getSettingsPath(this.projectPath);
|
|
42607
|
-
if (
|
|
42158
|
+
if (existsSync10(settingsPath)) {
|
|
42608
42159
|
unlinkSync3(settingsPath);
|
|
42609
42160
|
}
|
|
42610
42161
|
}
|
|
42611
42162
|
exists() {
|
|
42612
|
-
return
|
|
42163
|
+
return existsSync10(getSettingsPath(this.projectPath));
|
|
42613
42164
|
}
|
|
42614
42165
|
}
|
|
42615
42166
|
|
|
@@ -42875,79 +42426,105 @@ import { parseArgs } from "node:util";
|
|
|
42875
42426
|
// src/config-manager.ts
|
|
42876
42427
|
init_index_node();
|
|
42877
42428
|
import { execSync as execSync2 } from "node:child_process";
|
|
42878
|
-
import { existsSync as
|
|
42879
|
-
import { join as
|
|
42429
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
42430
|
+
import { join as join12 } from "node:path";
|
|
42880
42431
|
var LOCUS_GITIGNORE_MARKER = "# Locus AI";
|
|
42881
|
-
var
|
|
42882
|
-
|
|
42883
|
-
## Mission
|
|
42884
|
-
<!-- Describe your project's core purpose and value proposition -->
|
|
42432
|
+
var LOCUS_MD_TEMPLATE = `## Planning First
|
|
42885
42433
|
|
|
42886
|
-
|
|
42887
|
-
|
|
42434
|
+
Complex tasks must be planned before writing code. Create ".locus/plans/<task-name>.md" with:
|
|
42435
|
+
- **Goal**: What we're trying to achieve and why
|
|
42436
|
+
- **Approach**: Step-by-step strategy with technical decisions
|
|
42437
|
+
- **Affected files**: List of files to create/modify/delete
|
|
42438
|
+
- **Acceptance criteria**: Specific, testable conditions for completion
|
|
42439
|
+
- **Dependencies**: Required packages, APIs, or external services
|
|
42888
42440
|
|
|
42889
|
-
|
|
42890
|
-
<!-- Describe your high-level architecture -->
|
|
42441
|
+
Delete the planning .md files after successful execution.
|
|
42891
42442
|
|
|
42892
|
-
##
|
|
42893
|
-
<!-- Document important technical decisions and their rationale -->
|
|
42443
|
+
## Code Quality
|
|
42894
42444
|
|
|
42895
|
-
|
|
42896
|
-
|
|
42897
|
-
|
|
42898
|
-
|
|
42445
|
+
- **Follow existing patterns**: Run formatters and linters before finishing (check "package.json" scripts or project config)
|
|
42446
|
+
- **Minimize changes**: Keep modifications atomic. Separate refactors from behavioral changes into different tasks
|
|
42447
|
+
- **Never commit secrets**: No API keys, passwords, or credentials in code. Use environment variables or secret management
|
|
42448
|
+
- **Test as you go**: If tests exist, run relevant ones. If breaking changes occur, update tests accordingly
|
|
42449
|
+
- **Comment complex logic**: Explain *why*, not *what*. Focus on business logic and non-obvious decisions
|
|
42899
42450
|
|
|
42900
|
-
|
|
42901
|
-
`;
|
|
42902
|
-
var LOCUS_MD_TEMPLATE = `## Artifacts
|
|
42451
|
+
## Artifacts
|
|
42903
42452
|
|
|
42904
|
-
When a task produces knowledge, analysis, or research output rather than (or in addition to) code changes, you **must** save
|
|
42453
|
+
When a task produces knowledge, analysis, or research output rather than (or in addition to) code changes, you **must** save results as Markdown in ".locus/artifacts/<descriptive-name>.md":
|
|
42905
42454
|
|
|
42455
|
+
**Always create artifacts for:**
|
|
42906
42456
|
- Code quality audits, security reviews, vulnerability assessments
|
|
42907
|
-
- Architecture analyses or recommendations
|
|
42908
|
-
- Dependency reports, performance profiling
|
|
42909
|
-
-
|
|
42457
|
+
- Architecture analyses, system design proposals, or recommendations
|
|
42458
|
+
- Dependency reports, performance profiling, benchmarking results
|
|
42459
|
+
- Research summaries, technology comparisons, or feasibility studies
|
|
42460
|
+
- Migration plans, deployment strategies, or rollback procedures
|
|
42461
|
+
- Post-mortems, incident analysis, or debugging investigations
|
|
42462
|
+
|
|
42463
|
+
**Artifact structure:**
|
|
42464
|
+
- Clear title and date
|
|
42465
|
+
- Executive summary (2-3 sentences)
|
|
42466
|
+
- Detailed findings/analysis
|
|
42467
|
+
- Actionable recommendations (if applicable)
|
|
42468
|
+
|
|
42469
|
+
## Git Operations
|
|
42470
|
+
|
|
42471
|
+
- **Do NOT run**: git add, git commit, git push, git checkout, git branch, or any git commands
|
|
42472
|
+
- **Why**: The Locus orchestrator handles all version control automatically after execution
|
|
42473
|
+
- **Your role**: Focus solely on making file changes. The system commits, pushes, and creates PRs
|
|
42910
42474
|
|
|
42911
|
-
|
|
42912
|
-
- File path: \`.locus/artifacts/<descriptive-name>.md\` (e.g., \`code-quality-audit.md\`, \`dependency-report.md\`).
|
|
42913
|
-
- The artifact should be a well-structured Markdown document with clear headings, findings, and actionable recommendations where applicable.
|
|
42914
|
-
- Always create the artifact file — do not only return the results as conversation text.
|
|
42915
|
-
- If the task also involves code changes, still produce the artifact alongside the code changes.
|
|
42475
|
+
## Continuous Learning
|
|
42916
42476
|
|
|
42917
|
-
|
|
42477
|
+
Read ".locus/LEARNINGS.md" **before starting any task** to avoid repeating mistakes.
|
|
42918
42478
|
|
|
42919
|
-
|
|
42479
|
+
**When to update:**
|
|
42480
|
+
- User corrects your approach or provides guidance
|
|
42481
|
+
- You discover a better pattern while working
|
|
42482
|
+
- A decision prevents future confusion (e.g., "use X not Y because Z")
|
|
42483
|
+
- You encounter and solve a tricky problem
|
|
42920
42484
|
|
|
42921
|
-
|
|
42485
|
+
**What to record:**
|
|
42486
|
+
- Architectural decisions and their rationale
|
|
42487
|
+
- Preferred libraries, tools, or patterns for this project
|
|
42488
|
+
- Common pitfalls and how to avoid them
|
|
42489
|
+
- Project-specific conventions or user preferences
|
|
42490
|
+
- Solutions to non-obvious problems
|
|
42922
42491
|
|
|
42923
|
-
-
|
|
42924
|
-
- Keep changes minimal and atomic. Separate refactors from behavioral changes.
|
|
42925
|
-
- No new dependencies without explicit approval.
|
|
42926
|
-
- Never put raw secrets or credentials in the codebase.
|
|
42492
|
+
**Format (append-only, never delete):**
|
|
42927
42493
|
|
|
42928
|
-
|
|
42494
|
+
"""
|
|
42495
|
+
- **[Category]**: Concise description (1-2 lines max). *Context if needed.*
|
|
42496
|
+
"""
|
|
42929
42497
|
|
|
42930
|
-
|
|
42931
|
-
|
|
42932
|
-
|
|
42933
|
-
|
|
42498
|
+
**Categories:** Architecture, Dependencies, Patterns, Debugging, Performance, Security, DevOps, User Preferences
|
|
42499
|
+
|
|
42500
|
+
## Error Handling
|
|
42501
|
+
|
|
42502
|
+
- **Read error messages carefully**: Don't guess. Parse the actual error before proposing fixes
|
|
42503
|
+
- **Check dependencies first**: Missing packages, wrong versions, and environment issues are common
|
|
42504
|
+
- **Verify assumptions**: If something "should work," confirm it actually does in this environment
|
|
42505
|
+
- **Ask for context**: If you need environment details, configuration, or logs, request them explicitly
|
|
42506
|
+
|
|
42507
|
+
## Communication
|
|
42508
|
+
|
|
42509
|
+
- **Be precise**: When uncertain, state what you know and what you're assuming
|
|
42510
|
+
- **Show your work**: For complex changes, briefly explain the approach before executing
|
|
42511
|
+
- **Highlight trade-offs**: If multiple approaches exist, note why you chose one over others
|
|
42512
|
+
- **Request feedback**: For ambiguous requirements, propose an approach and ask for confirmation
|
|
42513
|
+
`;
|
|
42514
|
+
var DEFAULT_LEARNINGS_MD = `# Learnings
|
|
42934
42515
|
|
|
42935
|
-
|
|
42516
|
+
This file captures important lessons, decisions, and corrections made during development.
|
|
42517
|
+
It is read by AI agents before every task to avoid repeating mistakes and to follow established patterns.
|
|
42936
42518
|
|
|
42937
|
-
|
|
42938
|
-
- Never invent APIs, libraries, functions, or config options.** Only use APIs and methods you can verify exist in the project's dependencies or documentation. If unsure whether something exists, ask or look it up first.
|
|
42939
|
-
- No placeholder or stub logic unless explicitly requested. Every piece of code you write should be functional and intentional. Do not leave TODO blocks, fake return values, or mock implementations without flagging them clearly.
|
|
42940
|
-
- Do not generate boilerplate "just in case." Only write code that is directly required by the task. No speculative utilities, unused helpers, or premature abstractions.
|
|
42941
|
-
- If you're uncertain, say so. State your confidence level. "I believe this is correct but haven't verified X" is always better than silent guessing.
|
|
42942
|
-
- Read before writing Before modifying a file, read the relevant existing code to match conventions, understand context, and avoid duplicating logic that already exists.
|
|
42519
|
+
<!-- Add learnings below this line. Format: - **[Category]**: Description -->
|
|
42943
42520
|
`;
|
|
42944
42521
|
function updateGitignore(projectPath) {
|
|
42945
|
-
const gitignorePath =
|
|
42522
|
+
const gitignorePath = join12(projectPath, ".gitignore");
|
|
42946
42523
|
let content = "";
|
|
42947
42524
|
const locusBlock = LOCUS_GITIGNORE_PATTERNS.join(`
|
|
42948
42525
|
`);
|
|
42949
|
-
if (
|
|
42950
|
-
content =
|
|
42526
|
+
if (existsSync11(gitignorePath)) {
|
|
42527
|
+
content = readFileSync10(gitignorePath, "utf-8");
|
|
42951
42528
|
if (content.includes(LOCUS_GITIGNORE_MARKER)) {
|
|
42952
42529
|
const lines = content.split(`
|
|
42953
42530
|
`);
|
|
@@ -42964,7 +42541,7 @@ function updateGitignore(projectPath) {
|
|
|
42964
42541
|
const after = lines.slice(endIdx + 1);
|
|
42965
42542
|
content = [...before, locusBlock, ...after].join(`
|
|
42966
42543
|
`);
|
|
42967
|
-
|
|
42544
|
+
writeFileSync6(gitignorePath, content);
|
|
42968
42545
|
return;
|
|
42969
42546
|
}
|
|
42970
42547
|
if (content.length > 0 && !content.endsWith(`
|
|
@@ -42979,7 +42556,7 @@ function updateGitignore(projectPath) {
|
|
|
42979
42556
|
}
|
|
42980
42557
|
content += `${locusBlock}
|
|
42981
42558
|
`;
|
|
42982
|
-
|
|
42559
|
+
writeFileSync6(gitignorePath, content);
|
|
42983
42560
|
}
|
|
42984
42561
|
function ensureGitIdentity(projectPath) {
|
|
42985
42562
|
const hasName = (() => {
|
|
@@ -43026,9 +42603,9 @@ class ConfigManager {
|
|
|
43026
42603
|
this.projectPath = projectPath;
|
|
43027
42604
|
}
|
|
43028
42605
|
async init(version2) {
|
|
43029
|
-
const locusConfigDir =
|
|
42606
|
+
const locusConfigDir = join12(this.projectPath, LOCUS_CONFIG.dir);
|
|
43030
42607
|
const locusConfigPath = getLocusPath(this.projectPath, "configFile");
|
|
43031
|
-
if (!
|
|
42608
|
+
if (!existsSync11(locusConfigDir)) {
|
|
43032
42609
|
mkdirSync6(locusConfigDir, { recursive: true });
|
|
43033
42610
|
}
|
|
43034
42611
|
const locusSubdirs = [
|
|
@@ -43036,43 +42613,38 @@ class ConfigManager {
|
|
|
43036
42613
|
LOCUS_CONFIG.documentsDir,
|
|
43037
42614
|
LOCUS_CONFIG.sessionsDir,
|
|
43038
42615
|
LOCUS_CONFIG.reviewsDir,
|
|
43039
|
-
LOCUS_CONFIG.plansDir
|
|
43040
|
-
LOCUS_CONFIG.projectDir
|
|
42616
|
+
LOCUS_CONFIG.plansDir
|
|
43041
42617
|
];
|
|
43042
42618
|
for (const subdir of locusSubdirs) {
|
|
43043
|
-
const subdirPath =
|
|
43044
|
-
if (!
|
|
42619
|
+
const subdirPath = join12(locusConfigDir, subdir);
|
|
42620
|
+
if (!existsSync11(subdirPath)) {
|
|
43045
42621
|
mkdirSync6(subdirPath, { recursive: true });
|
|
43046
42622
|
}
|
|
43047
42623
|
}
|
|
43048
|
-
const contextFilePath = getLocusPath(this.projectPath, "projectContextFile");
|
|
43049
|
-
if (!existsSync12(contextFilePath)) {
|
|
43050
|
-
writeFileSync7(contextFilePath, DEFAULT_CONTEXT_MD);
|
|
43051
|
-
}
|
|
43052
|
-
const progressFilePath = getLocusPath(this.projectPath, "projectProgressFile");
|
|
43053
|
-
if (!existsSync12(progressFilePath)) {
|
|
43054
|
-
writeFileSync7(progressFilePath, DEFAULT_PROGRESS_MD);
|
|
43055
|
-
}
|
|
43056
42624
|
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
43057
|
-
if (!
|
|
43058
|
-
|
|
42625
|
+
if (!existsSync11(locusMdPath)) {
|
|
42626
|
+
writeFileSync6(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
42627
|
+
}
|
|
42628
|
+
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
42629
|
+
if (!existsSync11(learningsMdPath)) {
|
|
42630
|
+
writeFileSync6(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
43059
42631
|
}
|
|
43060
|
-
if (!
|
|
42632
|
+
if (!existsSync11(locusConfigPath)) {
|
|
43061
42633
|
const config2 = {
|
|
43062
42634
|
$schema: LOCUS_SCHEMAS.config,
|
|
43063
42635
|
version: version2,
|
|
43064
42636
|
createdAt: new Date().toISOString(),
|
|
43065
42637
|
projectPath: "."
|
|
43066
42638
|
};
|
|
43067
|
-
|
|
42639
|
+
writeFileSync6(locusConfigPath, JSON.stringify(config2, null, 2));
|
|
43068
42640
|
}
|
|
43069
42641
|
updateGitignore(this.projectPath);
|
|
43070
42642
|
ensureGitIdentity(this.projectPath);
|
|
43071
42643
|
}
|
|
43072
42644
|
loadConfig() {
|
|
43073
42645
|
const path2 = getLocusPath(this.projectPath, "configFile");
|
|
43074
|
-
if (
|
|
43075
|
-
return JSON.parse(
|
|
42646
|
+
if (existsSync11(path2)) {
|
|
42647
|
+
return JSON.parse(readFileSync10(path2, "utf-8"));
|
|
43076
42648
|
}
|
|
43077
42649
|
return null;
|
|
43078
42650
|
}
|
|
@@ -43090,8 +42662,6 @@ class ConfigManager {
|
|
|
43090
42662
|
directoriesCreated: [],
|
|
43091
42663
|
gitignoreUpdated: false
|
|
43092
42664
|
};
|
|
43093
|
-
const locusConfigDir = join11(this.projectPath, LOCUS_CONFIG.dir);
|
|
43094
|
-
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
43095
42665
|
const config2 = this.loadConfig();
|
|
43096
42666
|
if (config2) {
|
|
43097
42667
|
result.previousVersion = config2.version;
|
|
@@ -43104,18 +42674,19 @@ class ConfigManager {
|
|
|
43104
42674
|
this.saveConfig(config2);
|
|
43105
42675
|
}
|
|
43106
42676
|
}
|
|
43107
|
-
const settingsPath =
|
|
43108
|
-
if (
|
|
43109
|
-
const raw =
|
|
42677
|
+
const settingsPath = join12(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
42678
|
+
if (existsSync11(settingsPath)) {
|
|
42679
|
+
const raw = readFileSync10(settingsPath, "utf-8");
|
|
43110
42680
|
const settings = JSON.parse(raw);
|
|
43111
42681
|
if (settings.$schema !== LOCUS_SCHEMAS.settings) {
|
|
43112
42682
|
const { $schema: _2, ...rest } = settings;
|
|
43113
42683
|
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
43114
|
-
|
|
42684
|
+
writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
43115
42685
|
}
|
|
43116
42686
|
}
|
|
43117
|
-
const
|
|
43118
|
-
|
|
42687
|
+
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
42688
|
+
const locusMdExisted = existsSync11(locusMdPath);
|
|
42689
|
+
writeFileSync6(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
43119
42690
|
if (!locusMdExisted) {
|
|
43120
42691
|
result.directoriesCreated.push(".locus/LOCUS.md");
|
|
43121
42692
|
}
|
|
@@ -43124,30 +42695,25 @@ class ConfigManager {
|
|
|
43124
42695
|
LOCUS_CONFIG.documentsDir,
|
|
43125
42696
|
LOCUS_CONFIG.sessionsDir,
|
|
43126
42697
|
LOCUS_CONFIG.reviewsDir,
|
|
43127
|
-
LOCUS_CONFIG.plansDir
|
|
43128
|
-
LOCUS_CONFIG.projectDir
|
|
42698
|
+
LOCUS_CONFIG.plansDir
|
|
43129
42699
|
];
|
|
42700
|
+
const locusConfigDir = join12(this.projectPath, LOCUS_CONFIG.dir);
|
|
43130
42701
|
for (const subdir of locusSubdirs) {
|
|
43131
|
-
const subdirPath =
|
|
43132
|
-
if (!
|
|
42702
|
+
const subdirPath = join12(locusConfigDir, subdir);
|
|
42703
|
+
if (!existsSync11(subdirPath)) {
|
|
43133
42704
|
mkdirSync6(subdirPath, { recursive: true });
|
|
43134
42705
|
result.directoriesCreated.push(`.locus/${subdir}`);
|
|
43135
42706
|
}
|
|
43136
42707
|
}
|
|
43137
|
-
const
|
|
43138
|
-
if (!
|
|
43139
|
-
|
|
43140
|
-
result.directoriesCreated.push(".locus/
|
|
43141
|
-
}
|
|
43142
|
-
const progressFilePath = getLocusPath(this.projectPath, "projectProgressFile");
|
|
43143
|
-
if (!existsSync12(progressFilePath)) {
|
|
43144
|
-
writeFileSync7(progressFilePath, DEFAULT_PROGRESS_MD);
|
|
43145
|
-
result.directoriesCreated.push(".locus/project/progress.md");
|
|
42708
|
+
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
42709
|
+
if (!existsSync11(learningsMdPath)) {
|
|
42710
|
+
writeFileSync6(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
42711
|
+
result.directoriesCreated.push(".locus/LEARNINGS.md");
|
|
43146
42712
|
}
|
|
43147
|
-
const gitignorePath =
|
|
43148
|
-
const gitignoreBefore =
|
|
42713
|
+
const gitignorePath = join12(this.projectPath, ".gitignore");
|
|
42714
|
+
const gitignoreBefore = existsSync11(gitignorePath) ? readFileSync10(gitignorePath, "utf-8") : "";
|
|
43149
42715
|
updateGitignore(this.projectPath);
|
|
43150
|
-
const gitignoreAfter =
|
|
42716
|
+
const gitignoreAfter = readFileSync10(gitignorePath, "utf-8");
|
|
43151
42717
|
if (gitignoreBefore !== gitignoreAfter) {
|
|
43152
42718
|
result.gitignoreUpdated = true;
|
|
43153
42719
|
}
|
|
@@ -43158,7 +42724,7 @@ class ConfigManager {
|
|
|
43158
42724
|
const { $schema: _2, ...rest } = config2;
|
|
43159
42725
|
const ordered = { $schema: LOCUS_SCHEMAS.config, ...rest };
|
|
43160
42726
|
const path2 = getLocusPath(this.projectPath, "configFile");
|
|
43161
|
-
|
|
42727
|
+
writeFileSync6(path2, JSON.stringify(ordered, null, 2));
|
|
43162
42728
|
}
|
|
43163
42729
|
}
|
|
43164
42730
|
|
|
@@ -43166,23 +42732,23 @@ class ConfigManager {
|
|
|
43166
42732
|
init_index_node();
|
|
43167
42733
|
|
|
43168
42734
|
// src/utils/version.ts
|
|
43169
|
-
import { existsSync as
|
|
43170
|
-
import { dirname as
|
|
42735
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
42736
|
+
import { dirname as dirname3, join as join13 } from "node:path";
|
|
43171
42737
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
43172
42738
|
function getVersion() {
|
|
43173
42739
|
try {
|
|
43174
42740
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
43175
|
-
const __dirname2 =
|
|
43176
|
-
const bundledPath =
|
|
43177
|
-
const sourcePath =
|
|
43178
|
-
if (
|
|
43179
|
-
const pkg = JSON.parse(
|
|
42741
|
+
const __dirname2 = dirname3(__filename2);
|
|
42742
|
+
const bundledPath = join13(__dirname2, "..", "package.json");
|
|
42743
|
+
const sourcePath = join13(__dirname2, "..", "..", "package.json");
|
|
42744
|
+
if (existsSync12(bundledPath)) {
|
|
42745
|
+
const pkg = JSON.parse(readFileSync11(bundledPath, "utf-8"));
|
|
43180
42746
|
if (pkg.name === "@locusai/cli") {
|
|
43181
42747
|
return pkg.version || "0.0.0";
|
|
43182
42748
|
}
|
|
43183
42749
|
}
|
|
43184
|
-
if (
|
|
43185
|
-
const pkg = JSON.parse(
|
|
42750
|
+
if (existsSync12(sourcePath)) {
|
|
42751
|
+
const pkg = JSON.parse(readFileSync11(sourcePath, "utf-8"));
|
|
43186
42752
|
if (pkg.name === "@locusai/cli") {
|
|
43187
42753
|
return pkg.version || "0.0.0";
|
|
43188
42754
|
}
|
|
@@ -43204,12 +42770,12 @@ function printBanner() {
|
|
|
43204
42770
|
}
|
|
43205
42771
|
// src/utils/helpers.ts
|
|
43206
42772
|
init_index_node();
|
|
43207
|
-
import { existsSync as
|
|
43208
|
-
import { join as
|
|
42773
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
42774
|
+
import { join as join14 } from "node:path";
|
|
43209
42775
|
function isProjectInitialized(projectPath) {
|
|
43210
|
-
const locusDir =
|
|
43211
|
-
const configPath =
|
|
43212
|
-
return
|
|
42776
|
+
const locusDir = join14(projectPath, LOCUS_CONFIG.dir);
|
|
42777
|
+
const configPath = join14(locusDir, LOCUS_CONFIG.configFile);
|
|
42778
|
+
return existsSync13(locusDir) && existsSync13(configPath);
|
|
43213
42779
|
}
|
|
43214
42780
|
function requireInitialization(projectPath, command) {
|
|
43215
42781
|
if (!isProjectInitialized(projectPath)) {
|
|
@@ -43762,7 +43328,6 @@ async function execCommand(args) {
|
|
|
43762
43328
|
});
|
|
43763
43329
|
const builder = new PromptBuilder(projectPath);
|
|
43764
43330
|
const fullPrompt = await builder.buildGenericPrompt(promptInput);
|
|
43765
|
-
const knowledgeBase = new KnowledgeBase(projectPath);
|
|
43766
43331
|
console.log("");
|
|
43767
43332
|
console.log(`${c.primary("\uD83D\uDE80")} ${c.bold("Executing prompt with repository context...")}`);
|
|
43768
43333
|
console.log("");
|
|
@@ -43817,15 +43382,6 @@ async function execCommand(args) {
|
|
|
43817
43382
|
responseContent = await aiRunner.run(fullPrompt);
|
|
43818
43383
|
console.log(responseContent);
|
|
43819
43384
|
}
|
|
43820
|
-
try {
|
|
43821
|
-
knowledgeBase.updateProgress({ role: "user", content: promptInput });
|
|
43822
|
-
if (responseContent.trim()) {
|
|
43823
|
-
knowledgeBase.updateProgress({
|
|
43824
|
-
role: "assistant",
|
|
43825
|
-
content: responseContent.trim()
|
|
43826
|
-
});
|
|
43827
|
-
}
|
|
43828
|
-
} catch {}
|
|
43829
43385
|
console.log(`
|
|
43830
43386
|
${c.success("✔")} ${c.success("Execution finished!")}
|
|
43831
43387
|
`);
|
|
@@ -43881,13 +43437,6 @@ async function execJsonStream(values, positionals, projectPath) {
|
|
|
43881
43437
|
for await (const chunk of stream4) {
|
|
43882
43438
|
renderer.handleChunk(chunk);
|
|
43883
43439
|
}
|
|
43884
|
-
try {
|
|
43885
|
-
const knowledgeBase = new KnowledgeBase(projectPath);
|
|
43886
|
-
knowledgeBase.updateProgress({
|
|
43887
|
-
role: "user",
|
|
43888
|
-
content: promptInput
|
|
43889
|
-
});
|
|
43890
|
-
} catch {}
|
|
43891
43440
|
renderer.emitDone(0);
|
|
43892
43441
|
} catch (error48) {
|
|
43893
43442
|
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
@@ -44070,9 +43619,8 @@ async function initCommand() {
|
|
|
44070
43619
|
${c.bold("Created:")}
|
|
44071
43620
|
${c.primary("\uD83D\uDCC1")} ${c.bold(".locus/")} ${c.dim("Configuration directory")}
|
|
44072
43621
|
${c.primary("\uD83D\uDCC4")} ${c.bold(".locus/config.json")} ${c.dim("Project settings")}
|
|
44073
|
-
${c.primary("\uD83D\uDCC4")} ${c.bold(".locus/project/context.md")} ${c.dim("Project context & knowledge")}
|
|
44074
|
-
${c.primary("\uD83D\uDCC4")} ${c.bold(".locus/project/progress.md")} ${c.dim("Sprint progress tracking")}
|
|
44075
43622
|
${c.primary("\uD83D\uDCDD")} ${c.bold(".locus/LOCUS.md")} ${c.dim("AI agent instructions")}
|
|
43623
|
+
${c.primary("\uD83D\uDCDD")} ${c.bold(".locus/LEARNINGS.md")} ${c.dim("Continuous learning log")}
|
|
44076
43624
|
|
|
44077
43625
|
${c.bold("Next steps:")}
|
|
44078
43626
|
1. Run '${c.primary("locus config setup")}' to configure your API key
|
|
@@ -44084,6 +43632,8 @@ async function initCommand() {
|
|
|
44084
43632
|
}
|
|
44085
43633
|
// src/commands/plan.ts
|
|
44086
43634
|
init_index_node();
|
|
43635
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync4 } from "node:fs";
|
|
43636
|
+
import { join as join15 } from "node:path";
|
|
44087
43637
|
import { parseArgs as parseArgs4 } from "node:util";
|
|
44088
43638
|
function normalizePlanIdArgs(args) {
|
|
44089
43639
|
const planIdFlags = new Set(["--approve", "--reject", "--cancel", "--show"]);
|
|
@@ -44190,6 +43740,10 @@ async function planCommand(args) {
|
|
|
44190
43740
|
try {
|
|
44191
43741
|
const result = await meeting.run(directive, feedback);
|
|
44192
43742
|
planManager.save(result.plan);
|
|
43743
|
+
const tempFile = join15(getLocusPath(projectPath, "plansDir"), `${result.plan.id}.json`);
|
|
43744
|
+
if (existsSync14(tempFile)) {
|
|
43745
|
+
unlinkSync4(tempFile);
|
|
43746
|
+
}
|
|
44193
43747
|
console.log(`
|
|
44194
43748
|
${c.success("✔")} ${c.success("Planning meeting complete!")}
|
|
44195
43749
|
`);
|
|
@@ -44384,8 +43938,8 @@ function showPlanHelp() {
|
|
|
44384
43938
|
}
|
|
44385
43939
|
// src/commands/review.ts
|
|
44386
43940
|
init_index_node();
|
|
44387
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync7, writeFileSync as
|
|
44388
|
-
import { join as
|
|
43941
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
43942
|
+
import { join as join16 } from "node:path";
|
|
44389
43943
|
import { parseArgs as parseArgs5 } from "node:util";
|
|
44390
43944
|
async function reviewCommand(args) {
|
|
44391
43945
|
const subcommand = args[0];
|
|
@@ -44524,13 +44078,13 @@ async function reviewLocalCommand(args) {
|
|
|
44524
44078
|
`);
|
|
44525
44079
|
return;
|
|
44526
44080
|
}
|
|
44527
|
-
const reviewsDir =
|
|
44081
|
+
const reviewsDir = join16(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.reviewsDir);
|
|
44528
44082
|
if (!existsSync15(reviewsDir)) {
|
|
44529
44083
|
mkdirSync7(reviewsDir, { recursive: true });
|
|
44530
44084
|
}
|
|
44531
44085
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
44532
|
-
const reportPath =
|
|
44533
|
-
|
|
44086
|
+
const reportPath = join16(reviewsDir, `review-${timestamp}.md`);
|
|
44087
|
+
writeFileSync7(reportPath, report, "utf-8");
|
|
44534
44088
|
console.log(`
|
|
44535
44089
|
${c.success("✔")} ${c.success("Review complete!")}`);
|
|
44536
44090
|
console.log(` ${c.dim("Report saved to:")} ${c.primary(reportPath)}
|
|
@@ -44623,7 +44177,7 @@ ${c.info(`Received ${signal}. Stopping agent and cleaning up...`)}`);
|
|
|
44623
44177
|
init_index_node();
|
|
44624
44178
|
import { spawn as spawn4 } from "node:child_process";
|
|
44625
44179
|
import { existsSync as existsSync16 } from "node:fs";
|
|
44626
|
-
import { join as
|
|
44180
|
+
import { join as join17 } from "node:path";
|
|
44627
44181
|
import { createInterface as createInterface3 } from "node:readline";
|
|
44628
44182
|
function ask2(question) {
|
|
44629
44183
|
const rl = createInterface3({
|
|
@@ -44857,7 +44411,7 @@ function runBotCommand(projectPath) {
|
|
|
44857
44411
|
`);
|
|
44858
44412
|
process.exit(1);
|
|
44859
44413
|
}
|
|
44860
|
-
const monorepoTelegramEntry =
|
|
44414
|
+
const monorepoTelegramEntry = join17(projectPath, "packages/telegram/src/index.ts");
|
|
44861
44415
|
const isMonorepo = existsSync16(monorepoTelegramEntry);
|
|
44862
44416
|
let cmd;
|
|
44863
44417
|
let args;
|