@locusai/cli 0.12.0 → 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 +130 -323
- package/bin/locus.js +433 -827
- 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,102 +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 parts = [];
|
|
38785
|
-
if (context2.trim()) {
|
|
38786
|
-
parts.push(context2.trim());
|
|
38787
|
-
}
|
|
38788
|
-
return parts.join(`
|
|
38789
|
-
|
|
38790
|
-
---
|
|
38791
|
-
|
|
38792
|
-
`);
|
|
38793
|
-
}
|
|
38794
|
-
initialize(info) {
|
|
38795
|
-
this.ensureDir(this.contextPath);
|
|
38796
|
-
this.ensureDir(this.progressPath);
|
|
38797
|
-
const techStackList = info.techStack.map((t) => `- ${t}`).join(`
|
|
38798
|
-
`);
|
|
38799
|
-
const contextContent = `# Project: ${info.name}
|
|
38800
|
-
|
|
38801
|
-
## Mission
|
|
38802
|
-
${info.mission}
|
|
38803
|
-
|
|
38804
|
-
## Tech Stack
|
|
38805
|
-
${techStackList}
|
|
38806
|
-
|
|
38807
|
-
## Architecture
|
|
38808
|
-
<!-- Describe your high-level architecture here -->
|
|
38809
|
-
|
|
38810
|
-
## Key Decisions
|
|
38811
|
-
<!-- Document important technical decisions and their rationale -->
|
|
38812
|
-
|
|
38813
|
-
## Feature Areas
|
|
38814
|
-
<!-- List your main feature areas and their status -->
|
|
38815
|
-
`;
|
|
38816
|
-
const progressContent = `# Conversation History
|
|
38817
|
-
`;
|
|
38818
|
-
writeFileSync3(this.contextPath, contextContent);
|
|
38819
|
-
writeFileSync3(this.progressPath, progressContent);
|
|
38820
|
-
}
|
|
38821
|
-
get exists() {
|
|
38822
|
-
return existsSync5(this.contextPath) || existsSync5(this.progressPath);
|
|
38823
|
-
}
|
|
38824
|
-
ensureDir(filePath) {
|
|
38825
|
-
const dir = dirname2(filePath);
|
|
38826
|
-
if (!existsSync5(dir)) {
|
|
38827
|
-
mkdirSync3(dir, { recursive: true });
|
|
38828
|
-
}
|
|
38829
|
-
}
|
|
38830
|
-
}
|
|
38831
|
-
var init_knowledge_base = __esm(() => {
|
|
38832
|
-
init_config();
|
|
38833
|
-
});
|
|
38834
|
-
|
|
38835
38726
|
// ../sdk/src/agent/reviewer-worker.ts
|
|
38836
38727
|
function resolveProvider(value) {
|
|
38837
38728
|
if (!value || value.startsWith("--"))
|
|
@@ -38846,7 +38737,6 @@ class ReviewerWorker {
|
|
|
38846
38737
|
client;
|
|
38847
38738
|
aiRunner;
|
|
38848
38739
|
prService;
|
|
38849
|
-
knowledgeBase;
|
|
38850
38740
|
heartbeatInterval = null;
|
|
38851
38741
|
currentTaskId = null;
|
|
38852
38742
|
maxReviews = 50;
|
|
@@ -38872,7 +38762,6 @@ class ReviewerWorker {
|
|
|
38872
38762
|
log
|
|
38873
38763
|
});
|
|
38874
38764
|
this.prService = new PrService(projectPath, log);
|
|
38875
|
-
this.knowledgeBase = new KnowledgeBase(projectPath);
|
|
38876
38765
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
38877
38766
|
this.log(`Reviewer agent using ${providerLabel} CLI`, "info");
|
|
38878
38767
|
}
|
|
@@ -38988,17 +38877,6 @@ ${summary}`;
|
|
|
38988
38877
|
this.sendHeartbeat();
|
|
38989
38878
|
const result = await this.reviewPr(pr);
|
|
38990
38879
|
if (result.reviewed) {
|
|
38991
|
-
const status = result.approved ? "APPROVED" : "CHANGES REQUESTED";
|
|
38992
|
-
try {
|
|
38993
|
-
this.knowledgeBase.updateProgress({
|
|
38994
|
-
role: "user",
|
|
38995
|
-
content: `Review PR #${pr.number}: ${pr.title}`
|
|
38996
|
-
});
|
|
38997
|
-
this.knowledgeBase.updateProgress({
|
|
38998
|
-
role: "assistant",
|
|
38999
|
-
content: `${status}: ${result.summary}`
|
|
39000
|
-
});
|
|
39001
|
-
} catch {}
|
|
39002
38880
|
this.reviewsCompleted++;
|
|
39003
38881
|
} else {
|
|
39004
38882
|
this.log(`Review skipped: ${result.summary}`, "warn");
|
|
@@ -39017,7 +38895,6 @@ var init_reviewer_worker = __esm(() => {
|
|
|
39017
38895
|
init_git_utils();
|
|
39018
38896
|
init_pr_service();
|
|
39019
38897
|
init_src2();
|
|
39020
|
-
init_knowledge_base();
|
|
39021
38898
|
init_colors();
|
|
39022
38899
|
reviewerEntrypoint = process.argv[1]?.split(/[\\/]/).pop();
|
|
39023
38900
|
if (reviewerEntrypoint === "reviewer-worker.js" || reviewerEntrypoint === "reviewer-worker.ts") {
|
|
@@ -39060,7 +38937,7 @@ var init_reviewer_worker = __esm(() => {
|
|
|
39060
38937
|
});
|
|
39061
38938
|
|
|
39062
38939
|
// ../sdk/src/core/prompt-builder.ts
|
|
39063
|
-
import { existsSync as
|
|
38940
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
39064
38941
|
import { join as join6 } from "node:path";
|
|
39065
38942
|
|
|
39066
38943
|
class PromptBuilder {
|
|
@@ -39068,223 +38945,157 @@ class PromptBuilder {
|
|
|
39068
38945
|
constructor(projectPath) {
|
|
39069
38946
|
this.projectPath = projectPath;
|
|
39070
38947
|
}
|
|
39071
|
-
async build(task2
|
|
39072
|
-
let prompt = `# Task: ${task2.title}
|
|
39073
|
-
|
|
39074
|
-
`;
|
|
38948
|
+
async build(task2) {
|
|
39075
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 = "";
|
|
39076
38955
|
if (roleText) {
|
|
39077
|
-
|
|
38956
|
+
sections += `
|
|
38957
|
+
<role>
|
|
39078
38958
|
You are acting as a ${roleText}.
|
|
39079
|
-
|
|
39080
|
-
`;
|
|
39081
|
-
}
|
|
39082
|
-
prompt += `## Description
|
|
39083
|
-
${task2.description || "No description provided."}
|
|
39084
|
-
|
|
39085
|
-
`;
|
|
39086
|
-
const projectConfig = this.getProjectConfig();
|
|
39087
|
-
if (projectConfig) {
|
|
39088
|
-
prompt += `## Project Metadata
|
|
39089
|
-
`;
|
|
39090
|
-
prompt += `- Version: ${projectConfig.version || "Unknown"}
|
|
38959
|
+
</role>
|
|
39091
38960
|
`;
|
|
39092
|
-
prompt += `- Created At: ${projectConfig.createdAt || "Unknown"}
|
|
39093
|
-
|
|
39094
|
-
`;
|
|
39095
|
-
}
|
|
39096
|
-
let serverContext = null;
|
|
39097
|
-
if (options.taskContext) {
|
|
39098
|
-
try {
|
|
39099
|
-
serverContext = JSON.parse(options.taskContext);
|
|
39100
|
-
} catch {
|
|
39101
|
-
serverContext = { context: options.taskContext };
|
|
39102
|
-
}
|
|
39103
38961
|
}
|
|
39104
|
-
|
|
39105
|
-
|
|
39106
|
-
|
|
39107
|
-
try {
|
|
39108
|
-
const context2 = readFileSync6(contextPath, "utf-8");
|
|
39109
|
-
if (context2.trim().length > 20) {
|
|
39110
|
-
prompt += `## Project Context (Local)
|
|
38962
|
+
if (context2) {
|
|
38963
|
+
sections += `
|
|
38964
|
+
<project_context>
|
|
39111
38965
|
${context2}
|
|
39112
|
-
|
|
38966
|
+
</project_context>
|
|
39113
38967
|
`;
|
|
39114
|
-
hasLocalContext = true;
|
|
39115
|
-
}
|
|
39116
|
-
} catch (err) {
|
|
39117
|
-
console.warn(`Warning: Could not read context file: ${err}`);
|
|
39118
|
-
}
|
|
39119
38968
|
}
|
|
39120
|
-
|
|
39121
|
-
|
|
39122
|
-
|
|
39123
|
-
|
|
39124
|
-
${fallback}
|
|
39125
|
-
|
|
39126
|
-
`;
|
|
39127
|
-
}
|
|
39128
|
-
}
|
|
39129
|
-
if (serverContext) {
|
|
39130
|
-
prompt += `## Project Context (Server)
|
|
39131
|
-
`;
|
|
39132
|
-
const project = serverContext.project;
|
|
39133
|
-
if (project) {
|
|
39134
|
-
prompt += `- Project: ${project.name || "Unknown"}
|
|
38969
|
+
sections += `
|
|
38970
|
+
<knowledge_base>
|
|
38971
|
+
${knowledgeBase}
|
|
38972
|
+
</knowledge_base>
|
|
39135
38973
|
`;
|
|
39136
|
-
|
|
39137
|
-
|
|
39138
|
-
|
|
39139
|
-
|
|
39140
|
-
|
|
39141
|
-
|
|
39142
|
-
prompt += `
|
|
39143
|
-
${serverContext.context}
|
|
39144
|
-
`;
|
|
39145
|
-
}
|
|
39146
|
-
prompt += `
|
|
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>
|
|
39147
38980
|
`;
|
|
39148
38981
|
}
|
|
39149
|
-
prompt += this.getProjectStructure();
|
|
39150
|
-
prompt += `## Project Knowledge Base
|
|
39151
|
-
`;
|
|
39152
|
-
prompt += `You have access to the following documentation directories for context:
|
|
39153
|
-
`;
|
|
39154
|
-
prompt += `- Artifacts: \`.locus/artifacts\`
|
|
39155
|
-
`;
|
|
39156
|
-
prompt += `- Documents: \`.locus/documents\`
|
|
39157
|
-
`;
|
|
39158
|
-
prompt += `If you need more information about the project strategies, plans, or architecture, please read files in these directories.
|
|
39159
|
-
|
|
39160
|
-
`;
|
|
39161
38982
|
if (task2.docs && task2.docs.length > 0) {
|
|
39162
|
-
|
|
39163
|
-
`;
|
|
39164
|
-
prompt += `> Full content available on server. Rely on Task Description for specific requirements.
|
|
39165
|
-
|
|
39166
|
-
`;
|
|
38983
|
+
let docsContent = "";
|
|
39167
38984
|
for (const doc3 of task2.docs) {
|
|
39168
38985
|
const content = doc3.content || "";
|
|
39169
38986
|
const limit = 800;
|
|
39170
38987
|
const preview = content.slice(0, limit);
|
|
39171
38988
|
const isTruncated = content.length > limit;
|
|
39172
|
-
|
|
38989
|
+
docsContent += `### ${doc3.title}
|
|
39173
38990
|
${preview}${isTruncated ? `
|
|
39174
38991
|
...(truncated)...` : ""}
|
|
39175
38992
|
|
|
39176
38993
|
`;
|
|
39177
38994
|
}
|
|
38995
|
+
sections += `
|
|
38996
|
+
<documents>
|
|
38997
|
+
${docsContent.trimEnd()}
|
|
38998
|
+
</documents>
|
|
38999
|
+
`;
|
|
39178
39000
|
}
|
|
39179
39001
|
if (task2.acceptanceChecklist && task2.acceptanceChecklist.length > 0) {
|
|
39180
|
-
|
|
39181
|
-
`;
|
|
39002
|
+
let criteria = "";
|
|
39182
39003
|
for (const item of task2.acceptanceChecklist) {
|
|
39183
|
-
|
|
39004
|
+
criteria += `- ${item.done ? "[x]" : "[ ]"} ${item.text}
|
|
39184
39005
|
`;
|
|
39185
39006
|
}
|
|
39186
|
-
|
|
39007
|
+
sections += `
|
|
39008
|
+
<acceptance_criteria>
|
|
39009
|
+
${criteria.trimEnd()}
|
|
39010
|
+
</acceptance_criteria>
|
|
39187
39011
|
`;
|
|
39188
39012
|
}
|
|
39189
39013
|
if (task2.comments && task2.comments.length > 0) {
|
|
39190
39014
|
const filteredComments = task2.comments.filter((comment) => comment.author !== "system");
|
|
39191
39015
|
const comments = filteredComments.slice(0, 3);
|
|
39192
|
-
|
|
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}
|
|
39193
39021
|
`;
|
|
39194
|
-
|
|
39195
|
-
|
|
39196
|
-
|
|
39022
|
+
}
|
|
39023
|
+
sections += `
|
|
39024
|
+
<feedback>
|
|
39025
|
+
${commentsContent.trimEnd()}
|
|
39026
|
+
</feedback>
|
|
39197
39027
|
`;
|
|
39198
39028
|
}
|
|
39199
|
-
prompt += `
|
|
39200
|
-
`;
|
|
39201
39029
|
}
|
|
39202
|
-
|
|
39203
|
-
|
|
39204
|
-
|
|
39205
|
-
|
|
39206
|
-
|
|
39207
|
-
|
|
39208
|
-
|
|
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>`;
|
|
39209
39044
|
}
|
|
39210
39045
|
async buildGenericPrompt(query) {
|
|
39211
|
-
|
|
39212
|
-
|
|
39213
|
-
|
|
39214
|
-
|
|
39215
|
-
|
|
39216
|
-
|
|
39217
|
-
|
|
39218
|
-
|
|
39219
|
-
|
|
39220
|
-
prompt += `## Project Metadata
|
|
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>
|
|
39221
39055
|
`;
|
|
39222
|
-
|
|
39056
|
+
}
|
|
39057
|
+
sections += `
|
|
39058
|
+
<knowledge_base>
|
|
39059
|
+
${knowledgeBase}
|
|
39060
|
+
</knowledge_base>
|
|
39223
39061
|
`;
|
|
39224
|
-
|
|
39225
|
-
|
|
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>
|
|
39226
39068
|
`;
|
|
39227
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() {
|
|
39228
39081
|
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
39229
|
-
|
|
39230
|
-
if (existsSync6(contextPath)) {
|
|
39082
|
+
if (existsSync5(contextPath)) {
|
|
39231
39083
|
try {
|
|
39232
|
-
const context2 =
|
|
39084
|
+
const context2 = readFileSync5(contextPath, "utf-8");
|
|
39233
39085
|
if (context2.trim().length > 20) {
|
|
39234
|
-
|
|
39235
|
-
${context2}
|
|
39236
|
-
|
|
39237
|
-
`;
|
|
39238
|
-
hasLocalContext = true;
|
|
39086
|
+
return context2;
|
|
39239
39087
|
}
|
|
39240
39088
|
} catch (err) {
|
|
39241
39089
|
console.warn(`Warning: Could not read context file: ${err}`);
|
|
39242
39090
|
}
|
|
39243
39091
|
}
|
|
39244
|
-
|
|
39245
|
-
const fallback = this.getFallbackContext();
|
|
39246
|
-
if (fallback) {
|
|
39247
|
-
prompt += `## Project Context (README Fallback)
|
|
39248
|
-
${fallback}
|
|
39249
|
-
|
|
39250
|
-
`;
|
|
39251
|
-
}
|
|
39252
|
-
}
|
|
39253
|
-
prompt += this.getProjectStructure();
|
|
39254
|
-
prompt += `## Project Knowledge Base
|
|
39255
|
-
`;
|
|
39256
|
-
prompt += `You have access to the following documentation directories for context:
|
|
39257
|
-
`;
|
|
39258
|
-
prompt += `- Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
|
|
39259
|
-
`;
|
|
39260
|
-
prompt += `- Documents: \`.locus/documents\` (synced from cloud)
|
|
39261
|
-
`;
|
|
39262
|
-
prompt += `If you need more information about the project strategies, plans, or architecture, please read files in these directories.
|
|
39263
|
-
|
|
39264
|
-
`;
|
|
39265
|
-
prompt += `## Instructions
|
|
39266
|
-
1. Execute the prompt based on the provided project context.
|
|
39267
|
-
2. **Paths**: Use relative paths from the project root at all times. Do NOT use absolute local paths (e.g., /Users/...).
|
|
39268
|
-
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.
|
|
39269
|
-
4. **Progress**: Do NOT modify \`.locus/project/progress.md\`. The system updates it automatically.`;
|
|
39270
|
-
return prompt;
|
|
39271
|
-
}
|
|
39272
|
-
getProjectConfig() {
|
|
39273
|
-
const configPath = getLocusPath(this.projectPath, "configFile");
|
|
39274
|
-
if (existsSync6(configPath)) {
|
|
39275
|
-
try {
|
|
39276
|
-
return JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
39277
|
-
} catch {
|
|
39278
|
-
return null;
|
|
39279
|
-
}
|
|
39280
|
-
}
|
|
39281
|
-
return null;
|
|
39092
|
+
return this.getFallbackContext() || null;
|
|
39282
39093
|
}
|
|
39283
39094
|
getFallbackContext() {
|
|
39284
39095
|
const readmePath = join6(this.projectPath, "README.md");
|
|
39285
|
-
if (
|
|
39096
|
+
if (existsSync5(readmePath)) {
|
|
39286
39097
|
try {
|
|
39287
|
-
const content =
|
|
39098
|
+
const content = readFileSync5(readmePath, "utf-8");
|
|
39288
39099
|
const limit = 1000;
|
|
39289
39100
|
return content.slice(0, limit) + (content.length > limit ? `
|
|
39290
39101
|
...(truncated)...` : "");
|
|
@@ -39294,32 +39105,28 @@ ${fallback}
|
|
|
39294
39105
|
}
|
|
39295
39106
|
return "";
|
|
39296
39107
|
}
|
|
39297
|
-
|
|
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
|
+
}
|
|
39298
39119
|
try {
|
|
39299
|
-
const
|
|
39300
|
-
const
|
|
39301
|
-
|
|
39302
|
-
|
|
39303
|
-
|
|
39304
|
-
return statSync(join6(this.projectPath, e)).isDirectory();
|
|
39305
|
-
} catch {
|
|
39306
|
-
return false;
|
|
39307
|
-
}
|
|
39308
|
-
});
|
|
39309
|
-
if (folders.length === 0)
|
|
39310
|
-
return "";
|
|
39311
|
-
let structure = `## Project Structure
|
|
39312
|
-
`;
|
|
39313
|
-
structure += `Key directories in this project:
|
|
39314
|
-
`;
|
|
39315
|
-
for (const folder of folders) {
|
|
39316
|
-
structure += `- \`${folder}/\`
|
|
39317
|
-
`;
|
|
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;
|
|
39318
39125
|
}
|
|
39319
|
-
return
|
|
39320
|
-
|
|
39126
|
+
return lines.join(`
|
|
39127
|
+
`);
|
|
39321
39128
|
} catch {
|
|
39322
|
-
return
|
|
39129
|
+
return null;
|
|
39323
39130
|
}
|
|
39324
39131
|
}
|
|
39325
39132
|
roleToText(role) {
|
|
@@ -39455,7 +39262,6 @@ class AgentWorker {
|
|
|
39455
39262
|
client;
|
|
39456
39263
|
aiRunner;
|
|
39457
39264
|
taskExecutor;
|
|
39458
|
-
knowledgeBase;
|
|
39459
39265
|
gitWorkflow;
|
|
39460
39266
|
maxTasks = 50;
|
|
39461
39267
|
tasksCompleted = 0;
|
|
@@ -39495,7 +39301,6 @@ class AgentWorker {
|
|
|
39495
39301
|
projectPath,
|
|
39496
39302
|
log
|
|
39497
39303
|
});
|
|
39498
|
-
this.knowledgeBase = new KnowledgeBase(projectPath);
|
|
39499
39304
|
this.gitWorkflow = new GitWorkflow(config2, log);
|
|
39500
39305
|
const providerLabel = provider === "codex" ? "Codex" : "Claude";
|
|
39501
39306
|
this.log(`Using ${providerLabel} CLI for all phases`, "info");
|
|
@@ -39569,20 +39374,6 @@ class AgentWorker {
|
|
|
39569
39374
|
};
|
|
39570
39375
|
}
|
|
39571
39376
|
}
|
|
39572
|
-
updateProgress(task2, summary) {
|
|
39573
|
-
try {
|
|
39574
|
-
this.knowledgeBase.updateProgress({
|
|
39575
|
-
role: "user",
|
|
39576
|
-
content: task2.title
|
|
39577
|
-
});
|
|
39578
|
-
this.knowledgeBase.updateProgress({
|
|
39579
|
-
role: "assistant",
|
|
39580
|
-
content: summary
|
|
39581
|
-
});
|
|
39582
|
-
} catch (err) {
|
|
39583
|
-
this.log(`Failed to update progress: ${err instanceof Error ? err.message : String(err)}`, "warn");
|
|
39584
|
-
}
|
|
39585
|
-
}
|
|
39586
39377
|
startHeartbeat() {
|
|
39587
39378
|
this.sendHeartbeat();
|
|
39588
39379
|
this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), 60000);
|
|
@@ -39657,7 +39448,6 @@ Branch: \`${result.branch}\`` : "";
|
|
|
39657
39448
|
this.tasksCompleted++;
|
|
39658
39449
|
this.completedTaskList.push({ title: task2.title, id: task2.id });
|
|
39659
39450
|
this.taskSummaries.push(result.summary);
|
|
39660
|
-
this.updateProgress(task2, result.summary);
|
|
39661
39451
|
}
|
|
39662
39452
|
} else {
|
|
39663
39453
|
this.log(`Failed: ${task2.title} - ${result.summary}`, "error");
|
|
@@ -39703,7 +39493,6 @@ var init_worker = __esm(() => {
|
|
|
39703
39493
|
init_config();
|
|
39704
39494
|
init_git_utils();
|
|
39705
39495
|
init_src2();
|
|
39706
|
-
init_knowledge_base();
|
|
39707
39496
|
init_colors();
|
|
39708
39497
|
init_git_workflow();
|
|
39709
39498
|
init_task_executor();
|
|
@@ -40153,12 +39942,12 @@ var init_event_emitter = __esm(() => {
|
|
|
40153
39942
|
|
|
40154
39943
|
// ../sdk/src/exec/history-manager.ts
|
|
40155
39944
|
import {
|
|
40156
|
-
existsSync as
|
|
40157
|
-
mkdirSync as
|
|
40158
|
-
readdirSync as
|
|
40159
|
-
readFileSync as
|
|
39945
|
+
existsSync as existsSync6,
|
|
39946
|
+
mkdirSync as mkdirSync3,
|
|
39947
|
+
readdirSync as readdirSync2,
|
|
39948
|
+
readFileSync as readFileSync6,
|
|
40160
39949
|
rmSync,
|
|
40161
|
-
writeFileSync as
|
|
39950
|
+
writeFileSync as writeFileSync3
|
|
40162
39951
|
} from "node:fs";
|
|
40163
39952
|
import { join as join7 } from "node:path";
|
|
40164
39953
|
function generateSessionId2() {
|
|
@@ -40176,8 +39965,8 @@ class HistoryManager {
|
|
|
40176
39965
|
this.ensureHistoryDir();
|
|
40177
39966
|
}
|
|
40178
39967
|
ensureHistoryDir() {
|
|
40179
|
-
if (!
|
|
40180
|
-
|
|
39968
|
+
if (!existsSync6(this.historyDir)) {
|
|
39969
|
+
mkdirSync3(this.historyDir, { recursive: true });
|
|
40181
39970
|
}
|
|
40182
39971
|
}
|
|
40183
39972
|
getSessionPath(sessionId) {
|
|
@@ -40186,15 +39975,15 @@ class HistoryManager {
|
|
|
40186
39975
|
saveSession(session2) {
|
|
40187
39976
|
const filePath = this.getSessionPath(session2.id);
|
|
40188
39977
|
session2.updatedAt = Date.now();
|
|
40189
|
-
|
|
39978
|
+
writeFileSync3(filePath, JSON.stringify(session2, null, 2), "utf-8");
|
|
40190
39979
|
}
|
|
40191
39980
|
loadSession(sessionId) {
|
|
40192
39981
|
const filePath = this.getSessionPath(sessionId);
|
|
40193
|
-
if (!
|
|
39982
|
+
if (!existsSync6(filePath)) {
|
|
40194
39983
|
return null;
|
|
40195
39984
|
}
|
|
40196
39985
|
try {
|
|
40197
|
-
const content =
|
|
39986
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
40198
39987
|
return JSON.parse(content);
|
|
40199
39988
|
} catch {
|
|
40200
39989
|
return null;
|
|
@@ -40202,7 +39991,7 @@ class HistoryManager {
|
|
|
40202
39991
|
}
|
|
40203
39992
|
deleteSession(sessionId) {
|
|
40204
39993
|
const filePath = this.getSessionPath(sessionId);
|
|
40205
|
-
if (!
|
|
39994
|
+
if (!existsSync6(filePath)) {
|
|
40206
39995
|
return false;
|
|
40207
39996
|
}
|
|
40208
39997
|
try {
|
|
@@ -40213,7 +40002,7 @@ class HistoryManager {
|
|
|
40213
40002
|
}
|
|
40214
40003
|
}
|
|
40215
40004
|
listSessions(options) {
|
|
40216
|
-
const files =
|
|
40005
|
+
const files = readdirSync2(this.historyDir);
|
|
40217
40006
|
let sessions = [];
|
|
40218
40007
|
for (const file2 of files) {
|
|
40219
40008
|
if (file2.endsWith(".json")) {
|
|
@@ -40286,11 +40075,11 @@ class HistoryManager {
|
|
|
40286
40075
|
return deleted;
|
|
40287
40076
|
}
|
|
40288
40077
|
getSessionCount() {
|
|
40289
|
-
const files =
|
|
40078
|
+
const files = readdirSync2(this.historyDir);
|
|
40290
40079
|
return files.filter((f) => f.endsWith(".json")).length;
|
|
40291
40080
|
}
|
|
40292
40081
|
sessionExists(sessionId) {
|
|
40293
|
-
return
|
|
40082
|
+
return existsSync6(this.getSessionPath(sessionId));
|
|
40294
40083
|
}
|
|
40295
40084
|
findSessionByPartialId(partialId) {
|
|
40296
40085
|
const sessions = this.listSessions();
|
|
@@ -40304,7 +40093,7 @@ class HistoryManager {
|
|
|
40304
40093
|
return this.historyDir;
|
|
40305
40094
|
}
|
|
40306
40095
|
clearAllSessions() {
|
|
40307
|
-
const files =
|
|
40096
|
+
const files = readdirSync2(this.historyDir);
|
|
40308
40097
|
let deleted = 0;
|
|
40309
40098
|
for (const file2 of files) {
|
|
40310
40099
|
if (file2.endsWith(".json")) {
|
|
@@ -40598,8 +40387,8 @@ var init_git = __esm(() => {
|
|
|
40598
40387
|
|
|
40599
40388
|
// ../sdk/src/orchestrator/index.ts
|
|
40600
40389
|
import { spawn as spawn3 } from "node:child_process";
|
|
40601
|
-
import { existsSync as
|
|
40602
|
-
import { dirname as
|
|
40390
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
40391
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
40603
40392
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
40604
40393
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
40605
40394
|
function killProcessTree(proc) {
|
|
@@ -40886,18 +40675,21 @@ ${agentId} finished (exit code: ${code})`);
|
|
|
40886
40675
|
}
|
|
40887
40676
|
resolveWorkerPath() {
|
|
40888
40677
|
const currentModulePath = fileURLToPath2(import.meta.url);
|
|
40889
|
-
const currentModuleDir =
|
|
40678
|
+
const currentModuleDir = dirname2(currentModulePath);
|
|
40890
40679
|
const potentialPaths = [
|
|
40891
40680
|
join8(currentModuleDir, "..", "agent", "worker.js"),
|
|
40892
40681
|
join8(currentModuleDir, "agent", "worker.js"),
|
|
40893
40682
|
join8(currentModuleDir, "worker.js"),
|
|
40894
40683
|
join8(currentModuleDir, "..", "agent", "worker.ts")
|
|
40895
40684
|
];
|
|
40896
|
-
return potentialPaths.find((p) =>
|
|
40685
|
+
return potentialPaths.find((p) => existsSync7(p));
|
|
40897
40686
|
}
|
|
40898
40687
|
};
|
|
40899
40688
|
});
|
|
40900
40689
|
|
|
40690
|
+
// ../sdk/src/utils/structured-output.ts
|
|
40691
|
+
var init_structured_output = () => {};
|
|
40692
|
+
|
|
40901
40693
|
// ../sdk/src/planning/sprint-plan.ts
|
|
40902
40694
|
function sprintPlanToMarkdown(plan) {
|
|
40903
40695
|
const lines = [];
|
|
@@ -40972,68 +40764,48 @@ function plannedTasksToCreatePayloads(plan, sprintId) {
|
|
|
40972
40764
|
}))
|
|
40973
40765
|
}));
|
|
40974
40766
|
}
|
|
40975
|
-
|
|
40976
|
-
const jsonStr = extractJsonFromLLMOutput(raw);
|
|
40977
|
-
let parsed;
|
|
40978
|
-
try {
|
|
40979
|
-
parsed = JSON.parse(jsonStr);
|
|
40980
|
-
} catch (err) {
|
|
40981
|
-
const preview = jsonStr.slice(0, 200);
|
|
40982
|
-
throw new Error(`Failed to parse sprint plan JSON: ${err instanceof Error ? err.message : String(err)}
|
|
40983
|
-
Extracted JSON preview: ${preview}`);
|
|
40984
|
-
}
|
|
40985
|
-
if (parsed.revisedPlan) {
|
|
40986
|
-
parsed = parsed.revisedPlan;
|
|
40987
|
-
}
|
|
40988
|
-
const now = new Date().toISOString();
|
|
40989
|
-
const id = `plan-${Date.now()}`;
|
|
40990
|
-
const tasks2 = (parsed.tasks || []).map((t, i) => ({
|
|
40991
|
-
index: i + 1,
|
|
40992
|
-
title: t.title || `Task ${i + 1}`,
|
|
40993
|
-
description: t.description || "",
|
|
40994
|
-
assigneeRole: t.assigneeRole || "BACKEND",
|
|
40995
|
-
priority: t.priority || "MEDIUM",
|
|
40996
|
-
complexity: t.complexity || 3,
|
|
40997
|
-
acceptanceCriteria: t.acceptanceCriteria || [],
|
|
40998
|
-
labels: t.labels || []
|
|
40999
|
-
}));
|
|
41000
|
-
return {
|
|
41001
|
-
id,
|
|
41002
|
-
name: parsed.name || "Unnamed Sprint",
|
|
41003
|
-
goal: parsed.goal || directive,
|
|
41004
|
-
directive,
|
|
41005
|
-
tasks: tasks2,
|
|
41006
|
-
risks: (parsed.risks || []).map((r) => ({
|
|
41007
|
-
description: r.description || "",
|
|
41008
|
-
mitigation: r.mitigation || "",
|
|
41009
|
-
severity: r.severity || "medium"
|
|
41010
|
-
})),
|
|
41011
|
-
estimatedDays: parsed.estimatedDays || 1,
|
|
41012
|
-
status: "pending",
|
|
41013
|
-
createdAt: now,
|
|
41014
|
-
updatedAt: now
|
|
41015
|
-
};
|
|
41016
|
-
}
|
|
40767
|
+
var PlannedTaskSchema, SprintPlanRiskSchema, PlannerOutputSchema;
|
|
41017
40768
|
var init_sprint_plan = __esm(() => {
|
|
41018
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
|
+
});
|
|
41019
40793
|
});
|
|
41020
40794
|
|
|
41021
40795
|
// ../sdk/src/planning/plan-manager.ts
|
|
41022
40796
|
import {
|
|
41023
|
-
existsSync as
|
|
41024
|
-
mkdirSync as
|
|
41025
|
-
readdirSync as
|
|
41026
|
-
readFileSync as
|
|
40797
|
+
existsSync as existsSync8,
|
|
40798
|
+
mkdirSync as mkdirSync4,
|
|
40799
|
+
readdirSync as readdirSync3,
|
|
40800
|
+
readFileSync as readFileSync7,
|
|
41027
40801
|
unlinkSync as unlinkSync2,
|
|
41028
|
-
writeFileSync as
|
|
40802
|
+
writeFileSync as writeFileSync4
|
|
41029
40803
|
} from "node:fs";
|
|
41030
40804
|
import { join as join9 } from "node:path";
|
|
41031
40805
|
|
|
41032
40806
|
class PlanManager {
|
|
41033
|
-
projectPath;
|
|
41034
40807
|
plansDir;
|
|
41035
40808
|
constructor(projectPath) {
|
|
41036
|
-
this.projectPath = projectPath;
|
|
41037
40809
|
this.plansDir = getLocusPath(projectPath, "plansDir");
|
|
41038
40810
|
}
|
|
41039
40811
|
save(plan) {
|
|
@@ -41041,17 +40813,17 @@ class PlanManager {
|
|
|
41041
40813
|
const slug = this.slugify(plan.name);
|
|
41042
40814
|
const jsonPath = join9(this.plansDir, `${slug}.json`);
|
|
41043
40815
|
const mdPath = join9(this.plansDir, `sprint-${slug}.md`);
|
|
41044
|
-
|
|
41045
|
-
|
|
40816
|
+
writeFileSync4(jsonPath, JSON.stringify(plan, null, 2), "utf-8");
|
|
40817
|
+
writeFileSync4(mdPath, sprintPlanToMarkdown(plan), "utf-8");
|
|
41046
40818
|
return plan.id;
|
|
41047
40819
|
}
|
|
41048
40820
|
load(idOrSlug) {
|
|
41049
40821
|
this.ensurePlansDir();
|
|
41050
|
-
const files =
|
|
40822
|
+
const files = readdirSync3(this.plansDir).filter((f) => f.endsWith(".json"));
|
|
41051
40823
|
for (const file2 of files) {
|
|
41052
40824
|
const filePath = join9(this.plansDir, file2);
|
|
41053
40825
|
try {
|
|
41054
|
-
const plan = JSON.parse(
|
|
40826
|
+
const plan = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
41055
40827
|
if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
|
|
41056
40828
|
return plan;
|
|
41057
40829
|
}
|
|
@@ -41061,11 +40833,11 @@ class PlanManager {
|
|
|
41061
40833
|
}
|
|
41062
40834
|
list(status) {
|
|
41063
40835
|
this.ensurePlansDir();
|
|
41064
|
-
const files =
|
|
40836
|
+
const files = readdirSync3(this.plansDir).filter((f) => f.endsWith(".json"));
|
|
41065
40837
|
const plans = [];
|
|
41066
40838
|
for (const file2 of files) {
|
|
41067
40839
|
try {
|
|
41068
|
-
const plan = JSON.parse(
|
|
40840
|
+
const plan = JSON.parse(readFileSync7(join9(this.plansDir, file2), "utf-8"));
|
|
41069
40841
|
if (!status || plan.status === status) {
|
|
41070
40842
|
plans.push(plan);
|
|
41071
40843
|
}
|
|
@@ -41095,15 +40867,6 @@ class PlanManager {
|
|
|
41095
40867
|
plan.status = "approved";
|
|
41096
40868
|
plan.updatedAt = new Date().toISOString();
|
|
41097
40869
|
this.save(plan);
|
|
41098
|
-
const kb = new KnowledgeBase(this.projectPath);
|
|
41099
|
-
kb.updateProgress({
|
|
41100
|
-
role: "user",
|
|
41101
|
-
content: `Start sprint: ${plan.name}`
|
|
41102
|
-
});
|
|
41103
|
-
kb.updateProgress({
|
|
41104
|
-
role: "assistant",
|
|
41105
|
-
content: `Sprint started with ${tasks2.length} tasks. Goal: ${plan.goal}`
|
|
41106
|
-
});
|
|
41107
40870
|
return { sprint: sprint2, tasks: tasks2 };
|
|
41108
40871
|
}
|
|
41109
40872
|
reject(idOrSlug, feedback) {
|
|
@@ -41131,17 +40894,17 @@ class PlanManager {
|
|
|
41131
40894
|
}
|
|
41132
40895
|
delete(idOrSlug) {
|
|
41133
40896
|
this.ensurePlansDir();
|
|
41134
|
-
const files =
|
|
40897
|
+
const files = readdirSync3(this.plansDir);
|
|
41135
40898
|
for (const file2 of files) {
|
|
41136
40899
|
const filePath = join9(this.plansDir, file2);
|
|
41137
40900
|
if (!file2.endsWith(".json"))
|
|
41138
40901
|
continue;
|
|
41139
40902
|
try {
|
|
41140
|
-
const plan = JSON.parse(
|
|
40903
|
+
const plan = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
41141
40904
|
if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
|
|
41142
40905
|
unlinkSync2(filePath);
|
|
41143
40906
|
const mdPath = join9(this.plansDir, `sprint-${this.slugify(plan.name)}.md`);
|
|
41144
|
-
if (
|
|
40907
|
+
if (existsSync8(mdPath)) {
|
|
41145
40908
|
unlinkSync2(mdPath);
|
|
41146
40909
|
}
|
|
41147
40910
|
return;
|
|
@@ -41156,8 +40919,8 @@ class PlanManager {
|
|
|
41156
40919
|
return sprintPlanToMarkdown(plan);
|
|
41157
40920
|
}
|
|
41158
40921
|
ensurePlansDir() {
|
|
41159
|
-
if (!
|
|
41160
|
-
|
|
40922
|
+
if (!existsSync8(this.plansDir)) {
|
|
40923
|
+
mkdirSync4(this.plansDir, { recursive: true });
|
|
41161
40924
|
}
|
|
41162
40925
|
}
|
|
41163
40926
|
slugify(name) {
|
|
@@ -41166,227 +40929,82 @@ class PlanManager {
|
|
|
41166
40929
|
}
|
|
41167
40930
|
var init_plan_manager = __esm(() => {
|
|
41168
40931
|
init_config();
|
|
41169
|
-
init_knowledge_base();
|
|
41170
40932
|
init_sprint_plan();
|
|
41171
40933
|
});
|
|
41172
40934
|
|
|
41173
|
-
// ../sdk/src/planning/agents/cross-task-reviewer.ts
|
|
41174
|
-
function buildCrossTaskReviewerPrompt(input) {
|
|
41175
|
-
let prompt = `# Role: Cross-Task Reviewer (Architect + Engineer + Planner)
|
|
41176
|
-
|
|
41177
|
-
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.
|
|
41178
|
-
|
|
41179
|
-
## Context
|
|
41180
|
-
|
|
41181
|
-
In this system, tasks are executed SEQUENTIALLY by a single agent on ONE branch:
|
|
41182
|
-
- Tasks run one at a time, in the order they appear in the array
|
|
41183
|
-
- Each task's changes are committed before the next task starts
|
|
41184
|
-
- Later tasks can see and build on earlier tasks' work
|
|
41185
|
-
- The final result is a single branch with all changes, which becomes a pull request
|
|
41186
|
-
|
|
41187
|
-
This means:
|
|
41188
|
-
- Task ordering is critical — a task must NOT depend on a later task's output
|
|
41189
|
-
- Foundation work (config, schemas, shared code) must come first
|
|
41190
|
-
- Each task should be a focused, logical unit of work
|
|
41191
|
-
|
|
41192
|
-
## CEO Directive
|
|
41193
|
-
> ${input.directive}
|
|
41194
|
-
`;
|
|
41195
|
-
if (input.feedback) {
|
|
41196
|
-
prompt += `
|
|
41197
|
-
## CEO Feedback on Previous Plan
|
|
41198
|
-
> ${input.feedback}
|
|
41199
|
-
|
|
41200
|
-
IMPORTANT: Ensure the reviewed plan still addresses this feedback.
|
|
41201
|
-
`;
|
|
41202
|
-
}
|
|
41203
|
-
prompt += `
|
|
41204
|
-
## Project Context
|
|
41205
|
-
${input.projectContext || "No project context available."}
|
|
41206
|
-
|
|
41207
|
-
## Sprint Plan to Review
|
|
41208
|
-
${input.plannerOutput}
|
|
41209
|
-
|
|
41210
|
-
## Your Review Checklist
|
|
41211
|
-
|
|
41212
|
-
Go through EACH task and check for:
|
|
41213
|
-
|
|
41214
|
-
### 1. Ordering & Dependency Analysis
|
|
41215
|
-
For each task, verify:
|
|
41216
|
-
- Does it depend on any task that appears LATER in the list? If so, reorder.
|
|
41217
|
-
- Are foundational tasks (config, schemas, shared code) at the beginning?
|
|
41218
|
-
- Is the overall execution order logical?
|
|
41219
|
-
|
|
41220
|
-
### 2. Scope & Completeness
|
|
41221
|
-
For each task, verify:
|
|
41222
|
-
- Is the task well-scoped? Not too large, not too trivial?
|
|
41223
|
-
- Does it include ALL changes needed for its goal (given earlier tasks are done)?
|
|
41224
|
-
- Are there any missing tasks that should be added?
|
|
41225
|
-
|
|
41226
|
-
### 3. Description Quality Validation
|
|
41227
|
-
For each task, verify the description is a clear, actionable implementation guide. Each description must specify:
|
|
41228
|
-
- **What to do** — the specific goal and expected behavior/outcome
|
|
41229
|
-
- **Where to do it** — specific files, modules, or directories to modify or create
|
|
41230
|
-
- **How to do it** — implementation approach, patterns to follow, existing utilities to use
|
|
41231
|
-
- **Boundaries** — what is NOT in scope for this task
|
|
41232
|
-
|
|
41233
|
-
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.
|
|
41234
|
-
|
|
41235
|
-
### 4. Risk Assessment
|
|
41236
|
-
- Are there tasks that might fail or have unknowns?
|
|
41237
|
-
- Is the sprint scope realistic for sequential execution?
|
|
41238
|
-
|
|
41239
|
-
## Output Format
|
|
41240
|
-
|
|
41241
|
-
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:
|
|
41242
|
-
|
|
41243
|
-
{
|
|
41244
|
-
"hasIssues": true | false,
|
|
41245
|
-
"issues": [
|
|
41246
|
-
{
|
|
41247
|
-
"type": "wrong_order" | "missing_task" | "scope_issue" | "vague_description",
|
|
41248
|
-
"description": "string describing the specific issue",
|
|
41249
|
-
"affectedTasks": ["Task Title 1", "Task Title 2"],
|
|
41250
|
-
"resolution": "string describing how to fix it"
|
|
41251
|
-
}
|
|
41252
|
-
],
|
|
41253
|
-
"revisedPlan": {
|
|
41254
|
-
"name": "string (2-4 words)",
|
|
41255
|
-
"goal": "string (1 paragraph)",
|
|
41256
|
-
"estimatedDays": 3,
|
|
41257
|
-
"tasks": [
|
|
41258
|
-
{
|
|
41259
|
-
"title": "string",
|
|
41260
|
-
"description": "string (detailed implementation guide: what to do, where to do it, how to do it, and boundaries)",
|
|
41261
|
-
"assigneeRole": "BACKEND | FRONTEND | QA | PM | DESIGN",
|
|
41262
|
-
"priority": "CRITICAL | HIGH | MEDIUM | LOW",
|
|
41263
|
-
"labels": ["string"],
|
|
41264
|
-
"acceptanceCriteria": ["string"],
|
|
41265
|
-
"complexity": 3
|
|
41266
|
-
}
|
|
41267
|
-
],
|
|
41268
|
-
"risks": [
|
|
41269
|
-
{
|
|
41270
|
-
"description": "string",
|
|
41271
|
-
"mitigation": "string",
|
|
41272
|
-
"severity": "low | medium | high"
|
|
41273
|
-
}
|
|
41274
|
-
]
|
|
41275
|
-
}
|
|
41276
|
-
}
|
|
41277
|
-
|
|
41278
|
-
IMPORTANT:
|
|
41279
|
-
- If hasIssues is true, the revisedPlan MUST contain the corrected task list with issues resolved (reordered, descriptions rewritten, missing tasks added, etc.)
|
|
41280
|
-
- If hasIssues is false, the revisedPlan should be identical to the input plan (no changes needed)
|
|
41281
|
-
- The revisedPlan is ALWAYS required — it becomes the final plan
|
|
41282
|
-
- Ensure every task description is a detailed implementation guide (what, where, how, boundaries) — rewrite vague descriptions
|
|
41283
|
-
- Tasks execute sequentially — the array order IS the execution order`;
|
|
41284
|
-
return prompt;
|
|
41285
|
-
}
|
|
41286
|
-
|
|
41287
40935
|
// ../sdk/src/planning/agents/planner.ts
|
|
41288
40936
|
function buildPlannerPrompt(input) {
|
|
41289
|
-
let
|
|
41290
|
-
|
|
41291
|
-
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.
|
|
41292
|
-
|
|
41293
|
-
## CEO Directive
|
|
41294
|
-
> ${input.directive}
|
|
41295
|
-
`;
|
|
40937
|
+
let feedbackSection = "";
|
|
41296
40938
|
if (input.feedback) {
|
|
41297
|
-
|
|
41298
|
-
|
|
41299
|
-
|
|
41300
|
-
|
|
41301
|
-
|
|
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>
|
|
41302
40944
|
`;
|
|
41303
40945
|
}
|
|
41304
|
-
|
|
41305
|
-
|
|
41306
|
-
|
|
41307
|
-
|
|
41308
|
-
|
|
41309
|
-
|
|
41310
|
-
|
|
41311
|
-
|
|
41312
|
-
|
|
41313
|
-
|
|
41314
|
-
|
|
41315
|
-
|
|
41316
|
-
|
|
41317
|
-
|
|
41318
|
-
|
|
41319
|
-
|
|
41320
|
-
|
|
41321
|
-
|
|
41322
|
-
|
|
41323
|
-
- **Assignee Role** — BACKEND, FRONTEND, QA, PM, or DESIGN
|
|
41324
|
-
- **Priority** — CRITICAL, HIGH, MEDIUM, or LOW
|
|
41325
|
-
- **Complexity** — 1 (trivial) to 5 (very complex)
|
|
41326
|
-
- **Labels** — Relevant tags (e.g., "api", "database", "ui", "auth")
|
|
41327
|
-
- **Acceptance Criteria** — Specific, testable conditions for completion
|
|
41328
|
-
|
|
41329
|
-
### CRITICAL: Task Description Requirements
|
|
41330
|
-
|
|
41331
|
-
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:
|
|
41332
|
-
|
|
41333
|
-
1. **What to do** — Clearly state the goal and expected behavior/outcome
|
|
41334
|
-
2. **Where to do it** — List specific files, modules, or directories to modify or create. Reference existing code paths when extending functionality
|
|
41335
|
-
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
|
|
41336
|
-
4. **Boundaries** — What is NOT in scope for this task to prevent overlap with other tasks
|
|
41337
|
-
|
|
41338
|
-
Bad example: "Add authentication to the API."
|
|
41339
|
-
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."
|
|
41340
|
-
|
|
41341
|
-
### CRITICAL: Task Ordering Rules
|
|
41342
|
-
|
|
41343
|
-
Tasks are executed SEQUENTIALLY by a single agent on ONE branch. The agent works through tasks in array order. Therefore:
|
|
41344
|
-
|
|
41345
|
-
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.
|
|
41346
|
-
2. **No forward dependencies.** A task must NOT depend on a task that appears later in the list.
|
|
41347
|
-
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.
|
|
41348
|
-
4. **Keep tasks focused.** Each task should do one logical unit of work. Avoid trivially small or overly large tasks.
|
|
41349
|
-
5. **Merge related trivial work.** If two pieces of work are trivially small and tightly related, combine them into one task.
|
|
41350
|
-
|
|
41351
|
-
### Sprint Scope Guidelines
|
|
41352
|
-
|
|
41353
|
-
- If the sprint would exceed 12 tasks, reduce scope or merge related tasks
|
|
41354
|
-
- Ensure acceptance criteria are specific and testable
|
|
41355
|
-
- Keep the sprint focused on the directive — avoid scope creep
|
|
41356
|
-
|
|
41357
|
-
## Output Format
|
|
41358
|
-
|
|
41359
|
-
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:
|
|
41360
|
-
|
|
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:
|
|
41361
40965
|
{
|
|
41362
|
-
"
|
|
41363
|
-
"
|
|
41364
|
-
"
|
|
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}",
|
|
41365
40974
|
"tasks": [
|
|
41366
40975
|
{
|
|
41367
|
-
"
|
|
41368
|
-
"
|
|
41369
|
-
"
|
|
41370
|
-
"
|
|
41371
|
-
"
|
|
41372
|
-
"
|
|
41373
|
-
"
|
|
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
|
|
41374
40984
|
}
|
|
41375
40985
|
],
|
|
41376
40986
|
"risks": [
|
|
41377
40987
|
{
|
|
41378
|
-
"description": "
|
|
41379
|
-
"mitigation": "
|
|
41380
|
-
"severity": "low
|
|
40988
|
+
"description": "What could go wrong",
|
|
40989
|
+
"mitigation": "How to handle it",
|
|
40990
|
+
"severity": "low|medium|high"
|
|
41381
40991
|
}
|
|
41382
40992
|
]
|
|
41383
40993
|
}
|
|
41384
40994
|
|
|
41385
|
-
IMPORTANT:
|
|
41386
|
-
|
|
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>`;
|
|
41387
41002
|
}
|
|
41388
41003
|
|
|
41389
41004
|
// ../sdk/src/planning/planning-meeting.ts
|
|
41005
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync8 } from "node:fs";
|
|
41006
|
+
import { join as join10 } from "node:path";
|
|
41007
|
+
|
|
41390
41008
|
class PlanningMeeting {
|
|
41391
41009
|
projectPath;
|
|
41392
41010
|
aiRunner;
|
|
@@ -41399,45 +41017,44 @@ class PlanningMeeting {
|
|
|
41399
41017
|
});
|
|
41400
41018
|
}
|
|
41401
41019
|
async run(directive, feedback) {
|
|
41402
|
-
|
|
41403
|
-
|
|
41404
|
-
|
|
41405
|
-
|
|
41406
|
-
|
|
41407
|
-
|
|
41408
|
-
}
|
|
41409
|
-
|
|
41410
|
-
const
|
|
41411
|
-
this.log("Planner phase complete.", "success");
|
|
41412
|
-
this.log("Phase 2/2: Reviewer checking for conflicts and quality...", "info");
|
|
41413
|
-
const crossTaskReviewerPrompt = buildCrossTaskReviewerPrompt({
|
|
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({
|
|
41414
41029
|
directive,
|
|
41415
|
-
|
|
41416
|
-
|
|
41417
|
-
|
|
41030
|
+
feedback,
|
|
41031
|
+
plansDir,
|
|
41032
|
+
planId,
|
|
41033
|
+
fileName
|
|
41418
41034
|
});
|
|
41419
|
-
const
|
|
41420
|
-
this.log("
|
|
41421
|
-
const
|
|
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
|
+
}
|
|
41422
41047
|
if (feedback) {
|
|
41423
41048
|
plan.feedback = feedback;
|
|
41424
41049
|
}
|
|
41425
41050
|
return {
|
|
41426
41051
|
plan,
|
|
41427
|
-
|
|
41428
|
-
planner: plannerOutput,
|
|
41429
|
-
review: reviewOutput
|
|
41430
|
-
}
|
|
41052
|
+
rawOutput: response
|
|
41431
41053
|
};
|
|
41432
41054
|
}
|
|
41433
|
-
getProjectContext() {
|
|
41434
|
-
const kb = new KnowledgeBase(this.projectPath);
|
|
41435
|
-
return kb.getFullContext();
|
|
41436
|
-
}
|
|
41437
41055
|
}
|
|
41438
41056
|
var init_planning_meeting = __esm(() => {
|
|
41439
|
-
|
|
41440
|
-
init_sprint_plan();
|
|
41057
|
+
init_config();
|
|
41441
41058
|
});
|
|
41442
41059
|
|
|
41443
41060
|
// ../sdk/src/planning/index.ts
|
|
@@ -41451,8 +41068,8 @@ var init_planning = __esm(() => {
|
|
|
41451
41068
|
var init_index_node = __esm(() => {
|
|
41452
41069
|
init_prompt_builder();
|
|
41453
41070
|
init_orchestrator();
|
|
41454
|
-
init_knowledge_base();
|
|
41455
41071
|
init_colors();
|
|
41072
|
+
init_structured_output();
|
|
41456
41073
|
init_agent2();
|
|
41457
41074
|
init_ai();
|
|
41458
41075
|
init_core3();
|
|
@@ -42271,7 +41888,6 @@ class InteractiveSession {
|
|
|
42271
41888
|
isProcessing = false;
|
|
42272
41889
|
conversationHistory = [];
|
|
42273
41890
|
historyManager;
|
|
42274
|
-
knowledgeBase;
|
|
42275
41891
|
currentSession;
|
|
42276
41892
|
projectPath;
|
|
42277
41893
|
model;
|
|
@@ -42287,7 +41903,6 @@ class InteractiveSession {
|
|
|
42287
41903
|
this.promptBuilder = new PromptBuilder(options.projectPath);
|
|
42288
41904
|
this.renderer = new ProgressRenderer({ animated: true });
|
|
42289
41905
|
this.historyManager = new HistoryManager(options.projectPath);
|
|
42290
|
-
this.knowledgeBase = new KnowledgeBase(options.projectPath);
|
|
42291
41906
|
this.projectPath = options.projectPath;
|
|
42292
41907
|
this.model = options.model;
|
|
42293
41908
|
this.provider = options.provider;
|
|
@@ -42431,18 +42046,6 @@ class InteractiveSession {
|
|
|
42431
42046
|
}
|
|
42432
42047
|
}
|
|
42433
42048
|
this.saveSession();
|
|
42434
|
-
try {
|
|
42435
|
-
this.knowledgeBase.updateProgress({
|
|
42436
|
-
role: "user",
|
|
42437
|
-
content: prompt
|
|
42438
|
-
});
|
|
42439
|
-
if (responseContent.trim()) {
|
|
42440
|
-
this.knowledgeBase.updateProgress({
|
|
42441
|
-
role: "assistant",
|
|
42442
|
-
content: responseContent.trim()
|
|
42443
|
-
});
|
|
42444
|
-
}
|
|
42445
|
-
} catch {}
|
|
42446
42049
|
} catch (error48) {
|
|
42447
42050
|
console.error(c.error(`Error: ${error48 instanceof Error ? error48.message : String(error48)}`));
|
|
42448
42051
|
} finally {
|
|
@@ -42518,10 +42121,10 @@ import { createInterface } from "node:readline";
|
|
|
42518
42121
|
|
|
42519
42122
|
// src/settings-manager.ts
|
|
42520
42123
|
init_index_node();
|
|
42521
|
-
import { existsSync as existsSync10, readFileSync as readFileSync9, unlinkSync as unlinkSync3, writeFileSync as
|
|
42522
|
-
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";
|
|
42523
42126
|
function getSettingsPath(projectPath) {
|
|
42524
|
-
return
|
|
42127
|
+
return join11(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
42525
42128
|
}
|
|
42526
42129
|
|
|
42527
42130
|
class SettingsManager {
|
|
@@ -42540,7 +42143,7 @@ class SettingsManager {
|
|
|
42540
42143
|
const { $schema: _2, ...rest } = settings;
|
|
42541
42144
|
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
42542
42145
|
const settingsPath = getSettingsPath(this.projectPath);
|
|
42543
|
-
|
|
42146
|
+
writeFileSync5(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
42544
42147
|
}
|
|
42545
42148
|
get(key) {
|
|
42546
42149
|
return this.load()[key];
|
|
@@ -42823,74 +42426,100 @@ import { parseArgs } from "node:util";
|
|
|
42823
42426
|
// src/config-manager.ts
|
|
42824
42427
|
init_index_node();
|
|
42825
42428
|
import { execSync as execSync2 } from "node:child_process";
|
|
42826
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as
|
|
42827
|
-
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";
|
|
42828
42431
|
var LOCUS_GITIGNORE_MARKER = "# Locus AI";
|
|
42829
|
-
var
|
|
42432
|
+
var LOCUS_MD_TEMPLATE = `## Planning First
|
|
42830
42433
|
|
|
42831
|
-
|
|
42832
|
-
|
|
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
|
|
42833
42440
|
|
|
42834
|
-
|
|
42835
|
-
<!-- List your technologies -->
|
|
42441
|
+
Delete the planning .md files after successful execution.
|
|
42836
42442
|
|
|
42837
|
-
##
|
|
42838
|
-
<!-- Describe your high-level architecture -->
|
|
42443
|
+
## Code Quality
|
|
42839
42444
|
|
|
42840
|
-
|
|
42841
|
-
|
|
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
|
|
42842
42450
|
|
|
42843
|
-
##
|
|
42844
|
-
<!-- List your main feature areas and their status -->
|
|
42845
|
-
`;
|
|
42846
|
-
var DEFAULT_PROGRESS_MD = `# Project Progress
|
|
42451
|
+
## Artifacts
|
|
42847
42452
|
|
|
42848
|
-
|
|
42849
|
-
`;
|
|
42850
|
-
var LOCUS_MD_TEMPLATE = `## Artifacts
|
|
42851
|
-
|
|
42852
|
-
When a task produces knowledge, analysis, or research output rather than (or in addition to) code changes, you **must** save the results as a Markdown file in \`.locus/artifacts/\`. Examples of artifact-worthy tasks:
|
|
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":
|
|
42853
42454
|
|
|
42455
|
+
**Always create artifacts for:**
|
|
42854
42456
|
- Code quality audits, security reviews, vulnerability assessments
|
|
42855
|
-
- Architecture analyses or recommendations
|
|
42856
|
-
- Dependency reports, performance profiling
|
|
42857
|
-
-
|
|
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
|
|
42474
|
+
|
|
42475
|
+
## Continuous Learning
|
|
42858
42476
|
|
|
42859
|
-
**
|
|
42860
|
-
- File path: \`.locus/artifacts/<descriptive-name>.md\` (e.g., \`code-quality-audit.md\`, \`dependency-report.md\`).
|
|
42861
|
-
- The artifact should be a well-structured Markdown document with clear headings, findings, and actionable recommendations where applicable.
|
|
42862
|
-
- Always create the artifact file — do not only return the results as conversation text.
|
|
42863
|
-
- If the task also involves code changes, still produce the artifact alongside the code changes.
|
|
42477
|
+
Read ".locus/LEARNINGS.md" **before starting any task** to avoid repeating mistakes.
|
|
42864
42478
|
|
|
42865
|
-
|
|
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
|
|
42866
42484
|
|
|
42867
|
-
|
|
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
|
|
42868
42491
|
|
|
42869
|
-
|
|
42492
|
+
**Format (append-only, never delete):**
|
|
42870
42493
|
|
|
42871
|
-
|
|
42872
|
-
-
|
|
42873
|
-
|
|
42874
|
-
- Never put raw secrets or credentials in the codebase.
|
|
42494
|
+
"""
|
|
42495
|
+
- **[Category]**: Concise description (1-2 lines max). *Context if needed.*
|
|
42496
|
+
"""
|
|
42875
42497
|
|
|
42876
|
-
|
|
42498
|
+
**Categories:** Architecture, Dependencies, Patterns, Debugging, Performance, Security, DevOps, User Preferences
|
|
42877
42499
|
|
|
42878
|
-
|
|
42879
|
-
|
|
42880
|
-
-
|
|
42881
|
-
-
|
|
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
|
|
42882
42515
|
|
|
42883
|
-
|
|
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.
|
|
42884
42518
|
|
|
42885
|
-
|
|
42886
|
-
- 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.
|
|
42887
|
-
- 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.
|
|
42888
|
-
- 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.
|
|
42889
|
-
- 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.
|
|
42890
|
-
- 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 -->
|
|
42891
42520
|
`;
|
|
42892
42521
|
function updateGitignore(projectPath) {
|
|
42893
|
-
const gitignorePath =
|
|
42522
|
+
const gitignorePath = join12(projectPath, ".gitignore");
|
|
42894
42523
|
let content = "";
|
|
42895
42524
|
const locusBlock = LOCUS_GITIGNORE_PATTERNS.join(`
|
|
42896
42525
|
`);
|
|
@@ -42912,7 +42541,7 @@ function updateGitignore(projectPath) {
|
|
|
42912
42541
|
const after = lines.slice(endIdx + 1);
|
|
42913
42542
|
content = [...before, locusBlock, ...after].join(`
|
|
42914
42543
|
`);
|
|
42915
|
-
|
|
42544
|
+
writeFileSync6(gitignorePath, content);
|
|
42916
42545
|
return;
|
|
42917
42546
|
}
|
|
42918
42547
|
if (content.length > 0 && !content.endsWith(`
|
|
@@ -42927,7 +42556,7 @@ function updateGitignore(projectPath) {
|
|
|
42927
42556
|
}
|
|
42928
42557
|
content += `${locusBlock}
|
|
42929
42558
|
`;
|
|
42930
|
-
|
|
42559
|
+
writeFileSync6(gitignorePath, content);
|
|
42931
42560
|
}
|
|
42932
42561
|
function ensureGitIdentity(projectPath) {
|
|
42933
42562
|
const hasName = (() => {
|
|
@@ -42974,7 +42603,7 @@ class ConfigManager {
|
|
|
42974
42603
|
this.projectPath = projectPath;
|
|
42975
42604
|
}
|
|
42976
42605
|
async init(version2) {
|
|
42977
|
-
const locusConfigDir =
|
|
42606
|
+
const locusConfigDir = join12(this.projectPath, LOCUS_CONFIG.dir);
|
|
42978
42607
|
const locusConfigPath = getLocusPath(this.projectPath, "configFile");
|
|
42979
42608
|
if (!existsSync11(locusConfigDir)) {
|
|
42980
42609
|
mkdirSync6(locusConfigDir, { recursive: true });
|
|
@@ -42984,26 +42613,21 @@ class ConfigManager {
|
|
|
42984
42613
|
LOCUS_CONFIG.documentsDir,
|
|
42985
42614
|
LOCUS_CONFIG.sessionsDir,
|
|
42986
42615
|
LOCUS_CONFIG.reviewsDir,
|
|
42987
|
-
LOCUS_CONFIG.plansDir
|
|
42988
|
-
LOCUS_CONFIG.projectDir
|
|
42616
|
+
LOCUS_CONFIG.plansDir
|
|
42989
42617
|
];
|
|
42990
42618
|
for (const subdir of locusSubdirs) {
|
|
42991
|
-
const subdirPath =
|
|
42619
|
+
const subdirPath = join12(locusConfigDir, subdir);
|
|
42992
42620
|
if (!existsSync11(subdirPath)) {
|
|
42993
42621
|
mkdirSync6(subdirPath, { recursive: true });
|
|
42994
42622
|
}
|
|
42995
42623
|
}
|
|
42996
|
-
const contextFilePath = getLocusPath(this.projectPath, "projectContextFile");
|
|
42997
|
-
if (!existsSync11(contextFilePath)) {
|
|
42998
|
-
writeFileSync7(contextFilePath, DEFAULT_CONTEXT_MD);
|
|
42999
|
-
}
|
|
43000
|
-
const progressFilePath = getLocusPath(this.projectPath, "projectProgressFile");
|
|
43001
|
-
if (!existsSync11(progressFilePath)) {
|
|
43002
|
-
writeFileSync7(progressFilePath, DEFAULT_PROGRESS_MD);
|
|
43003
|
-
}
|
|
43004
42624
|
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
43005
42625
|
if (!existsSync11(locusMdPath)) {
|
|
43006
|
-
|
|
42626
|
+
writeFileSync6(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
42627
|
+
}
|
|
42628
|
+
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
42629
|
+
if (!existsSync11(learningsMdPath)) {
|
|
42630
|
+
writeFileSync6(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
43007
42631
|
}
|
|
43008
42632
|
if (!existsSync11(locusConfigPath)) {
|
|
43009
42633
|
const config2 = {
|
|
@@ -43012,7 +42636,7 @@ class ConfigManager {
|
|
|
43012
42636
|
createdAt: new Date().toISOString(),
|
|
43013
42637
|
projectPath: "."
|
|
43014
42638
|
};
|
|
43015
|
-
|
|
42639
|
+
writeFileSync6(locusConfigPath, JSON.stringify(config2, null, 2));
|
|
43016
42640
|
}
|
|
43017
42641
|
updateGitignore(this.projectPath);
|
|
43018
42642
|
ensureGitIdentity(this.projectPath);
|
|
@@ -43038,8 +42662,6 @@ class ConfigManager {
|
|
|
43038
42662
|
directoriesCreated: [],
|
|
43039
42663
|
gitignoreUpdated: false
|
|
43040
42664
|
};
|
|
43041
|
-
const locusConfigDir = join11(this.projectPath, LOCUS_CONFIG.dir);
|
|
43042
|
-
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
43043
42665
|
const config2 = this.loadConfig();
|
|
43044
42666
|
if (config2) {
|
|
43045
42667
|
result.previousVersion = config2.version;
|
|
@@ -43052,18 +42674,19 @@ class ConfigManager {
|
|
|
43052
42674
|
this.saveConfig(config2);
|
|
43053
42675
|
}
|
|
43054
42676
|
}
|
|
43055
|
-
const settingsPath =
|
|
42677
|
+
const settingsPath = join12(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
|
|
43056
42678
|
if (existsSync11(settingsPath)) {
|
|
43057
42679
|
const raw = readFileSync10(settingsPath, "utf-8");
|
|
43058
42680
|
const settings = JSON.parse(raw);
|
|
43059
42681
|
if (settings.$schema !== LOCUS_SCHEMAS.settings) {
|
|
43060
42682
|
const { $schema: _2, ...rest } = settings;
|
|
43061
42683
|
const ordered = { $schema: LOCUS_SCHEMAS.settings, ...rest };
|
|
43062
|
-
|
|
42684
|
+
writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
|
|
43063
42685
|
}
|
|
43064
42686
|
}
|
|
42687
|
+
const locusMdPath = getLocusPath(this.projectPath, "contextFile");
|
|
43065
42688
|
const locusMdExisted = existsSync11(locusMdPath);
|
|
43066
|
-
|
|
42689
|
+
writeFileSync6(locusMdPath, LOCUS_MD_TEMPLATE);
|
|
43067
42690
|
if (!locusMdExisted) {
|
|
43068
42691
|
result.directoriesCreated.push(".locus/LOCUS.md");
|
|
43069
42692
|
}
|
|
@@ -43072,27 +42695,22 @@ class ConfigManager {
|
|
|
43072
42695
|
LOCUS_CONFIG.documentsDir,
|
|
43073
42696
|
LOCUS_CONFIG.sessionsDir,
|
|
43074
42697
|
LOCUS_CONFIG.reviewsDir,
|
|
43075
|
-
LOCUS_CONFIG.plansDir
|
|
43076
|
-
LOCUS_CONFIG.projectDir
|
|
42698
|
+
LOCUS_CONFIG.plansDir
|
|
43077
42699
|
];
|
|
42700
|
+
const locusConfigDir = join12(this.projectPath, LOCUS_CONFIG.dir);
|
|
43078
42701
|
for (const subdir of locusSubdirs) {
|
|
43079
|
-
const subdirPath =
|
|
42702
|
+
const subdirPath = join12(locusConfigDir, subdir);
|
|
43080
42703
|
if (!existsSync11(subdirPath)) {
|
|
43081
42704
|
mkdirSync6(subdirPath, { recursive: true });
|
|
43082
42705
|
result.directoriesCreated.push(`.locus/${subdir}`);
|
|
43083
42706
|
}
|
|
43084
42707
|
}
|
|
43085
|
-
const
|
|
43086
|
-
if (!existsSync11(
|
|
43087
|
-
|
|
43088
|
-
result.directoriesCreated.push(".locus/
|
|
42708
|
+
const learningsMdPath = getLocusPath(this.projectPath, "learningsFile");
|
|
42709
|
+
if (!existsSync11(learningsMdPath)) {
|
|
42710
|
+
writeFileSync6(learningsMdPath, DEFAULT_LEARNINGS_MD);
|
|
42711
|
+
result.directoriesCreated.push(".locus/LEARNINGS.md");
|
|
43089
42712
|
}
|
|
43090
|
-
const
|
|
43091
|
-
if (!existsSync11(progressFilePath)) {
|
|
43092
|
-
writeFileSync7(progressFilePath, DEFAULT_PROGRESS_MD);
|
|
43093
|
-
result.directoriesCreated.push(".locus/project/progress.md");
|
|
43094
|
-
}
|
|
43095
|
-
const gitignorePath = join11(this.projectPath, ".gitignore");
|
|
42713
|
+
const gitignorePath = join12(this.projectPath, ".gitignore");
|
|
43096
42714
|
const gitignoreBefore = existsSync11(gitignorePath) ? readFileSync10(gitignorePath, "utf-8") : "";
|
|
43097
42715
|
updateGitignore(this.projectPath);
|
|
43098
42716
|
const gitignoreAfter = readFileSync10(gitignorePath, "utf-8");
|
|
@@ -43106,7 +42724,7 @@ class ConfigManager {
|
|
|
43106
42724
|
const { $schema: _2, ...rest } = config2;
|
|
43107
42725
|
const ordered = { $schema: LOCUS_SCHEMAS.config, ...rest };
|
|
43108
42726
|
const path2 = getLocusPath(this.projectPath, "configFile");
|
|
43109
|
-
|
|
42727
|
+
writeFileSync6(path2, JSON.stringify(ordered, null, 2));
|
|
43110
42728
|
}
|
|
43111
42729
|
}
|
|
43112
42730
|
|
|
@@ -43115,14 +42733,14 @@ init_index_node();
|
|
|
43115
42733
|
|
|
43116
42734
|
// src/utils/version.ts
|
|
43117
42735
|
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
43118
|
-
import { dirname as
|
|
42736
|
+
import { dirname as dirname3, join as join13 } from "node:path";
|
|
43119
42737
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
43120
42738
|
function getVersion() {
|
|
43121
42739
|
try {
|
|
43122
42740
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
43123
|
-
const __dirname2 =
|
|
43124
|
-
const bundledPath =
|
|
43125
|
-
const sourcePath =
|
|
42741
|
+
const __dirname2 = dirname3(__filename2);
|
|
42742
|
+
const bundledPath = join13(__dirname2, "..", "package.json");
|
|
42743
|
+
const sourcePath = join13(__dirname2, "..", "..", "package.json");
|
|
43126
42744
|
if (existsSync12(bundledPath)) {
|
|
43127
42745
|
const pkg = JSON.parse(readFileSync11(bundledPath, "utf-8"));
|
|
43128
42746
|
if (pkg.name === "@locusai/cli") {
|
|
@@ -43153,10 +42771,10 @@ function printBanner() {
|
|
|
43153
42771
|
// src/utils/helpers.ts
|
|
43154
42772
|
init_index_node();
|
|
43155
42773
|
import { existsSync as existsSync13 } from "node:fs";
|
|
43156
|
-
import { join as
|
|
42774
|
+
import { join as join14 } from "node:path";
|
|
43157
42775
|
function isProjectInitialized(projectPath) {
|
|
43158
|
-
const locusDir =
|
|
43159
|
-
const configPath =
|
|
42776
|
+
const locusDir = join14(projectPath, LOCUS_CONFIG.dir);
|
|
42777
|
+
const configPath = join14(locusDir, LOCUS_CONFIG.configFile);
|
|
43160
42778
|
return existsSync13(locusDir) && existsSync13(configPath);
|
|
43161
42779
|
}
|
|
43162
42780
|
function requireInitialization(projectPath, command) {
|
|
@@ -43710,7 +43328,6 @@ async function execCommand(args) {
|
|
|
43710
43328
|
});
|
|
43711
43329
|
const builder = new PromptBuilder(projectPath);
|
|
43712
43330
|
const fullPrompt = await builder.buildGenericPrompt(promptInput);
|
|
43713
|
-
const knowledgeBase = new KnowledgeBase(projectPath);
|
|
43714
43331
|
console.log("");
|
|
43715
43332
|
console.log(`${c.primary("\uD83D\uDE80")} ${c.bold("Executing prompt with repository context...")}`);
|
|
43716
43333
|
console.log("");
|
|
@@ -43765,15 +43382,6 @@ async function execCommand(args) {
|
|
|
43765
43382
|
responseContent = await aiRunner.run(fullPrompt);
|
|
43766
43383
|
console.log(responseContent);
|
|
43767
43384
|
}
|
|
43768
|
-
try {
|
|
43769
|
-
knowledgeBase.updateProgress({ role: "user", content: promptInput });
|
|
43770
|
-
if (responseContent.trim()) {
|
|
43771
|
-
knowledgeBase.updateProgress({
|
|
43772
|
-
role: "assistant",
|
|
43773
|
-
content: responseContent.trim()
|
|
43774
|
-
});
|
|
43775
|
-
}
|
|
43776
|
-
} catch {}
|
|
43777
43385
|
console.log(`
|
|
43778
43386
|
${c.success("✔")} ${c.success("Execution finished!")}
|
|
43779
43387
|
`);
|
|
@@ -43829,13 +43437,6 @@ async function execJsonStream(values, positionals, projectPath) {
|
|
|
43829
43437
|
for await (const chunk of stream4) {
|
|
43830
43438
|
renderer.handleChunk(chunk);
|
|
43831
43439
|
}
|
|
43832
|
-
try {
|
|
43833
|
-
const knowledgeBase = new KnowledgeBase(projectPath);
|
|
43834
|
-
knowledgeBase.updateProgress({
|
|
43835
|
-
role: "user",
|
|
43836
|
-
content: promptInput
|
|
43837
|
-
});
|
|
43838
|
-
} catch {}
|
|
43839
43440
|
renderer.emitDone(0);
|
|
43840
43441
|
} catch (error48) {
|
|
43841
43442
|
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
@@ -44018,9 +43619,8 @@ async function initCommand() {
|
|
|
44018
43619
|
${c.bold("Created:")}
|
|
44019
43620
|
${c.primary("\uD83D\uDCC1")} ${c.bold(".locus/")} ${c.dim("Configuration directory")}
|
|
44020
43621
|
${c.primary("\uD83D\uDCC4")} ${c.bold(".locus/config.json")} ${c.dim("Project settings")}
|
|
44021
|
-
${c.primary("\uD83D\uDCC4")} ${c.bold(".locus/project/context.md")} ${c.dim("Project context & knowledge")}
|
|
44022
|
-
${c.primary("\uD83D\uDCC4")} ${c.bold(".locus/project/progress.md")} ${c.dim("Sprint progress tracking")}
|
|
44023
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")}
|
|
44024
43624
|
|
|
44025
43625
|
${c.bold("Next steps:")}
|
|
44026
43626
|
1. Run '${c.primary("locus config setup")}' to configure your API key
|
|
@@ -44032,6 +43632,8 @@ async function initCommand() {
|
|
|
44032
43632
|
}
|
|
44033
43633
|
// src/commands/plan.ts
|
|
44034
43634
|
init_index_node();
|
|
43635
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync4 } from "node:fs";
|
|
43636
|
+
import { join as join15 } from "node:path";
|
|
44035
43637
|
import { parseArgs as parseArgs4 } from "node:util";
|
|
44036
43638
|
function normalizePlanIdArgs(args) {
|
|
44037
43639
|
const planIdFlags = new Set(["--approve", "--reject", "--cancel", "--show"]);
|
|
@@ -44138,6 +43740,10 @@ async function planCommand(args) {
|
|
|
44138
43740
|
try {
|
|
44139
43741
|
const result = await meeting.run(directive, feedback);
|
|
44140
43742
|
planManager.save(result.plan);
|
|
43743
|
+
const tempFile = join15(getLocusPath(projectPath, "plansDir"), `${result.plan.id}.json`);
|
|
43744
|
+
if (existsSync14(tempFile)) {
|
|
43745
|
+
unlinkSync4(tempFile);
|
|
43746
|
+
}
|
|
44141
43747
|
console.log(`
|
|
44142
43748
|
${c.success("✔")} ${c.success("Planning meeting complete!")}
|
|
44143
43749
|
`);
|
|
@@ -44332,8 +43938,8 @@ function showPlanHelp() {
|
|
|
44332
43938
|
}
|
|
44333
43939
|
// src/commands/review.ts
|
|
44334
43940
|
init_index_node();
|
|
44335
|
-
import { existsSync as
|
|
44336
|
-
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";
|
|
44337
43943
|
import { parseArgs as parseArgs5 } from "node:util";
|
|
44338
43944
|
async function reviewCommand(args) {
|
|
44339
43945
|
const subcommand = args[0];
|
|
@@ -44472,13 +44078,13 @@ async function reviewLocalCommand(args) {
|
|
|
44472
44078
|
`);
|
|
44473
44079
|
return;
|
|
44474
44080
|
}
|
|
44475
|
-
const reviewsDir =
|
|
44476
|
-
if (!
|
|
44081
|
+
const reviewsDir = join16(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.reviewsDir);
|
|
44082
|
+
if (!existsSync15(reviewsDir)) {
|
|
44477
44083
|
mkdirSync7(reviewsDir, { recursive: true });
|
|
44478
44084
|
}
|
|
44479
44085
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
44480
|
-
const reportPath =
|
|
44481
|
-
|
|
44086
|
+
const reportPath = join16(reviewsDir, `review-${timestamp}.md`);
|
|
44087
|
+
writeFileSync7(reportPath, report, "utf-8");
|
|
44482
44088
|
console.log(`
|
|
44483
44089
|
${c.success("✔")} ${c.success("Review complete!")}`);
|
|
44484
44090
|
console.log(` ${c.dim("Report saved to:")} ${c.primary(reportPath)}
|
|
@@ -44570,8 +44176,8 @@ ${c.info(`Received ${signal}. Stopping agent and cleaning up...`)}`);
|
|
|
44570
44176
|
// src/commands/telegram.ts
|
|
44571
44177
|
init_index_node();
|
|
44572
44178
|
import { spawn as spawn4 } from "node:child_process";
|
|
44573
|
-
import { existsSync as
|
|
44574
|
-
import { join as
|
|
44179
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
44180
|
+
import { join as join17 } from "node:path";
|
|
44575
44181
|
import { createInterface as createInterface3 } from "node:readline";
|
|
44576
44182
|
function ask2(question) {
|
|
44577
44183
|
const rl = createInterface3({
|
|
@@ -44805,8 +44411,8 @@ function runBotCommand(projectPath) {
|
|
|
44805
44411
|
`);
|
|
44806
44412
|
process.exit(1);
|
|
44807
44413
|
}
|
|
44808
|
-
const monorepoTelegramEntry =
|
|
44809
|
-
const isMonorepo =
|
|
44414
|
+
const monorepoTelegramEntry = join17(projectPath, "packages/telegram/src/index.ts");
|
|
44415
|
+
const isMonorepo = existsSync16(monorepoTelegramEntry);
|
|
44810
44416
|
let cmd;
|
|
44811
44417
|
let args;
|
|
44812
44418
|
if (isMonorepo) {
|