@locusai/sdk 0.12.1 → 0.13.2

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.
@@ -391,6 +391,39 @@ var init_workspaces = __esm(() => {
391
391
  };
392
392
  });
393
393
 
394
+ // src/discussion/discussion-types.ts
395
+ var import_zod, DiscussionMessageSchema, DiscussionInsightSchema, DiscussionSchema;
396
+ var init_discussion_types = __esm(() => {
397
+ import_zod = require("zod");
398
+ DiscussionMessageSchema = import_zod.z.object({
399
+ role: import_zod.z.enum(["user", "assistant"]),
400
+ content: import_zod.z.string(),
401
+ timestamp: import_zod.z.number()
402
+ });
403
+ DiscussionInsightSchema = import_zod.z.object({
404
+ id: import_zod.z.string(),
405
+ type: import_zod.z.enum(["decision", "requirement", "idea", "concern", "learning"]),
406
+ title: import_zod.z.string(),
407
+ content: import_zod.z.string(),
408
+ tags: import_zod.z.array(import_zod.z.string()).default([]),
409
+ createdAt: import_zod.z.string()
410
+ });
411
+ DiscussionSchema = import_zod.z.object({
412
+ id: import_zod.z.string(),
413
+ title: import_zod.z.string(),
414
+ topic: import_zod.z.string(),
415
+ status: import_zod.z.enum(["active", "completed", "archived"]).default("active"),
416
+ messages: import_zod.z.array(DiscussionMessageSchema).default([]),
417
+ insights: import_zod.z.array(DiscussionInsightSchema).default([]),
418
+ createdAt: import_zod.z.string(),
419
+ updatedAt: import_zod.z.string(),
420
+ metadata: import_zod.z.object({
421
+ model: import_zod.z.string(),
422
+ provider: import_zod.z.string()
423
+ })
424
+ });
425
+ });
426
+
394
427
  // src/index.ts
395
428
  var exports_src = {};
