@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.
Files changed (3) hide show
  1. package/bin/agent/worker.js +135 -348
  2. package/bin/locus.js +458 -904
  3. 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 codeBlockMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
6324
- if (codeBlockMatch) {
6325
- return codeBlockMatch[1]?.trim() || "";
6326
- }
6327
- const startIdx = trimmed.indexOf("{");
6328
- if (startIdx === -1) {
6329
- return trimmed;
6330
- }
6331
- let depth = 0;
6332
- let inString = false;
6333
- let escaped = false;
6334
- for (let i = startIdx;i < trimmed.length; i++) {
6335
- const ch = trimmed[i];
6336
- if (escaped) {
6337
- escaped = false;
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
- escaped = true;
6343
- } else if (ch === '"') {
6344
- inString = false;
6339
+ isEscape = true;
6340
+ continue;
6345
6341
  }
6346
- continue;
6347
- }
6348
- if (ch === '"') {
6349
- inString = true;
6350
- } else if (ch === "{") {
6351
- depth++;
6352
- } else if (ch === "}") {
6353
- depth--;
6354
- if (depth === 0) {
6355
- return trimmed.slice(startIdx, i + 1);
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.slice(startIdx);
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, progress, etc.)",
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
- async* runStream(prompt) {
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, timeoutMs, reasoningEffort) {
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.timeoutMs, config.reasoningEffort ?? "high");
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 existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync } from "node:fs";
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, options = {}) {
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
- prompt += `## Role
38956
+ sections += `
38957
+ <role>
39082
38958
  You are acting as a ${roleText}.
39083
-
38959
+ </role>
39084
38960
  `;
39085
38961
  }
39086
- prompt += `## Description
39087
- ${task2.description || "No description provided."}
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
- if (!hasLocalContext) {
39125
- const fallback = this.getFallbackContext();
39126
- if (fallback) {
39127
- prompt += `## Project Context (README Fallback)
39128
- ${fallback}
39129
-
38969
+ sections += `
38970
+ <knowledge_base>
38971
+ ${knowledgeBase}
38972
+ </knowledge_base>
39130
38973
  `;
39131
- }
39132
- }
39133
- if (serverContext) {
39134
- prompt += `## Project Context (Server)
39135
- `;
39136
- const project = serverContext.project;
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
- prompt += `## Attached Documents (Summarized)
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
- prompt += `### Doc: ${doc3.title}
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
- prompt += `## Acceptance Criteria
39192
- `;
39002
+ let criteria = "";
39193
39003
  for (const item of task2.acceptanceChecklist) {
39194
- prompt += `- ${item.done ? "[x]" : "[ ]"} ${item.text}
39004
+ criteria += `- ${item.done ? "[x]" : "[ ]"} ${item.text}
39195
39005
  `;
39196
39006
  }
39197
- prompt += `
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 comments = task2.comments.slice(0, 3);
39202
- prompt += `## Task History & Feedback
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
- prompt += `Review the following comments for context or rejection feedback:
39205
-
39206
- `;
39207
- for (const comment of comments) {
39208
- const date5 = new Date(comment.createdAt).toLocaleString();
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
- prompt += `## Instructions
39216
- 1. Complete this task.
39217
- 2. **Artifact Management**: If you create any high-level documentation (PRDs, technical drafts, architecture docs), you MUST save them in \`.locus/artifacts/\`. Do NOT create them in the root directory.
39218
- 3. **Paths**: Use relative paths from the project root at all times. Do NOT use absolute local paths (e.g., /Users/...).
39219
- 4. **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.
39220
- 5. **Progress**: Do NOT modify \`.locus/project/progress.md\`. The system updates it automatically.`;
39221
- return prompt;
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
- let prompt = `# Direct Execution
39225
-
39226
- `;
39227
- prompt += `## Prompt
39228
- ${query}
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
- const projectConfig = this.getProjectConfig();
39232
- if (projectConfig) {
39233
- prompt += `## Project Metadata
39056
+ }
39057
+ sections += `
39058
+ <knowledge_base>
39059
+ ${knowledgeBase}
39060
+ </knowledge_base>
39234
39061
  `;
39235
- prompt += `- Version: ${projectConfig.version || "Unknown"}
39236
- `;
39237
- prompt += `- Created At: ${projectConfig.createdAt || "Unknown"}
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
- let hasLocalContext = false;
39243
- if (existsSync6(contextPath)) {
39082
+ if (existsSync5(contextPath)) {
39244
39083
  try {
39245
- const context2 = readFileSync6(contextPath, "utf-8");
39084
+ const context2 = readFileSync5(contextPath, "utf-8");
39246
39085
  if (context2.trim().length > 20) {
39247
- prompt += `## Project Context (Local)
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
- if (!hasLocalContext) {
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 (existsSync6(readmePath)) {
39096
+ if (existsSync5(readmePath)) {
39306
39097
  try {
39307
- const content = readFileSync6(readmePath, "utf-8");
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
- getProjectStructure() {
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 entries = readdirSync2(this.projectPath);
39320
- const folders = entries.filter((e) => {
39321
- if (e.startsWith(".") || e === "node_modules")
39322
- return false;
39323
- try {
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 `${structure}
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: this.config.agentId,
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: this.config.agentId,
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: this.config.agentId,
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 existsSync7,
40177
- mkdirSync as mkdirSync4,
40178
- readdirSync as readdirSync3,
40179
- readFileSync as readFileSync7,
39945
+ existsSync as existsSync6,
39946
+ mkdirSync as mkdirSync3,
39947
+ readdirSync as readdirSync2,
39948
+ readFileSync as readFileSync6,
40180
39949
  rmSync,
40181
- writeFileSync as writeFileSync4
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 (!existsSync7(this.historyDir)) {
40200
- mkdirSync4(this.historyDir, { recursive: true });
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
- writeFileSync4(filePath, JSON.stringify(session2, null, 2), "utf-8");
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 (!existsSync7(filePath)) {
39982
+ if (!existsSync6(filePath)) {
40214
39983
  return null;
40215
39984
  }
40216
39985
  try {
40217
- const content = readFileSync7(filePath, "utf-8");
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 (!existsSync7(filePath)) {
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 = readdirSync3(this.historyDir);
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 = readdirSync3(this.historyDir);
40078
+ const files = readdirSync2(this.historyDir);
40310
40079
  return files.filter((f) => f.endsWith(".json")).length;
40311
40080
  }
40312
40081
  sessionExists(sessionId) {
40313
- return existsSync7(this.getSessionPath(sessionId));
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 = readdirSync3(this.historyDir);
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 existsSync8 } from "node:fs";
40622
- import { dirname as dirname3, join as join8 } from "node:path";
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 = dirname3(currentModulePath);
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) => existsSync8(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
- function parseSprintPlanFromAI(raw, directive) {
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 existsSync9,
41044
- mkdirSync as mkdirSync5,
41045
- readdirSync as readdirSync4,
41046
- readFileSync as readFileSync8,
40797
+ existsSync as existsSync8,
40798
+ mkdirSync as mkdirSync4,
40799
+ readdirSync as readdirSync3,
40800
+ readFileSync as readFileSync7,
41047
40801
  unlinkSync as unlinkSync2,
41048
- writeFileSync as writeFileSync5
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
- writeFileSync5(jsonPath, JSON.stringify(plan, null, 2), "utf-8");
41065
- writeFileSync5(mdPath, sprintPlanToMarkdown(plan), "utf-8");
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 = readdirSync4(this.plansDir).filter((f) => f.endsWith(".json"));
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(readFileSync8(filePath, "utf-8"));
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 = readdirSync4(this.plansDir).filter((f) => f.endsWith(".json"));
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(readFileSync8(join9(this.plansDir, file2), "utf-8"));
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 = readdirSync4(this.plansDir);
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(readFileSync8(filePath, "utf-8"));
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 (existsSync9(mdPath)) {
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 (!existsSync9(this.plansDir)) {
41180
- mkdirSync5(this.plansDir, { recursive: true });
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 prompt = `# Role: Sprint Planner
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
- prompt += `
41318
- ## CEO Feedback on Previous Plan
41319
- > ${input.feedback}
41320
-
41321
- IMPORTANT: Incorporate this feedback into your plan. The CEO has reviewed a previous plan and wants changes.
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
- prompt += `
41325
- ## Project Context
41326
- ${input.projectContext || "No project context available."}
41327
-
41328
- ## Codebase Structure
41329
- ${input.codebaseIndex || "No codebase index available."}
41330
-
41331
- ## Your Task
41332
-
41333
- Analyze the directive and produce a **complete sprint plan** with the following:
41334
-
41335
- 1. **Sprint Name** A concise, memorable name (2-4 words)
41336
- 2. **Sprint Goal** One paragraph describing what this sprint delivers
41337
- 3. **Duration Estimate** — How many days this sprint will take with a single agent working sequentially
41338
- 4. **Task Breakdown** — An ordered list of tasks that fully implement the directive
41339
- 5. **Risk Assessment** — Potential risks with mitigations
41340
-
41341
- ### Task Requirements
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
- "name": "string (2-4 words)",
41386
- "goal": "string (1 paragraph)",
41387
- "estimatedDays": 3,
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
- "title": "string",
41391
- "description": "string (detailed implementation guide: what, where, how, boundaries)",
41392
- "assigneeRole": "BACKEND | FRONTEND | QA | PM | DESIGN",
41393
- "priority": "CRITICAL | HIGH | MEDIUM | LOW",
41394
- "labels": ["string"],
41395
- "acceptanceCriteria": ["string"],
41396
- "complexity": 3
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": "string",
41402
- "mitigation": "string",
41403
- "severity": "low | medium | high"
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: Tasks are executed sequentially by a single agent. The array order IS the execution order — ensure foundational work comes first and dependent tasks come after their prerequisites.`;
41409
- return prompt;
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 existsSync10, readFileSync as readFileSync9 } from "node:fs";
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
- const projectContext = this.getProjectContext();
41428
- const codebaseIndex = this.getCodebaseIndex();
41429
- this.log("Phase 1/2: Planner building sprint plan...", "info");
41430
- const plannerPrompt = buildPlannerPrompt({
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
- projectContext,
41433
- codebaseIndex,
41434
- feedback
41030
+ feedback,
41031
+ plansDir,
41032
+ planId,
41033
+ fileName
41435
41034
  });
41436
- const plannerOutput = await this.aiRunner.run(plannerPrompt);
41437
- this.log("Planner phase complete.", "success");
41438
- this.log("Phase 2/2: Reviewer checking for conflicts and quality...", "info");
41439
- const crossTaskReviewerPrompt = buildCrossTaskReviewerPrompt({
41440
- directive,
41441
- projectContext,
41442
- plannerOutput,
41443
- feedback
41444
- });
41445
- const reviewOutput = await this.aiRunner.run(crossTaskReviewerPrompt);
41446
- this.log("Review phase complete.", "success");
41447
- const plan = parseSprintPlanFromAI(reviewOutput, directive);
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
- phaseOutputs: {
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 existsSync11, readFileSync as readFileSync10, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
42574
- import { join as join10 } from "node:path";
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 join10(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
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 (!existsSync11(settingsPath)) {
42137
+ if (!existsSync10(settingsPath)) {
42587
42138
  return {};
42588
42139
  }
42589
- return JSON.parse(readFileSync10(settingsPath, "utf-8"));
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
- writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
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 (existsSync11(settingsPath)) {
42158
+ if (existsSync10(settingsPath)) {
42608
42159
  unlinkSync3(settingsPath);
42609
42160
  }
42610
42161
  }
42611
42162
  exists() {
42612
- return existsSync11(getSettingsPath(this.projectPath));
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 existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
42879
- import { join as join11 } from "node:path";
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 DEFAULT_CONTEXT_MD = `# Project
42882
-
42883
- ## Mission
42884
- <!-- Describe your project's core purpose and value proposition -->
42432
+ var LOCUS_MD_TEMPLATE = `## Planning First
42885
42433
 
42886
- ## Tech Stack
42887
- <!-- List your technologies -->
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
- ## Architecture
42890
- <!-- Describe your high-level architecture -->
42441
+ Delete the planning .md files after successful execution.
42891
42442
 
42892
- ## Key Decisions
42893
- <!-- Document important technical decisions and their rationale -->
42443
+ ## Code Quality
42894
42444
 
42895
- ## Feature Areas
42896
- <!-- List your main feature areas and their status -->
42897
- `;
42898
- var DEFAULT_PROGRESS_MD = `# Project Progress
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
- No sprints started yet.
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 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":
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 summaries
42909
- - Any research, comparison, or investigation the user requests
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
- **Rules:**
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
- ## Planning First
42477
+ Read ".locus/LEARNINGS.md" **before starting any task** to avoid repeating mistakes.
42918
42478
 
42919
- Complex tasks must be planned before writing code. Create \`.locus/plans/<task-name>.md\` with: goal, approach, affected files, and acceptance criteria. Delete the planning .md files after the execution.
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
- ## Code
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
- - Follow the existing formatter, linter, and code style. Run them before finishing.
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
- ## Git
42494
+ """
42495
+ - **[Category]**: Concise description (1-2 lines max). *Context if needed.*
42496
+ """
42929
42497
 
42930
- - Do NOT run \`git add\`, \`git commit\`, \`git push\`, or create branches.
42931
- - The Locus system handles all git operations (commit, push, PR creation) automatically after your execution completes.
42932
- - Focus only on making file changes — the orchestrator takes care of version control.
42933
- - Do NOT modify \`.locus/project/progress.md\`. The system updates it automatically. Changes to this file are excluded from commits to prevent merge conflicts across concurrent agents.
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
- ## Avoiding Hallucinated / Slop Code
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
- - Ask before assuming. If requirements are ambiguous, incomplete, or could be interpreted multiple ways, stop and ask clarifying questions rather than guessing.
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 = join11(projectPath, ".gitignore");
42522
+ const gitignorePath = join12(projectPath, ".gitignore");
42946
42523
  let content = "";
42947
42524
  const locusBlock = LOCUS_GITIGNORE_PATTERNS.join(`
42948
42525
  `);
42949
- if (existsSync12(gitignorePath)) {
42950
- content = readFileSync11(gitignorePath, "utf-8");
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
- writeFileSync7(gitignorePath, content);
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
- writeFileSync7(gitignorePath, content);
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 = join11(this.projectPath, LOCUS_CONFIG.dir);
42606
+ const locusConfigDir = join12(this.projectPath, LOCUS_CONFIG.dir);
43030
42607
  const locusConfigPath = getLocusPath(this.projectPath, "configFile");
43031
- if (!existsSync12(locusConfigDir)) {
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 = join11(locusConfigDir, subdir);
43044
- if (!existsSync12(subdirPath)) {
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 (!existsSync12(locusMdPath)) {
43058
- writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
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 (!existsSync12(locusConfigPath)) {
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
- writeFileSync7(locusConfigPath, JSON.stringify(config2, null, 2));
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 (existsSync12(path2)) {
43075
- return JSON.parse(readFileSync11(path2, "utf-8"));
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 = join11(this.projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.settingsFile);
43108
- if (existsSync12(settingsPath)) {
43109
- const raw = readFileSync11(settingsPath, "utf-8");
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
- writeFileSync7(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
42684
+ writeFileSync6(settingsPath, JSON.stringify(ordered, null, 2), "utf-8");
43115
42685
  }
43116
42686
  }
43117
- const locusMdExisted = existsSync12(locusMdPath);
43118
- writeFileSync7(locusMdPath, LOCUS_MD_TEMPLATE);
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 = join11(locusConfigDir, subdir);
43132
- if (!existsSync12(subdirPath)) {
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 contextFilePath = getLocusPath(this.projectPath, "projectContextFile");
43138
- if (!existsSync12(contextFilePath)) {
43139
- writeFileSync7(contextFilePath, DEFAULT_CONTEXT_MD);
43140
- result.directoriesCreated.push(".locus/project/context.md");
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 = join11(this.projectPath, ".gitignore");
43148
- const gitignoreBefore = existsSync12(gitignorePath) ? readFileSync11(gitignorePath, "utf-8") : "";
42713
+ const gitignorePath = join12(this.projectPath, ".gitignore");
42714
+ const gitignoreBefore = existsSync11(gitignorePath) ? readFileSync10(gitignorePath, "utf-8") : "";
43149
42715
  updateGitignore(this.projectPath);
43150
- const gitignoreAfter = readFileSync11(gitignorePath, "utf-8");
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
- writeFileSync7(path2, JSON.stringify(ordered, null, 2));
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 existsSync13, readFileSync as readFileSync12 } from "node:fs";
43170
- import { dirname as dirname4, join as join12 } from "node:path";
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 = dirname4(__filename2);
43176
- const bundledPath = join12(__dirname2, "..", "package.json");
43177
- const sourcePath = join12(__dirname2, "..", "..", "package.json");
43178
- if (existsSync13(bundledPath)) {
43179
- const pkg = JSON.parse(readFileSync12(bundledPath, "utf-8"));
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 (existsSync13(sourcePath)) {
43185
- const pkg = JSON.parse(readFileSync12(sourcePath, "utf-8"));
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 existsSync14 } from "node:fs";
43208
- import { join as join13 } from "node:path";
42773
+ import { existsSync as existsSync13 } from "node:fs";
42774
+ import { join as join14 } from "node:path";
43209
42775
  function isProjectInitialized(projectPath) {
43210
- const locusDir = join13(projectPath, LOCUS_CONFIG.dir);
43211
- const configPath = join13(locusDir, LOCUS_CONFIG.configFile);
43212
- return existsSync14(locusDir) && existsSync14(configPath);
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 writeFileSync8 } from "node:fs";
44388
- import { join as join14 } from "node:path";
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 = join14(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.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 = join14(reviewsDir, `review-${timestamp}.md`);
44533
- writeFileSync8(reportPath, report, "utf-8");
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 join15 } from "node:path";
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 = join15(projectPath, "packages/telegram/src/index.ts");
44414
+ const monorepoTelegramEntry = join17(projectPath, "packages/telegram/src/index.ts");
44861
44415
  const isMonorepo = existsSync16(monorepoTelegramEntry);
44862
44416
  let cmd;
44863
44417
  let args;