396
429
  __export(exports_src, {
@@ -403,6 +436,9 @@ __export(exports_src, {
403
436
  LocusClient: () => LocusClient,
404
437
  InvitationsModule: () => InvitationsModule,
405
438
  DocsModule: () => DocsModule,
439
+ DiscussionSchema: () => DiscussionSchema,
440
+ DiscussionMessageSchema: () => DiscussionMessageSchema,
441
+ DiscussionInsightSchema: () => DiscussionInsightSchema,
406
442
  CiModule: () => CiModule,
407
443
  AuthModule: () => AuthModule
408
444
  });
@@ -508,6 +544,7 @@ var init_src = __esm(() => {
508
544
  init_sprints();
509
545
  init_tasks();
510
546
  init_workspaces();
547
+ init_discussion_types();
511
548
  import_axios = __toESM(require("axios"));
512
549
  init_events();
513
550
  init_auth();
@@ -554,7 +591,8 @@ var init_config = __esm(() => {
554
591
  documentsDir: "documents",
555
592
  sessionsDir: "sessions",
556
593
  reviewsDir: "reviews",
557
- plansDir: "plans"
594
+ plansDir: "plans",
595
+ discussionsDir: "discussions"
558
596
  };
559
597
  LOCUS_GITIGNORE_PATTERNS = [
560
598
  "# Locus AI - Session data (user-specific, can grow large)",
@@ -569,6 +607,9 @@ var init_config = __esm(() => {
569
607
  "# Locus AI - Plans (generated per task)",
570
608
  ".locus/plans/",
571
609
  "",
610
+ "# Locus AI - Discussions (AI discussion sessions)",
611
+ ".locus/discussions/",
612
+ "",
572
613
  "# Locus AI - Settings (contains API key, telegram config, etc.)",
573
614
  ".locus/settings.json",
574
615
  "",
@@ -1871,6 +1912,187 @@ var init_git_workflow = __esm(() => {
1871
1912
  import_node_child_process4 = require("node:child_process");
1872
1913
  });
1873
1914
 
1915
+ // src/discussion/discussion-manager.ts
1916
+ class DiscussionManager {
1917
+ discussionsDir;
1918
+ constructor(projectPath) {
1919
+ this.discussionsDir = getLocusPath(projectPath, "discussionsDir");
1920
+ }
1921
+ create(topic, model, provider) {
1922
+ this.ensureDir();
1923
+ const now = new Date().toISOString();
1924
+ const id = `disc-${Date.now()}`;
1925
+ const discussion = {
1926
+ id,
1927
+ title: topic,
1928
+ topic,
1929
+ status: "active",
1930
+ messages: [],
1931
+ insights: [],
1932
+ createdAt: now,
1933
+ updatedAt: now,
1934
+ metadata: { model, provider }
1935
+ };
1936
+ this.save(discussion);
1937
+ return discussion;
1938
+ }
1939
+ save(discussion) {
1940
+ this.ensureDir();
1941
+ const jsonPath = import_node_path5.join(this.discussionsDir, `${discussion.id}.json`);
1942
+ const mdPath = import_node_path5.join(this.discussionsDir, `summary-${discussion.id}.md`);
1943
+ import_node_fs3.writeFileSync(jsonPath, JSON.stringify(discussion, null, 2), "utf-8");
1944
+ import_node_fs3.writeFileSync(mdPath, this.toMarkdown(discussion), "utf-8");
1945
+ }
1946
+ load(id) {
1947
+ this.ensureDir();
1948
+ const filePath = import_node_path5.join(this.discussionsDir, `${id}.json`);
1949
+ if (!import_node_fs3.existsSync(filePath)) {
1950
+ return null;
1951
+ }
1952
+ try {
1953
+ return JSON.parse(import_node_fs3.readFileSync(filePath, "utf-8"));
1954
+ } catch {
1955
+ return null;
1956
+ }
1957
+ }
1958
+ list(status) {
1959
+ this.ensureDir();
1960
+ const files = import_node_fs3.readdirSync(this.discussionsDir).filter((f) => f.endsWith(".json"));
1961
+ const discussions = [];
1962
+ for (const file of files) {
1963
+ try {
1964
+ const discussion = JSON.parse(import_node_fs3.readFileSync(import_node_path5.join(this.discussionsDir, file), "utf-8"));
1965
+ if (!status || discussion.status === status) {
1966
+ discussions.push(discussion);
1967
+ }
1968
+ } catch {}
1969
+ }
1970
+ discussions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
1971
+ return discussions;
1972
+ }
1973
+ complete(id) {
1974
+ const discussion = this.load(id);
1975
+ if (!discussion) {
1976
+ throw new Error(`Discussion not found: ${id}`);
1977
+ }
1978
+ discussion.status = "completed";
1979
+ discussion.updatedAt = new Date().toISOString();
1980
+ this.save(discussion);
1981
+ return discussion;
1982
+ }
1983
+ archive(id) {
1984
+ const discussion = this.load(id);
1985
+ if (!discussion) {
1986
+ throw new Error(`Discussion not found: ${id}`);
1987
+ }
1988
+ discussion.status = "archived";
1989
+ discussion.updatedAt = new Date().toISOString();
1990
+ this.save(discussion);
1991
+ }
1992
+ delete(id) {
1993
+ this.ensureDir();
1994
+ const jsonPath = import_node_path5.join(this.discussionsDir, `${id}.json`);
1995
+ const mdPath = import_node_path5.join(this.discussionsDir, `summary-${id}.md`);
1996
+ if (import_node_fs3.existsSync(jsonPath)) {
1997
+ import_node_fs3.unlinkSync(jsonPath);
1998
+ }
1999
+ if (import_node_fs3.existsSync(mdPath)) {
2000
+ import_node_fs3.unlinkSync(mdPath);
2001
+ }
2002
+ }
2003
+ addMessage(id, role, content) {
2004
+ const discussion = this.load(id);
2005
+ if (!discussion) {
2006
+ throw new Error(`Discussion not found: ${id}`);
2007
+ }
2008
+ discussion.messages.push({
2009
+ role,
2010
+ content,
2011
+ timestamp: Date.now()
2012
+ });
2013
+ discussion.updatedAt = new Date().toISOString();
2014
+ this.save(discussion);
2015
+ return discussion;
2016
+ }
2017
+ addInsight(id, insight) {
2018
+ const discussion = this.load(id);
2019
+ if (!discussion) {
2020
+ throw new Error(`Discussion not found: ${id}`);
2021
+ }
2022
+ discussion.insights.push(insight);
2023
+ discussion.updatedAt = new Date().toISOString();
2024
+ this.save(discussion);
2025
+ return discussion;
2026
+ }
2027
+ getAllInsights() {
2028
+ const discussions = this.list("completed");
2029
+ const insights = [];
2030
+ for (const discussion of discussions) {
2031
+ insights.push(...discussion.insights);
2032
+ }
2033
+ insights.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
2034
+ return insights;
2035
+ }
2036
+ getMarkdown(id) {
2037
+ const discussion = this.load(id);
2038
+ if (!discussion)
2039
+ return null;
2040
+ return this.toMarkdown(discussion);
2041
+ }
2042
+ toMarkdown(discussion) {
2043
+ const lines = [];
2044
+ lines.push(`# Discussion: ${discussion.title}`);
2045
+ lines.push("");
2046
+ lines.push(`**Status:** ${discussion.status.toUpperCase()}`);
2047
+ lines.push(`**Topic:** ${discussion.topic}`);
2048
+ lines.push(`**Created:** ${discussion.createdAt}`);
2049
+ lines.push(`**Updated:** ${discussion.updatedAt}`);
2050
+ lines.push(`**Model:** ${discussion.metadata.model} (${discussion.metadata.provider})`);
2051
+ lines.push("");
2052
+ if (discussion.messages.length > 0) {
2053
+ lines.push(`## Messages (${discussion.messages.length})`);
2054
+ lines.push("");
2055
+ for (const msg of discussion.messages) {
2056
+ const time = new Date(msg.timestamp).toISOString();
2057
+ const roleLabel = msg.role === "user" ? "User" : "Assistant";
2058
+ lines.push(`### ${roleLabel} — ${time}`);
2059
+ lines.push("");
2060
+ lines.push(msg.content);
2061
+ lines.push("");
2062
+ }
2063
+ }
2064
+ if (discussion.insights.length > 0) {
2065
+ lines.push(`## Insights (${discussion.insights.length})`);
2066
+ lines.push("");
2067
+ for (const insight of discussion.insights) {
2068
+ lines.push(`### [${insight.type.toUpperCase()}] ${insight.title}`);
2069
+ lines.push("");
2070
+ lines.push(insight.content);
2071
+ if (insight.tags.length > 0) {
2072
+ lines.push("");
2073
+ lines.push(`**Tags:** ${insight.tags.join(", ")}`);
2074
+ }
2075
+ lines.push("");
2076
+ }
2077
+ }
2078
+ lines.push("---");
2079
+ lines.push(`*Discussion ID: ${discussion.id}*`);
2080
+ return lines.join(`
2081
+ `);
2082
+ }
2083
+ ensureDir() {
2084
+ if (!import_node_fs3.existsSync(this.discussionsDir)) {
2085
+ import_node_fs3.mkdirSync(this.discussionsDir, { recursive: true });
2086
+ }
2087
+ }
2088
+ }
2089
+ var import_node_fs3, import_node_path5;
2090
+ var init_discussion_manager = __esm(() => {
2091
+ init_config();
2092
+ import_node_fs3 = require("node:fs");
2093
+ import_node_path5 = require("node:path");
2094
+ });
2095
+
1874
2096
  // src/core/prompt-builder.ts
1875
2097
  class PromptBuilder {
1876
2098
  projectPath;
@@ -1909,6 +2131,15 @@ ${knowledgeBase}
1909
2131
  These are accumulated lessons from past tasks. Follow them to avoid repeating mistakes:
1910
2132
  ${learnings}
1911
2133
  </learnings>
2134
+ `;
2135
+ }
2136
+ const discussionInsights = this.getDiscussionInsightsContent();
2137
+ if (discussionInsights) {
2138
+ sections += `
2139
+ <discussion_insights>
2140
+ These are key decisions and insights from product discussions. Follow them to maintain product coherence:
2141
+ ${discussionInsights}
2142
+ </discussion_insights>
1912
2143
  `;
1913
2144
  }
1914
2145
  if (task.docs && task.docs.length > 0) {
@@ -1997,6 +2228,15 @@ ${knowledgeBase}
1997
2228
  These are accumulated lessons from past tasks. Follow them to avoid repeating mistakes:
1998
2229
  ${learnings}
1999
2230
  </learnings>
2231
+ `;
2232
+ }
2233
+ const discussionInsights = this.getDiscussionInsightsContent();
2234
+ if (discussionInsights) {
2235
+ sections += `
2236
+ <discussion_insights>
2237
+ These are key decisions and insights from product discussions. Follow them to maintain product coherence:
2238
+ ${discussionInsights}
2239
+ </discussion_insights>
2000
2240
  `;
2001
2241
  }
2002
2242
  return `<direct_execution>
@@ -2011,9 +2251,9 @@ ${sections}
2011
2251
  }
2012
2252
  getProjectContext() {
2013
2253
  const contextPath = getLocusPath(this.projectPath, "contextFile");
2014
- if (import_node_fs3.existsSync(contextPath)) {
2254
+ if (import_node_fs4.existsSync(contextPath)) {
2015
2255
  try {
2016
- const context = import_node_fs3.readFileSync(contextPath, "utf-8");
2256
+ const context = import_node_fs4.readFileSync(contextPath, "utf-8");
2017
2257
  if (context.trim().length > 20) {
2018
2258
  return context;
2019
2259
  }
@@ -2024,10 +2264,10 @@ ${sections}
2024
2264
  return this.getFallbackContext() || null;
2025
2265
  }
2026
2266
  getFallbackContext() {
2027
- const readmePath = import_node_path5.join(this.projectPath, "README.md");
2028
- if (import_node_fs3.existsSync(readmePath)) {
2267
+ const readmePath = import_node_path6.join(this.projectPath, "README.md");
2268
+ if (import_node_fs4.existsSync(readmePath)) {
2029
2269
  try {
2030
- const content = import_node_fs3.readFileSync(readmePath, "utf-8");
2270
+ const content = import_node_fs4.readFileSync(readmePath, "utf-8");
2031
2271
  const limit = 1000;
2032
2272
  return content.slice(0, limit) + (content.length > limit ? `
2033
2273
  ...(truncated)...` : "");
@@ -2041,15 +2281,16 @@ ${sections}
2041
2281
  return `You have access to the following documentation directories for context:
2042
2282
  - Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
2043
2283
  - Documents: \`.locus/documents\` (synced from cloud)
2284
+ - Discussions: \`.locus/discussions\` (product discussion insights and decisions)
2044
2285
  If you need more information about the project strategies, plans, or architecture, read files in these directories.`;
2045
2286
  }
2046
2287
  getLearningsContent() {
2047
2288
  const learningsPath = getLocusPath(this.projectPath, "learningsFile");
2048
- if (!import_node_fs3.existsSync(learningsPath)) {
2289
+ if (!import_node_fs4.existsSync(learningsPath)) {
2049
2290
  return null;
2050
2291
  }
2051
2292
  try {
2052
- const content = import_node_fs3.readFileSync(learningsPath, "utf-8");
2293
+ const content = import_node_fs4.readFileSync(learningsPath, "utf-8");
2053
2294
  const lines = content.split(`
2054
2295
  `).filter((l) => l.startsWith("- "));
2055
2296
  if (lines.length === 0) {
@@ -2061,6 +2302,53 @@ If you need more information about the project strategies, plans, or architectur
2061
2302
  return null;
2062
2303
  }
2063
2304
  }
2305
+ getDiscussionInsightsContent() {
2306
+ try {
2307
+ const manager = new DiscussionManager(this.projectPath);
2308
+ const insights = manager.getAllInsights();
2309
+ if (insights.length === 0) {
2310
+ return null;
2311
+ }
2312
+ const groups = {};
2313
+ for (const insight of insights) {
2314
+ const key = insight.type;
2315
+ if (!groups[key]) {
2316
+ groups[key] = [];
2317
+ }
2318
+ groups[key].push(insight);
2319
+ }
2320
+ const typeLabels = {
2321
+ decision: "Decisions",
2322
+ requirement: "Requirements",
2323
+ idea: "Ideas",
2324
+ concern: "Concerns",
2325
+ learning: "Learnings"
2326
+ };
2327
+ let output = "";
2328
+ for (const [type, label] of Object.entries(typeLabels)) {
2329
+ const items = groups[type];
2330
+ if (!items || items.length === 0)
2331
+ continue;
2332
+ output += `## ${label}
2333
+ `;
2334
+ for (const item of items) {
2335
+ output += `- [${item.title}]: ${item.content}
2336
+ `;
2337
+ }
2338
+ output += `
2339
+ `;
2340
+ }
2341
+ if (output.length === 0) {
2342
+ return null;
2343
+ }
2344
+ if (output.length > 2000) {
2345
+ output = `${output.slice(0, 1997)}...`;
2346
+ }
2347
+ return output.trimEnd();
2348
+ } catch {
2349
+ return null;
2350
+ }
2351
+ }
2064
2352
  roleToText(role) {
2065
2353
  if (!role) {
2066
2354
  return null;
@@ -2081,11 +2369,12 @@ If you need more information about the project strategies, plans, or architectur
2081
2369
  }
2082
2370
  }
2083
2371
  }
2084
- var import_node_fs3, import_node_path5, import_shared2;
2372
+ var import_node_fs4, import_node_path6, import_shared2;
2085
2373
  var init_prompt_builder = __esm(() => {
2374
+ init_discussion_manager();
2086
2375
  init_config();
2087
- import_node_fs3 = require("node:fs");
2088
- import_node_path5 = require("node:path");
2376
+ import_node_fs4 = require("node:fs");
2377
+ import_node_path6 = require("node:path");
2089
2378
  import_shared2 = require("@locusai/shared");
2090
2379
  });
2091
2380
 
@@ -2408,7 +2697,6 @@ Branch: \`${result.branch}\`` : "";
2408
2697
  this.log("All tasks done. Creating pull request...", "info");
2409
2698
  const prResult = this.gitWorkflow.createPullRequest(this.completedTaskList, this.taskSummaries);
2410
2699
  if (prResult.url) {
2411
- this.log(`PR created: ${prResult.url}`, "success");
2412
2700
  for (const task of this.completedTaskList) {
2413
2701
  try {
2414
2702
  await this.client.tasks.update(task.id, this.config.workspaceId, {
@@ -2467,6 +2755,8 @@ __export(exports_index_node, {
2467
2755
  detectRemoteProvider: () => detectRemoteProvider,
2468
2756
  createAiRunner: () => createAiRunner,
2469
2757
  c: () => c,
2758
+ buildSummaryPrompt: () => buildSummaryPrompt,
2759
+ buildFacilitatorPrompt: () => buildFacilitatorPrompt,
2470
2760
  WorkspacesModule: () => WorkspacesModule,
2471
2761
  TasksModule: () => TasksModule,
2472
2762
  TaskExecutor: () => TaskExecutor,
@@ -2494,6 +2784,11 @@ __export(exports_index_node, {
2494
2784
  ExecEventEmitter: () => ExecEventEmitter,
2495
2785
  DocumentFetcher: () => DocumentFetcher,
2496
2786
  DocsModule: () => DocsModule,
2787
+ DiscussionSchema: () => DiscussionSchema,
2788
+ DiscussionMessageSchema: () => DiscussionMessageSchema,
2789
+ DiscussionManager: () => DiscussionManager,
2790
+ DiscussionInsightSchema: () => DiscussionInsightSchema,
2791
+ DiscussionFacilitator: () => DiscussionFacilitator,
2497
2792
  DEFAULT_MODEL: () => DEFAULT_MODEL,
2498
2793
  ContextTracker: () => ContextTracker,
2499
2794
  CodexRunner: () => CodexRunner,
@@ -2509,8 +2804,8 @@ module.exports = __toCommonJS(exports_index_node);
2509
2804
 
2510
2805
  // src/core/indexer.ts
2511
2806
  var import_node_crypto2 = require("node:crypto");
2512
- var import_node_fs4 = require("node:fs");
2513
- var import_node_path6 = require("node:path");
2807
+ var import_node_fs5 = require("node:fs");
2808
+ var import_node_path7 = require("node:path");
2514
2809
  var import_globby = require("globby");
2515
2810
 
2516
2811
  class CodebaseIndexer {
@@ -2519,7 +2814,7 @@ class CodebaseIndexer {
2519
2814
  fullReindexRatioThreshold = 0.2;
2520
2815
  constructor(projectPath) {
2521
2816
  this.projectPath = projectPath;
2522
- this.indexPath = import_node_path6.join(projectPath, ".locus", "codebase-index.json");
2817
+ this.indexPath = import_node_path7.join(projectPath, ".locus", "codebase-index.json");
2523
2818
  }
2524
2819
  async index(onProgress, treeSummarizer, force = false) {
2525
2820
  if (!treeSummarizer) {
@@ -2575,11 +2870,11 @@ class CodebaseIndexer {
2575
2870
  }
2576
2871
  }
2577
2872
  async getFileTree() {
2578
- const gitmodulesPath = import_node_path6.join(this.projectPath, ".gitmodules");
2873
+ const gitmodulesPath = import_node_path7.join(this.projectPath, ".gitmodules");
2579
2874
  const submoduleIgnores = [];
2580
- if (import_node_fs4.existsSync(gitmodulesPath)) {
2875
+ if (import_node_fs5.existsSync(gitmodulesPath)) {
2581
2876
  try {
2582
- const content = import_node_fs4.readFileSync(gitmodulesPath, "utf-8");
2877
+ const content = import_node_fs5.readFileSync(gitmodulesPath, "utf-8");
2583
2878
  const lines = content.split(`
2584
2879
  `);
2585
2880
  for (const line of lines) {
@@ -2635,9 +2930,9 @@ class CodebaseIndexer {
2635
2930
  });
2636
2931
  }
2637
2932
  loadIndex() {
2638
- if (import_node_fs4.existsSync(this.indexPath)) {
2933
+ if (import_node_fs5.existsSync(this.indexPath)) {
2639
2934
  try {
2640
- return JSON.parse(import_node_fs4.readFileSync(this.indexPath, "utf-8"));
2935
+ return JSON.parse(import_node_fs5.readFileSync(this.indexPath, "utf-8"));
2641
2936
  } catch {
2642
2937
  return null;
2643
2938
  }
@@ -2645,11 +2940,11 @@ class CodebaseIndexer {
2645
2940
  return null;
2646
2941
  }
2647
2942
  saveIndex(index) {
2648
- const dir = import_node_path6.dirname(this.indexPath);
2649
- if (!import_node_fs4.existsSync(dir)) {
2650
- import_node_fs4.mkdirSync(dir, { recursive: true });
2943
+ const dir = import_node_path7.dirname(this.indexPath);
2944
+ if (!import_node_fs5.existsSync(dir)) {
2945
+ import_node_fs5.mkdirSync(dir, { recursive: true });
2651
2946
  }
2652
- import_node_fs4.writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
2947
+ import_node_fs5.writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
2653
2948
  }
2654
2949
  cloneIndex(index) {
2655
2950
  return JSON.parse(JSON.stringify(index));
@@ -2665,7 +2960,7 @@ class CodebaseIndexer {
2665
2960
  }
2666
2961
  hashFile(filePath) {
2667
2962
  try {
2668
- const content = import_node_fs4.readFileSync(import_node_path6.join(this.projectPath, filePath), "utf-8");
2963
+ const content = import_node_fs5.readFileSync(import_node_path7.join(this.projectPath, filePath), "utf-8");
2669
2964
  return import_node_crypto2.createHash("sha256").update(content).digest("hex").slice(0, 16);
2670
2965
  } catch {
2671
2966
  return null;
@@ -2820,8 +3115,8 @@ Return ONLY valid JSON (no code fences, no markdown):
2820
3115
  }
2821
3116
  // src/agent/document-fetcher.ts
2822
3117
  init_config();
2823
- var import_node_fs5 = require("node:fs");
2824
- var import_node_path7 = require("node:path");
3118
+ var import_node_fs6 = require("node:fs");
3119
+ var import_node_path8 = require("node:path");
2825
3120
 
2826
3121
  class DocumentFetcher {
2827
3122
  deps;
@@ -2830,8 +3125,8 @@ class DocumentFetcher {
2830
3125
  }
2831
3126
  async fetch() {
2832
3127
  const documentsDir = getLocusPath(this.deps.projectPath, "documentsDir");
2833
- if (!import_node_fs5.existsSync(documentsDir)) {
2834
- import_node_fs5.mkdirSync(documentsDir, { recursive: true });
3128
+ if (!import_node_fs6.existsSync(documentsDir)) {
3129
+ import_node_fs6.mkdirSync(documentsDir, { recursive: true });
2835
3130
  }
2836
3131
  try {
2837
3132
  const groups = await this.deps.client.docs.listGroups(this.deps.workspaceId);
@@ -2844,14 +3139,14 @@ class DocumentFetcher {
2844
3139
  continue;
2845
3140
  }
2846
3141
  const groupName = groupMap.get(doc.groupId || "") || "General";
2847
- const groupDir = import_node_path7.join(documentsDir, groupName);
2848
- if (!import_node_fs5.existsSync(groupDir)) {
2849
- import_node_fs5.mkdirSync(groupDir, { recursive: true });
3142
+ const groupDir = import_node_path8.join(documentsDir, groupName);
3143
+ if (!import_node_fs6.existsSync(groupDir)) {
3144
+ import_node_fs6.mkdirSync(groupDir, { recursive: true });
2850
3145
  }
2851
3146
  const fileName = `${doc.title}.md`;
2852
- const filePath = import_node_path7.join(groupDir, fileName);
2853
- if (!import_node_fs5.existsSync(filePath) || import_node_fs5.readFileSync(filePath, "utf-8") !== doc.content) {
2854
- import_node_fs5.writeFileSync(filePath, doc.content || "");
3147
+ const filePath = import_node_path8.join(groupDir, fileName);
3148
+ if (!import_node_fs6.existsSync(filePath) || import_node_fs6.readFileSync(filePath, "utf-8") !== doc.content) {
3149
+ import_node_fs6.writeFileSync(filePath, doc.content || "");
2855
3150
  fetchedCount++;
2856
3151
  }
2857
3152
  }
@@ -3385,6 +3680,341 @@ init_prompt_builder();
3385
3680
  // src/index-node.ts
3386
3681
  init_prompt_builder();
3387
3682
 
3683
+ // src/discussion/agents/facilitator-prompt.ts
3684
+ function buildFacilitatorPrompt(input) {
3685
+ const {
3686
+ topic,
3687
+ projectContext,
3688
+ learnings,
3689
+ knowledgeBase,
3690
+ previousMessages,
3691
+ insights,
3692
+ isFirstMessage
3693
+ } = input;
3694
+ let sections = "";
3695
+ if (projectContext) {
3696
+ sections += `
3697
+ <project_context>
3698
+ ${projectContext}
3699
+ </project_context>
3700
+ `;
3701
+ }
3702
+ sections += `
3703
+ <knowledge_base>
3704
+ ${knowledgeBase}
3705
+ </knowledge_base>
3706
+ `;
3707
+ if (learnings) {
3708
+ sections += `
3709
+ <learnings>
3710
+ These are accumulated lessons from past work on this project. Use them to ask more informed questions:
3711
+ ${learnings}
3712
+ </learnings>
3713
+ `;
3714
+ }
3715
+ if (previousMessages.length > 0) {
3716
+ let history = "";
3717
+ for (const msg of previousMessages) {
3718
+ const role = msg.role === "user" ? "User" : "Facilitator";
3719
+ history += `[${role}]: ${msg.content}
3720
+
3721
+ `;
3722
+ }
3723
+ sections += `
3724
+ <conversation_history>
3725
+ ${history.trimEnd()}
3726
+ </conversation_history>
3727
+ `;
3728
+ }
3729
+ if (insights.length > 0) {
3730
+ let insightsText = "";
3731
+ for (const insight of insights) {
3732
+ insightsText += `- [${insight.type.toUpperCase()}] ${insight.title}: ${insight.content}
3733
+ `;
3734
+ }
3735
+ sections += `
3736
+ <extracted_insights>
3737
+ Insights identified so far in this discussion:
3738
+ ${insightsText.trimEnd()}
3739
+ </extracted_insights>
3740
+ `;
3741
+ }
3742
+ const firstMessageInstruction = isFirstMessage ? `This is the START of the discussion. Introduce yourself briefly, then ask your first probing question about the topic. Do NOT extract any insights yet — there is no user input to extract from.` : `Continue the discussion by responding to the user's latest message. Build on their answers to go deeper. After responding, extract any insights from their message.`;
3743
+ return `<discussion_facilitator>
3744
+ You are a product strategy facilitator leading a structured discussion.
3745
+
3746
+ <topic>
3747
+ ${topic}
3748
+ </topic>
3749
+ ${sections}
3750
+ <role>
3751
+ You are an expert product strategy facilitator. Your job is to:
3752
+ 1. Ask probing, specific questions about the topic — never generic or surface-level
3753
+ 2. Build on previous answers to progressively deepen the conversation
3754
+ 3. Identify and extract key decisions, requirements, ideas, concerns, and learnings
3755
+ 4. Reference existing project context and learnings to ask informed questions
3756
+ 5. When the topic feels fully explored, suggest wrapping up with a summary
3757
+ </role>
3758
+
3759
+ <rules>
3760
+ - ${firstMessageInstruction}
3761
+ - Ask ONE focused question at a time. Do not overwhelm with multiple questions.
3762
+ - Be conversational but purposeful — every question should drive toward actionable insights.
3763
+ - When you identify an insight from the user's response, include it as a structured XML block in your response.
3764
+ - Insight blocks use this format within your response text:
3765
+
3766
+ <insight>
3767
+ {"type": "decision|requirement|idea|concern|learning", "title": "short title", "content": "detailed description", "tags": ["relevant", "tags"]}
3768
+ </insight>
3769
+
3770
+ - You may include multiple <insight> blocks if the user's response contains several distinct insights.
3771
+ - The insight blocks will be parsed and removed from the displayed response, so write your conversational text as if they are not there.
3772
+ - Types explained:
3773
+ - **decision**: A choice or direction that has been made or agreed upon
3774
+ - **requirement**: A specific need, constraint, or must-have
3775
+ - **idea**: A suggestion, proposal, or possibility to explore
3776
+ - **concern**: A risk, worry, or potential problem identified
3777
+ - **learning**: A realization, lesson, or important context discovered
3778
+ - Keep responses concise. Aim for 2-4 sentences of conversation plus any insight blocks.
3779
+ - If the user's responses indicate the topic is well-explored, suggest summarizing and wrapping up.
3780
+ </rules>
3781
+ </discussion_facilitator>`;
3782
+ }
3783
+ function buildSummaryPrompt(topic, messages, insights) {
3784
+ let history = "";
3785
+ for (const msg of messages) {
3786
+ const role = msg.role === "user" ? "User" : "Facilitator";
3787
+ history += `[${role}]: ${msg.content}
3788
+
3789
+ `;
3790
+ }
3791
+ let insightsText = "";
3792
+ if (insights.length > 0) {
3793
+ for (const insight of insights) {
3794
+ insightsText += `- [${insight.type.toUpperCase()}] **${insight.title}**: ${insight.content}`;
3795
+ if (insight.tags.length > 0) {
3796
+ insightsText += ` (tags: ${insight.tags.join(", ")})`;
3797
+ }
3798
+ insightsText += `
3799
+ `;
3800
+ }
3801
+ }
3802
+ return `<discussion_summary>
3803
+ Create a final summary of this product discussion.
3804
+
3805
+ <topic>
3806
+ ${topic}
3807
+ </topic>
3808
+
3809
+ <conversation>
3810
+ ${history.trimEnd()}
3811
+ </conversation>
3812
+
3813
+ ${insightsText ? `<insights>
3814
+ ${insightsText.trimEnd()}
3815
+ </insights>
3816
+ ` : ""}
3817
+ <rules>
3818
+ - Write a clear, structured summary of the entire discussion.
3819
+ - Organize by: Key Decisions, Requirements, Ideas to Explore, Concerns & Risks, and Learnings.
3820
+ - Only include sections that have relevant content — skip empty categories.
3821
+ - For each item, provide a brief but actionable description.
3822
+ - End with a "Next Steps" section listing concrete action items that emerged.
3823
+ - Be concise — this summary should be scannable and useful as a reference.
3824
+ - Do NOT include any <insight> XML blocks in the summary.
3825
+ </rules>
3826
+ </discussion_summary>`;
3827
+ }
3828
+ // src/discussion/discussion-facilitator.ts
3829
+ init_config();
3830
+ var import_node_fs7 = require("node:fs");
3831
+ class DiscussionFacilitator {
3832
+ projectPath;
3833
+ aiRunner;
3834
+ discussionManager;
3835
+ log;
3836
+ provider;
3837
+ model;
3838
+ constructor(config) {
3839
+ this.projectPath = config.projectPath;
3840
+ this.aiRunner = config.aiRunner;
3841
+ this.discussionManager = config.discussionManager;
3842
+ this.log = config.log ?? ((_msg) => {
3843
+ return;
3844
+ });
3845
+ this.provider = config.provider;
3846
+ this.model = config.model;
3847
+ }
3848
+ async startDiscussion(topic) {
3849
+ this.log("Starting new discussion...", "info");
3850
+ const discussion = this.discussionManager.create(topic, this.model, this.provider);
3851
+ const { projectContext, learnings, knowledgeBase } = this.buildContext();
3852
+ const prompt = buildFacilitatorPrompt({
3853
+ topic,
3854
+ projectContext,
3855
+ learnings,
3856
+ knowledgeBase,
3857
+ previousMessages: [],
3858
+ insights: [],
3859
+ isFirstMessage: true
3860
+ });
3861
+ const response = await this.aiRunner.run(prompt);
3862
+ const { cleanResponse } = this.parseInsights(response);
3863
+ this.discussionManager.addMessage(discussion.id, "assistant", cleanResponse);
3864
+ this.log("Discussion started", "success");
3865
+ const saved = this.discussionManager.load(discussion.id);
3866
+ if (!saved) {
3867
+ throw new Error(`Failed to load discussion after creation: ${discussion.id}`);
3868
+ }
3869
+ return {
3870
+ discussion: saved,
3871
+ message: cleanResponse
3872
+ };
3873
+ }
3874
+ async continueDiscussion(discussionId, userMessage) {
3875
+ const discussion = this.discussionManager.load(discussionId);
3876
+ if (!discussion) {
3877
+ throw new Error(`Discussion not found: ${discussionId}`);
3878
+ }
3879
+ const updated = this.discussionManager.addMessage(discussionId, "user", userMessage);
3880
+ const { projectContext, learnings, knowledgeBase } = this.buildContext();
3881
+ const prompt = buildFacilitatorPrompt({
3882
+ topic: updated.topic,
3883
+ projectContext,
3884
+ learnings,
3885
+ knowledgeBase,
3886
+ previousMessages: updated.messages,
3887
+ insights: updated.insights,
3888
+ isFirstMessage: false
3889
+ });
3890
+ const response = await this.aiRunner.run(prompt);
3891
+ const { cleanResponse, insights } = this.parseInsights(response);
3892
+ for (const insight of insights) {
3893
+ this.discussionManager.addInsight(discussionId, insight);
3894
+ }
3895
+ this.discussionManager.addMessage(discussionId, "assistant", cleanResponse);
3896
+ return { response: cleanResponse, insights };
3897
+ }
3898
+ async* continueDiscussionStream(discussionId, userMessage) {
3899
+ const discussion = this.discussionManager.load(discussionId);
3900
+ if (!discussion) {
3901
+ throw new Error(`Discussion not found: ${discussionId}`);
3902
+ }
3903
+ const updated = this.discussionManager.addMessage(discussionId, "user", userMessage);
3904
+ const { projectContext, learnings, knowledgeBase } = this.buildContext();
3905
+ const prompt = buildFacilitatorPrompt({
3906
+ topic: updated.topic,
3907
+ projectContext,
3908
+ learnings,
3909
+ knowledgeBase,
3910
+ previousMessages: updated.messages,
3911
+ insights: updated.insights,
3912
+ isFirstMessage: false
3913
+ });
3914
+ let fullResponse = "";
3915
+ const stream = this.aiRunner.runStream(prompt);
3916
+ for await (const chunk of stream) {
3917
+ yield chunk;
3918
+ if (chunk.type === "text_delta") {
3919
+ fullResponse += chunk.content;
3920
+ } else if (chunk.type === "result") {
3921
+ fullResponse = chunk.content;
3922
+ }
3923
+ }
3924
+ const { cleanResponse, insights } = this.parseInsights(fullResponse);
3925
+ for (const insight of insights) {
3926
+ this.discussionManager.addInsight(discussionId, insight);
3927
+ }
3928
+ this.discussionManager.addMessage(discussionId, "assistant", cleanResponse);
3929
+ return { response: cleanResponse, insights };
3930
+ }
3931
+ async summarizeDiscussion(discussionId) {
3932
+ const discussion = this.discussionManager.load(discussionId);
3933
+ if (!discussion) {
3934
+ throw new Error(`Discussion not found: ${discussionId}`);
3935
+ }
3936
+ this.log("Generating discussion summary...", "info");
3937
+ const prompt = buildSummaryPrompt(discussion.topic, discussion.messages, discussion.insights);
3938
+ const summary = await this.aiRunner.run(prompt);
3939
+ this.discussionManager.addMessage(discussionId, "assistant", summary);
3940
+ this.discussionManager.complete(discussionId);
3941
+ this.log("Discussion summarized and completed", "success");
3942
+ return summary;
3943
+ }
3944
+ parseInsights(response) {
3945
+ const insights = [];
3946
+ const insightRegex = /<insight>\s*([\s\S]*?)\s*<\/insight>/g;
3947
+ let match = insightRegex.exec(response);
3948
+ while (match !== null) {
3949
+ try {
3950
+ const parsed = JSON.parse(match[1]);
3951
+ const id = `ins-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
3952
+ insights.push({
3953
+ id,
3954
+ type: parsed.type,
3955
+ title: parsed.title,
3956
+ content: parsed.content,
3957
+ tags: parsed.tags ?? [],
3958
+ createdAt: new Date().toISOString()
3959
+ });
3960
+ } catch {}
3961
+ match = insightRegex.exec(response);
3962
+ }
3963
+ const cleanResponse = response.replace(/<insight>\s*[\s\S]*?\s*<\/insight>/g, "").replace(/\n{3,}/g, `
3964
+
3965
+ `).trim();
3966
+ return { cleanResponse, insights };
3967
+ }
3968
+ buildContext() {
3969
+ return {
3970
+ projectContext: this.getProjectContext(),
3971
+ learnings: this.getLearningsContent(),
3972
+ knowledgeBase: this.getKnowledgeBaseSection()
3973
+ };
3974
+ }
3975
+ getProjectContext() {
3976
+ const contextPath = getLocusPath(this.projectPath, "contextFile");
3977
+ if (import_node_fs7.existsSync(contextPath)) {
3978
+ try {
3979
+ const context = import_node_fs7.readFileSync(contextPath, "utf-8");
3980
+ if (context.trim().length > 20) {
3981
+ return context;
3982
+ }
3983
+ } catch {
3984
+ return null;
3985
+ }
3986
+ }
3987
+ return null;
3988
+ }
3989
+ getLearningsContent() {
3990
+ const learningsPath = getLocusPath(this.projectPath, "learningsFile");
3991
+ if (!import_node_fs7.existsSync(learningsPath)) {
3992
+ return null;
3993
+ }
3994
+ try {
3995
+ const content = import_node_fs7.readFileSync(learningsPath, "utf-8");
3996
+ const lines = content.split(`
3997
+ `).filter((l) => l.startsWith("- "));
3998
+ if (lines.length === 0) {
3999
+ return null;
4000
+ }
4001
+ return lines.join(`
4002
+ `);
4003
+ } catch {
4004
+ return null;
4005
+ }
4006
+ }
4007
+ getKnowledgeBaseSection() {
4008
+ return `You have access to the following documentation directories for context:
4009
+ - Artifacts: \`.locus/artifacts\` (local-only, not synced to cloud)
4010
+ - Documents: \`.locus/documents\` (synced from cloud)
4011
+ If you need more information about the project strategies, plans, or architecture, read files in these directories.`;
4012
+ }
4013
+ }
4014
+
4015
+ // src/discussion/index.ts
4016
+ init_discussion_manager();
4017
+ init_discussion_types();
3388
4018
  // src/exec/context-tracker.ts
3389
4019
  var REFERENCE_ALIASES = {
3390
4020
  plan: ["the plan", "sprint plan", "project plan", "implementation plan"],
@@ -3799,8 +4429,8 @@ class ExecEventEmitter {
3799
4429
  }
3800
4430
  // src/exec/history-manager.ts
3801
4431
  init_config();
3802
- var import_node_fs6 = require("node:fs");
3803
- var import_node_path8 = require("node:path");
4432
+ var import_node_fs8 = require("node:fs");
4433
+ var import_node_path9 = require("node:path");
3804
4434
  var DEFAULT_MAX_SESSIONS = 30;
3805
4435
  function generateSessionId2() {
3806
4436
  const timestamp = Date.now().toString(36);
@@ -3812,30 +4442,30 @@ class HistoryManager {
3812
4442
  historyDir;
3813
4443
  maxSessions;
3814
4444
  constructor(projectPath, options) {
3815
- this.historyDir = options?.historyDir ?? import_node_path8.join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.sessionsDir);
4445
+ this.historyDir = options?.historyDir ?? import_node_path9.join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG.sessionsDir);
3816
4446
  this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
3817
4447
  this.ensureHistoryDir();
3818
4448
  }
3819
4449
  ensureHistoryDir() {
3820
- if (!import_node_fs6.existsSync(this.historyDir)) {
3821
- import_node_fs6.mkdirSync(this.historyDir, { recursive: true });
4450
+ if (!import_node_fs8.existsSync(this.historyDir)) {
4451
+ import_node_fs8.mkdirSync(this.historyDir, { recursive: true });
3822
4452
  }
3823
4453
  }
3824
4454
  getSessionPath(sessionId) {
3825
- return import_node_path8.join(this.historyDir, `${sessionId}.json`);
4455
+ return import_node_path9.join(this.historyDir, `${sessionId}.json`);
3826
4456
  }
3827
4457
  saveSession(session) {
3828
4458
  const filePath = this.getSessionPath(session.id);
3829
4459
  session.updatedAt = Date.now();
3830
- import_node_fs6.writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
4460
+ import_node_fs8.writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
3831
4461
  }
3832
4462
  loadSession(sessionId) {
3833
4463
  const filePath = this.getSessionPath(sessionId);
3834
- if (!import_node_fs6.existsSync(filePath)) {
4464
+ if (!import_node_fs8.existsSync(filePath)) {
3835
4465
  return null;
3836
4466
  }
3837
4467
  try {
3838
- const content = import_node_fs6.readFileSync(filePath, "utf-8");
4468
+ const content = import_node_fs8.readFileSync(filePath, "utf-8");
3839
4469
  return JSON.parse(content);
3840
4470
  } catch {
3841
4471
  return null;
@@ -3843,18 +4473,18 @@ class HistoryManager {
3843
4473
  }
3844
4474
  deleteSession(sessionId) {
3845
4475
  const filePath = this.getSessionPath(sessionId);
3846
- if (!import_node_fs6.existsSync(filePath)) {
4476
+ if (!import_node_fs8.existsSync(filePath)) {
3847
4477
  return false;
3848
4478
  }
3849
4479
  try {
3850
- import_node_fs6.rmSync(filePath);
4480
+ import_node_fs8.rmSync(filePath);
3851
4481
  return true;
3852
4482
  } catch {
3853
4483
  return false;
3854
4484
  }
3855
4485
  }
3856
4486
  listSessions(options) {
3857
- const files = import_node_fs6.readdirSync(this.historyDir);
4487
+ const files = import_node_fs8.readdirSync(this.historyDir);
3858
4488
  let sessions = [];
3859
4489
  for (const file of files) {
3860
4490
  if (file.endsWith(".json")) {
@@ -3927,11 +4557,11 @@ class HistoryManager {
3927
4557
  return deleted;
3928
4558
  }
3929
4559
  getSessionCount() {
3930
- const files = import_node_fs6.readdirSync(this.historyDir);
4560
+ const files = import_node_fs8.readdirSync(this.historyDir);
3931
4561
  return files.filter((f) => f.endsWith(".json")).length;
3932
4562
  }
3933
4563
  sessionExists(sessionId) {
3934
- return import_node_fs6.existsSync(this.getSessionPath(sessionId));
4564
+ return import_node_fs8.existsSync(this.getSessionPath(sessionId));
3935
4565
  }
3936
4566
  findSessionByPartialId(partialId) {
3937
4567
  const sessions = this.listSessions();
@@ -3945,12 +4575,12 @@ class HistoryManager {
3945
4575
  return this.historyDir;
3946
4576
  }
3947
4577
  clearAllSessions() {
3948
- const files = import_node_fs6.readdirSync(this.historyDir);
4578
+ const files = import_node_fs8.readdirSync(this.historyDir);
3949
4579
  let deleted = 0;
3950
4580
  for (const file of files) {
3951
4581
  if (file.endsWith(".json")) {
3952
4582
  try {
3953
- import_node_fs6.rmSync(import_node_path8.join(this.historyDir, file));
4583
+ import_node_fs8.rmSync(import_node_path9.join(this.historyDir, file));
3954
4584
  deleted++;
3955
4585
  } catch {}
3956
4586
  }
@@ -4226,8 +4856,8 @@ init_src();
4226
4856
  init_colors();
4227
4857
  init_resolve_bin();
4228
4858
  var import_node_child_process7 = require("node:child_process");
4229
- var import_node_fs7 = require("node:fs");
4230
- var import_node_path9 = require("node:path");
4859
+ var import_node_fs9 = require("node:fs");
4860
+ var import_node_path10 = require("node:path");
4231
4861
  var import_node_url = require("node:url");
4232
4862
  var import_shared4 = require("@locusai/shared");
4233
4863
  var import_events4 = require("events");
@@ -4495,14 +5125,14 @@ ${agentId} finished (exit code: ${code})`);
4495
5125
  }
4496
5126
  resolveWorkerPath() {
4497
5127
  const currentModulePath = import_node_url.fileURLToPath("file:///home/runner/work/locusai/locusai/packages/sdk/src/orchestrator/index.ts");
4498
- const currentModuleDir = import_node_path9.dirname(currentModulePath);
5128
+ const currentModuleDir = import_node_path10.dirname(currentModulePath);
4499
5129
  const potentialPaths = [
4500
- import_node_path9.join(currentModuleDir, "..", "agent", "worker.js"),
4501
- import_node_path9.join(currentModuleDir, "agent", "worker.js"),
4502
- import_node_path9.join(currentModuleDir, "worker.js"),
4503
- import_node_path9.join(currentModuleDir, "..", "agent", "worker.ts")
5130
+ import_node_path10.join(currentModuleDir, "..", "agent", "worker.js"),
5131
+ import_node_path10.join(currentModuleDir, "agent", "worker.js"),
5132
+ import_node_path10.join(currentModuleDir, "worker.js"),
5133
+ import_node_path10.join(currentModuleDir, "..", "agent", "worker.ts")
4504
5134
  ];
4505
- return potentialPaths.find((p) => import_node_fs7.existsSync(p));
5135
+ return potentialPaths.find((p) => import_node_fs9.existsSync(p));
4506
5136
  }
4507
5137
  }
4508
5138
  function killProcessTree(proc) {
@@ -4521,12 +5151,12 @@ function sleep(ms) {
4521
5151
  }
4522
5152
  // src/planning/plan-manager.ts
4523
5153
  init_config();
4524
- var import_node_fs8 = require("node:fs");
4525
- var import_node_path10 = require("node:path");
5154
+ var import_node_fs10 = require("node:fs");
5155
+ var import_node_path11 = require("node:path");
4526
5156
 
4527
5157
  // src/planning/sprint-plan.ts
4528
5158
  var import_shared5 = require("@locusai/shared");
4529
- var import_zod = require("zod");
5159
+ var import_zod2 = require("zod");
4530
5160
 
4531
5161
  // src/utils/structured-output.ts
4532
5162
  function parseJsonWithSchema(raw, schema) {
@@ -4551,26 +5181,26 @@ Parsed JSON preview: ${JSON.stringify(parsed).slice(0, 300)}`);
4551
5181
  }
4552
5182
 
4553
5183
  // src/planning/sprint-plan.ts
4554
- var PlannedTaskSchema = import_zod.z.object({
4555
- title: import_zod.z.string().default("Untitled Task"),
4556
- description: import_zod.z.string().default(""),
4557
- assigneeRole: import_zod.z.enum(["BACKEND", "FRONTEND", "QA", "PM", "DESIGN"]).default("BACKEND"),
4558
- priority: import_zod.z.enum(["CRITICAL", "HIGH", "MEDIUM", "LOW"]).default("MEDIUM"),
4559
- complexity: import_zod.z.number().min(1).max(5).default(3),
4560
- acceptanceCriteria: import_zod.z.array(import_zod.z.string()).default([]),
4561
- labels: import_zod.z.array(import_zod.z.string()).default([])
5184
+ var PlannedTaskSchema = import_zod2.z.object({
5185
+ title: import_zod2.z.string().default("Untitled Task"),
5186
+ description: import_zod2.z.string().default(""),
5187
+ assigneeRole: import_zod2.z.enum(["BACKEND", "FRONTEND", "QA", "PM", "DESIGN"]).default("BACKEND"),
5188
+ priority: import_zod2.z.enum(["CRITICAL", "HIGH", "MEDIUM", "LOW"]).default("MEDIUM"),
5189
+ complexity: import_zod2.z.number().min(1).max(5).default(3),
5190
+ acceptanceCriteria: import_zod2.z.array(import_zod2.z.string()).default([]),
5191
+ labels: import_zod2.z.array(import_zod2.z.string()).default([])
4562
5192
  });
4563
- var SprintPlanRiskSchema = import_zod.z.object({
4564
- description: import_zod.z.string().default(""),
4565
- mitigation: import_zod.z.string().default(""),
4566
- severity: import_zod.z.enum(["low", "medium", "high"]).default("medium")
5193
+ var SprintPlanRiskSchema = import_zod2.z.object({
5194
+ description: import_zod2.z.string().default(""),
5195
+ mitigation: import_zod2.z.string().default(""),
5196
+ severity: import_zod2.z.enum(["low", "medium", "high"]).default("medium")
4567
5197
  });
4568
- var PlannerOutputSchema = import_zod.z.object({
4569
- name: import_zod.z.string().default("Unnamed Sprint"),
4570
- goal: import_zod.z.string().default(""),
4571
- estimatedDays: import_zod.z.number().default(1),
4572
- tasks: import_zod.z.array(PlannedTaskSchema).default([]),
4573
- risks: import_zod.z.array(SprintPlanRiskSchema).default([])
5198
+ var PlannerOutputSchema = import_zod2.z.object({
5199
+ name: import_zod2.z.string().default("Unnamed Sprint"),
5200
+ goal: import_zod2.z.string().default(""),
5201
+ estimatedDays: import_zod2.z.number().default(1),
5202
+ tasks: import_zod2.z.array(PlannedTaskSchema).default([]),
5203
+ risks: import_zod2.z.array(SprintPlanRiskSchema).default([])
4574
5204
  });
4575
5205
  var SprintPlanAIOutputSchema = PlannerOutputSchema;
4576
5206
  function sprintPlanToMarkdown(plan) {
@@ -4687,19 +5317,19 @@ class PlanManager {
4687
5317
  save(plan) {
4688
5318
  this.ensurePlansDir();
4689
5319
  const slug = this.slugify(plan.name);
4690
- const jsonPath = import_node_path10.join(this.plansDir, `${slug}.json`);
4691
- const mdPath = import_node_path10.join(this.plansDir, `sprint-${slug}.md`);
4692
- import_node_fs8.writeFileSync(jsonPath, JSON.stringify(plan, null, 2), "utf-8");
4693
- import_node_fs8.writeFileSync(mdPath, sprintPlanToMarkdown(plan), "utf-8");
5320
+ const jsonPath = import_node_path11.join(this.plansDir, `${slug}.json`);
5321
+ const mdPath = import_node_path11.join(this.plansDir, `sprint-${slug}.md`);
5322
+ import_node_fs10.writeFileSync(jsonPath, JSON.stringify(plan, null, 2), "utf-8");
5323
+ import_node_fs10.writeFileSync(mdPath, sprintPlanToMarkdown(plan), "utf-8");
4694
5324
  return plan.id;
4695
5325
  }
4696
5326
  load(idOrSlug) {
4697
5327
  this.ensurePlansDir();
4698
- const files = import_node_fs8.readdirSync(this.plansDir).filter((f) => f.endsWith(".json"));
5328
+ const files = import_node_fs10.readdirSync(this.plansDir).filter((f) => f.endsWith(".json"));
4699
5329
  for (const file of files) {
4700
- const filePath = import_node_path10.join(this.plansDir, file);
5330
+ const filePath = import_node_path11.join(this.plansDir, file);
4701
5331
  try {
4702
- const plan = JSON.parse(import_node_fs8.readFileSync(filePath, "utf-8"));
5332
+ const plan = JSON.parse(import_node_fs10.readFileSync(filePath, "utf-8"));
4703
5333
  if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
4704
5334
  return plan;
4705
5335
  }
@@ -4709,11 +5339,11 @@ class PlanManager {
4709
5339
  }
4710
5340
  list(status) {
4711
5341
  this.ensurePlansDir();
4712
- const files = import_node_fs8.readdirSync(this.plansDir).filter((f) => f.endsWith(".json"));
5342
+ const files = import_node_fs10.readdirSync(this.plansDir).filter((f) => f.endsWith(".json"));
4713
5343
  const plans = [];
4714
5344
  for (const file of files) {
4715
5345
  try {
4716
- const plan = JSON.parse(import_node_fs8.readFileSync(import_node_path10.join(this.plansDir, file), "utf-8"));
5346
+ const plan = JSON.parse(import_node_fs10.readFileSync(import_node_path11.join(this.plansDir, file), "utf-8"));
4717
5347
  if (!status || plan.status === status) {
4718
5348
  plans.push(plan);
4719
5349
  }
@@ -4770,18 +5400,18 @@ class PlanManager {
4770
5400
  }
4771
5401
  delete(idOrSlug) {
4772
5402
  this.ensurePlansDir();
4773
- const files = import_node_fs8.readdirSync(this.plansDir);
5403
+ const files = import_node_fs10.readdirSync(this.plansDir);
4774
5404
  for (const file of files) {
4775
- const filePath = import_node_path10.join(this.plansDir, file);
5405
+ const filePath = import_node_path11.join(this.plansDir, file);
4776
5406
  if (!file.endsWith(".json"))
4777
5407
  continue;
4778
5408
  try {
4779
- const plan = JSON.parse(import_node_fs8.readFileSync(filePath, "utf-8"));
5409
+ const plan = JSON.parse(import_node_fs10.readFileSync(filePath, "utf-8"));
4780
5410
  if (plan.id === idOrSlug || this.slugify(plan.name) === idOrSlug) {
4781
- import_node_fs8.unlinkSync(filePath);
4782
- const mdPath = import_node_path10.join(this.plansDir, `sprint-${this.slugify(plan.name)}.md`);
4783
- if (import_node_fs8.existsSync(mdPath)) {
4784
- import_node_fs8.unlinkSync(mdPath);
5411
+ import_node_fs10.unlinkSync(filePath);
5412
+ const mdPath = import_node_path11.join(this.plansDir, `sprint-${this.slugify(plan.name)}.md`);
5413
+ if (import_node_fs10.existsSync(mdPath)) {
5414
+ import_node_fs10.unlinkSync(mdPath);
4785
5415
  }
4786
5416
  return;
4787
5417
  }
@@ -4795,8 +5425,8 @@ class PlanManager {
4795
5425
  return sprintPlanToMarkdown(plan);
4796
5426
  }
4797
5427
  ensurePlansDir() {
4798
- if (!import_node_fs8.existsSync(this.plansDir)) {
4799
- import_node_fs8.mkdirSync(this.plansDir, { recursive: true });
5428
+ if (!import_node_fs10.existsSync(this.plansDir)) {
5429
+ import_node_fs10.mkdirSync(this.plansDir, { recursive: true });
4800
5430
  }
4801
5431
  }
4802
5432
  slugify(name) {
@@ -4805,8 +5435,8 @@ class PlanManager {
4805
5435
  }
4806
5436
  // src/planning/planning-meeting.ts
4807
5437
  init_config();
4808
- var import_node_fs9 = require("node:fs");
4809
- var import_node_path11 = require("node:path");
5438
+ var import_node_fs11 = require("node:fs");
5439
+ var import_node_path12 = require("node:path");
4810
5440
 
4811
5441
  // src/planning/agents/planner.ts
4812
5442
  function buildPlannerPrompt(input) {
@@ -4892,8 +5522,8 @@ class PlanningMeeting {
4892
5522
  async run(directive, feedback) {
4893
5523
  this.log("Planning sprint...", "info");
4894
5524
  const plansDir = getLocusPath(this.projectPath, "plansDir");
4895
- if (!import_node_fs9.existsSync(plansDir)) {
4896
- import_node_fs9.mkdirSync(plansDir, { recursive: true });
5525
+ if (!import_node_fs11.existsSync(plansDir)) {
5526
+ import_node_fs11.mkdirSync(plansDir, { recursive: true });
4897
5527
  }
4898
5528
  const ts = Date.now();
4899
5529
  const planId = `plan-${ts}`;
@@ -4907,11 +5537,11 @@ class PlanningMeeting {
4907
5537
  });
4908
5538
  const response = await this.aiRunner.run(prompt);
4909
5539
  this.log("Planning meeting complete.", "success");
4910
- const expectedPath = import_node_path11.join(plansDir, `${fileName}.json`);
5540
+ const expectedPath = import_node_path12.join(plansDir, `${fileName}.json`);
4911
5541
  let plan = null;
4912
- if (import_node_fs9.existsSync(expectedPath)) {
5542
+ if (import_node_fs11.existsSync(expectedPath)) {
4913
5543
  try {
4914
- plan = JSON.parse(import_node_fs9.readFileSync(expectedPath, "utf-8"));
5544
+ plan = JSON.parse(import_node_fs11.readFileSync(expectedPath, "utf-8"));
4915
5545
  } catch {}
4916
5546
  }
4917
5547
  if (!plan) {