@proxysoul/soulforge 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  <br/><br/>
21
21
 
22
- <img src="assets/main-1.png" alt="SoulForge" width="900" />
22
+ <img src="assets/intro.gif" alt="SoulForge" width="900" />
23
23
 
24
24
  <br/>
25
25
 
@@ -129,6 +129,10 @@ SoulForge already knows. On startup it builds a **live dependency graph** of you
129
129
  <td><strong>4-tier intelligence</strong></td>
130
130
  <td>LSP, ts-morph, tree-sitter, regex. 33 languages. Dual LSP: Neovim bridge when the editor is open, standalone servers when it's not. <a href="docs/architecture.md">More</a></td>
131
131
  </tr>
132
+ <tr>
133
+ <td><strong>Persistent memory</strong></td>
134
+ <td>Optional <a href="https://github.com/milla-jovovich/mempalace">MemPalace</a> integration. Compaction v2 automatically saves decisions, discoveries, and failures to a searchable knowledge graph. Agent diary persists across sessions. Zero extra tokens. <a href="docs/compaction.md#mempalace-integration">More</a></td>
135
+ </tr>
132
136
  </table>
133
137
 
134
138
  </details>
@@ -554,6 +558,7 @@ See [GETTING_STARTED.md](GETTING_STARTED.md) for the full config reference.
554
558
  <tr><td><a href="docs/compound-tools.md">Compound Tools</a></td><td>read, multi_edit, rename_symbol, move_symbol, refactor, project</td></tr>
555
559
  <tr><td><a href="docs/project-tool.md">Project Tool</a></td><td>25+ ecosystems, pre-commit checks, monorepo discovery</td></tr>
556
560
  <tr><td><a href="docs/mcp.md">MCP Servers</a></td><td>Model Context Protocol integration, transports, configuration, lifecycle</td></tr>
561
+ <tr><td><a href="docs/compaction.md#mempalace-integration">MemPalace</a></td><td>Persistent memory via <a href="https://github.com/milla-jovovich/mempalace">MemPalace</a>. Compaction auto-saves to knowledge graph, agent diary, semantic search</td></tr>
557
562
  <tr><td><a href="docs/commands-reference.md">Commands</a></td><td>All 86 slash commands</td></tr>
558
563
  <tr><td colspan="2"><strong>Usage</strong></td></tr>
559
564
  <tr><td><a href="docs/headless.md">Headless Mode</a></td><td>CLI flags, JSON/JSONL output, CI/CD integration</td></tr>
package/dist/index.js CHANGED
@@ -39274,7 +39274,7 @@ var package_default;
39274
39274
  var init_package = __esm(() => {
39275
39275
  package_default = {
39276
39276
  name: "@proxysoul/soulforge",
39277
- version: "2.7.0",
39277
+ version: "2.8.0",
39278
39278
  description: "Graph-powered code intelligence \u2014 multi-agent coding with codebase-aware AI",
39279
39279
  repository: {
39280
39280
  type: "git",
@@ -332602,6 +332602,7 @@ class RepoMap {
332602
332602
  onStaleSymbols = null;
332603
332603
  onError = null;
332604
332604
  indexErrors = 0;
332605
+ lastRenderedPaths = [];
332605
332606
  constructor(cwd) {
332606
332607
  this.cwd = cwd;
332607
332608
  const dbDir = join16(cwd, ".soulforge");
@@ -334860,6 +334861,7 @@ class RepoMap {
334860
334861
  this.onStaleSymbols?.(stale);
334861
334862
  }, 2000);
334862
334863
  }
334864
+ this.lastRenderedPaths = currentPaths;
334863
334865
  return lines.join(`
334864
334866
  `);
334865
334867
  }
@@ -335001,6 +335003,26 @@ class RepoMap {
335001
335003
  endLine: r.end_line
335002
335004
  }));
335003
335005
  }
335006
+ getFileDiffBlock(relPath) {
335007
+ const fileRow = this.db.query("SELECT id FROM files WHERE path = ?").get(relPath);
335008
+ if (!fileRow)
335009
+ return {
335010
+ blastRadius: 0,
335011
+ symbols: []
335012
+ };
335013
+ const blastRadius = this.db.query("SELECT COUNT(DISTINCT source_file_id) AS c FROM edges WHERE target_file_id = ?").get(fileRow.id)?.c ?? 0;
335014
+ const symbols = this.db.query(`SELECT s.name, s.kind, s.signature, s.line
335015
+ FROM symbols s
335016
+ WHERE s.file_id = ?
335017
+ AND s.is_exported = 1
335018
+ AND s.kind IN ('interface','type','class','function','enum','method')
335019
+ ORDER BY s.line
335020
+ LIMIT 10`).all(fileRow.id);
335021
+ return {
335022
+ blastRadius,
335023
+ symbols
335024
+ };
335025
+ }
335004
335026
  getFileSymbolRanges(relPath) {
335005
335027
  return this.db.query(`SELECT s.name, s.qualified_name, s.kind, s.line, s.end_line
335006
335028
  FROM symbols s JOIN files f ON f.id = s.file_id
@@ -355524,6 +355546,30 @@ async function autoResolveFile(client, symbol31, repoMap) {
355524
355546
  } catch {}
355525
355547
  return null;
355526
355548
  }
355549
+ async function buildAnnotation(filePath, repoMap) {
355550
+ if (!filePath || !repoMap?.isReady)
355551
+ return null;
355552
+ if (!repoMap.getFileBlastRadius || !repoMap.getFileCoChanges)
355553
+ return null;
355554
+ try {
355555
+ const cwd2 = process.cwd();
355556
+ const rel = filePath.startsWith(`${cwd2}/`) ? filePath.slice(cwd2.length + 1) : filePath;
355557
+ const [blastRadius, coChanges] = await Promise.all([repoMap.getFileBlastRadius(rel), repoMap.getFileCoChanges(rel)]);
355558
+ const parts2 = [];
355559
+ if (blastRadius >= 2)
355560
+ parts2.push(`\u2192${String(blastRadius)} dependents`);
355561
+ if (coChanges.length > 0) {
355562
+ const top = coChanges.slice(0, 3).map((c) => c.path.replace(/.*\//, "")).join(", ");
355563
+ parts2.push(`co-changes: ${top}`);
355564
+ }
355565
+ if (parts2.length === 0)
355566
+ return null;
355567
+ return `
355568
+ (${parts2.join(", ")})`;
355569
+ } catch {
355570
+ return null;
355571
+ }
355572
+ }
355527
355573
  var FILE_REQUIRED_ACTIONS, DEFINITION_KEYWORDS2, RG_FILE_TYPES = "*.{ts,tsx,js,jsx,mjs,cjs,py,go,rs,java,kt,kts,scala,c,h,cpp,hpp,cc,cxx,cs,rb,php,swift,ex,exs,dart,zig,lua}", navigateTool;
355528
355574
  var init_navigate = __esm(() => {
355529
355575
  init_intelligence();
@@ -355621,11 +355667,13 @@ Re-call with file set to the correct one.`,
355621
355667
  error: "not found"
355622
355668
  };
355623
355669
  }
355670
+ const defOutput = `Definition of '${symbol31}':
355671
+ ${tracked2.value.map(formatLocation).join(`
355672
+ `)}`;
355673
+ const annotation = await buildAnnotation(tracked2.value[0]?.file, repoMap);
355624
355674
  return {
355625
355675
  success: true,
355626
- output: `Definition of '${symbol31}':
355627
- ${tracked2.value.map(formatLocation).join(`
355628
- `)}`,
355676
+ output: annotation ? `${defOutput}${annotation}` : defOutput,
355629
355677
  backend: tracked2.backend
355630
355678
  };
355631
355679
  }
@@ -355657,11 +355705,13 @@ ${tracked2.value.map(formatLocation).join(`
355657
355705
  const capped = refs3.length > MAX_REFS ? refs3.slice(0, MAX_REFS) : refs3;
355658
355706
  const overflow = refs3.length > MAX_REFS ? `
355659
355707
  + ${String(refs3.length - MAX_REFS)} more \u2014 narrow your query` : "";
355708
+ const refsOutput = `References to '${symbol31}' (${String(refs3.length)}):
355709
+ ${capped.map(formatLocation).join(`
355710
+ `)}${overflow}`;
355711
+ const refsAnnotation = await buildAnnotation(resolvedFile, repoMap);
355660
355712
  return {
355661
355713
  success: true,
355662
- output: `References to '${symbol31}' (${String(refs3.length)}):
355663
- ${capped.map(formatLocation).join(`
355664
- `)}${overflow}`,
355714
+ output: refsAnnotation ? `${refsOutput}${refsAnnotation}` : refsOutput,
355665
355715
  backend: tracked2.backend
355666
355716
  };
355667
355717
  }
@@ -388084,6 +388134,9 @@ var init_intelligence_client = __esm(() => {
388084
388134
  async getFileBlastRadius(relPath) {
388085
388135
  return this.call("getFileBlastRadius", relPath);
388086
388136
  }
388137
+ async getFileDiffBlock(relPath) {
388138
+ return this.call("getFileDiffBlock", relPath);
388139
+ }
388087
388140
  async getFilesByPackage(pkg) {
388088
388141
  return this.call("getFilesByPackage", pkg);
388089
388142
  }
@@ -388334,6 +388387,7 @@ __export(exports_manager, {
388334
388387
  extractConversationTerms: () => extractConversationTerms,
388335
388388
  ContextManager: () => ContextManager
388336
388389
  });
388390
+ import { existsSync as existsSync23 } from "fs";
388337
388391
  import { readFile as readFile18 } from "fs/promises";
388338
388392
  import { join as join29 } from "path";
388339
388393
 
@@ -388357,7 +388411,10 @@ class ContextManager {
388357
388411
  conversationTokens = 0;
388358
388412
  contextWindowTokens = DEFAULT_CONTEXT_WINDOW2;
388359
388413
  repoMapCache = null;
388360
- soulMapDiffChangedFiles = new Set;
388414
+ soulMapDiffChangedFiles = new Map;
388415
+ soulMapDiffSeq = 0;
388416
+ soulMapSnapshotPaths = new Set;
388417
+ soulMapDiffBlocks = new Map;
388361
388418
  taskRouter;
388362
388419
  semanticSummaryLimit = 500;
388363
388420
  semanticAutoRegen = false;
@@ -388577,9 +388634,41 @@ class ContextManager {
388577
388634
  }
388578
388635
  this.editedFiles.add(absPath);
388579
388636
  const rel = absPath.startsWith(`${this.cwd}/`) ? absPath.slice(this.cwd.length + 1) : absPath;
388580
- this.soulMapDiffChangedFiles.add(rel);
388637
+ this.soulMapDiffChangedFiles.set(rel, ++this.soulMapDiffSeq);
388638
+ this.pendingSoulMapDiff = null;
388581
388639
  if (this.repoMapCache)
388582
388640
  this.repoMapCache.at = 0;
388641
+ if (this.repoMapReady) {
388642
+ this.prefetchDiffBlock(rel);
388643
+ }
388644
+ }
388645
+ prefetchDiffBlock(rel) {
388646
+ setTimeout(() => {
388647
+ this.repoMap.getFileDiffBlock(rel).then(({
388648
+ blastRadius,
388649
+ symbols
388650
+ }) => {
388651
+ const MAX_SYMBOLS = 8;
388652
+ const capped = symbols.slice(0, MAX_SYMBOLS);
388653
+ const parts2 = [];
388654
+ for (const s of capped) {
388655
+ const sig = s.signature ? s.signature.replace(/^export\s+(default\s+)?/, "").replace(/\s*\{[\s\S]*$/, "") : `${s.kind} ${s.name}`;
388656
+ parts2.push(` +${sig} :${String(s.line)}`);
388657
+ }
388658
+ if (symbols.length > MAX_SYMBOLS) {
388659
+ parts2.push(` ... +${String(symbols.length - MAX_SYMBOLS)} more exports`);
388660
+ }
388661
+ const radiusTag = blastRadius >= 2 ? ` (\u2192${String(blastRadius)})` : "";
388662
+ this.soulMapDiffBlocks.set(rel, {
388663
+ radiusTag,
388664
+ symbolBlock: parts2.join(`
388665
+ `)
388666
+ });
388667
+ if (!this.lastEmittedSoulMapDiff) {
388668
+ this.pendingSoulMapDiff = null;
388669
+ }
388670
+ }).catch(() => {});
388671
+ }, 300);
388583
388672
  }
388584
388673
  trackMentionedFile(absPath) {
388585
388674
  this.mentionedFiles.add(absPath);
@@ -388599,6 +388688,13 @@ class ContextManager {
388599
388688
  this.conversationTokens = 0;
388600
388689
  if (this.repoMapCache)
388601
388690
  this.repoMapCache.at = 0;
388691
+ this.repoMapGeneration++;
388692
+ this.soulMapDiffChangedFiles.clear();
388693
+ this.soulMapDiffSeq = 0;
388694
+ this.soulMapSnapshotPaths.clear();
388695
+ this.soulMapDiffBlocks.clear();
388696
+ this.pendingSoulMapDiff = null;
388697
+ this.lastEmittedSoulMapDiff = null;
388602
388698
  this.warmRepoMapCache();
388603
388699
  }
388604
388700
  repoMapRefreshing = false;
@@ -388620,7 +388716,7 @@ class ContextManager {
388620
388716
  return;
388621
388717
  this.repoMapRefreshing = true;
388622
388718
  try {
388623
- const content = await this.repoMap.render({
388719
+ const result = await this.repoMap.render({
388624
388720
  editorFile: this.editorFile,
388625
388721
  editedFiles: [...this.editedFiles],
388626
388722
  mentionedFiles: [...this.mentionedFiles],
@@ -388628,9 +388724,12 @@ class ContextManager {
388628
388724
  tokenBudget: this.repoMapTokenBudget
388629
388725
  });
388630
388726
  this.repoMapCache = {
388631
- content,
388727
+ content: result.content,
388632
388728
  at: Date.now()
388633
388729
  };
388730
+ if (this.soulMapSnapshotPaths.size === 0) {
388731
+ this.soulMapSnapshotPaths = new Set(result.paths);
388732
+ }
388634
388733
  } catch {}
388635
388734
  this.repoMapRefreshing = false;
388636
388735
  }
@@ -389187,33 +389286,63 @@ ${skillBlocks}
389187
389286
  const isMinimal = this.contextWindowTokens <= 32000;
389188
389287
  const treeLimit = this.repoMapTokenBudget ? Math.ceil(this.repoMapTokenBudget / 100) : 60;
389189
389288
  const dirTree = buildDirectoryTree(this.cwd, treeLimit);
389190
- if (clearDiffTracker)
389289
+ if (clearDiffTracker) {
389191
389290
  this.soulMapDiffChangedFiles.clear();
389291
+ this.soulMapDiffSeq = 0;
389292
+ this.soulMapDiffBlocks.clear();
389293
+ this.pendingSoulMapDiff = null;
389294
+ this.lastEmittedSoulMapDiff = null;
389295
+ }
389192
389296
  return buildSoulMapUserMessage(rendered, isMinimal, dirTree);
389193
389297
  }
389194
389298
  pendingSoulMapDiff = null;
389299
+ lastEmittedSoulMapDiff = null;
389195
389300
  buildSoulMapDiff() {
389196
- if (this.pendingSoulMapDiff)
389197
- return this.pendingSoulMapDiff;
389198
389301
  if (!this.isRepoMapReady())
389199
389302
  return null;
389200
389303
  if (this.soulMapDiffChangedFiles.size === 0)
389201
389304
  return null;
389202
- const changed = [...this.soulMapDiffChangedFiles];
389203
- const lines = ["<soul_map_update>"];
389204
- for (const file2 of changed.slice(0, 15)) {
389205
- lines.push(` ${file2}`);
389206
- }
389207
- if (changed.length > 15)
389208
- lines.push(` (+${String(changed.length - 15)} more)`);
389209
- lines.push("</soul_map_update>");
389210
- this.pendingSoulMapDiff = lines.join(`
389305
+ if (!this.pendingSoulMapDiff) {
389306
+ const changed = [...this.soulMapDiffChangedFiles.entries()].sort((a, b) => b[1] - a[1]).map(([path]) => path);
389307
+ const hasSnapshot = this.soulMapSnapshotPaths.size > 0;
389308
+ const lines = ["<soul_map_update>"];
389309
+ const MAX_RICH_BLOCKS = 5;
389310
+ let richBlockCount = 0;
389311
+ for (const file2 of changed.slice(0, 15)) {
389312
+ const absPath = join29(this.cwd, file2);
389313
+ const fileExists2 = existsSync23(absPath);
389314
+ const block = this.soulMapDiffBlocks.get(file2);
389315
+ if (!fileExists2) {
389316
+ lines.push(`- ${file2}`);
389317
+ } else if (hasSnapshot && !this.soulMapSnapshotPaths.has(file2)) {
389318
+ const tag = block ? `${file2}:${block.radiusTag} [NEW FILE]` : `${file2}: [NEW FILE]`;
389319
+ lines.push(tag);
389320
+ if (block?.symbolBlock && richBlockCount < MAX_RICH_BLOCKS) {
389321
+ lines.push(block.symbolBlock);
389322
+ richBlockCount++;
389323
+ }
389324
+ } else {
389325
+ const tag = block ? `${file2}:${block.radiusTag}` : `${file2}:`;
389326
+ lines.push(tag);
389327
+ if (block?.symbolBlock && richBlockCount < MAX_RICH_BLOCKS) {
389328
+ lines.push(block.symbolBlock);
389329
+ richBlockCount++;
389330
+ }
389331
+ }
389332
+ }
389333
+ if (changed.length > 15)
389334
+ lines.push(`(+${String(changed.length - 15)} more)`);
389335
+ lines.push("</soul_map_update>");
389336
+ this.pendingSoulMapDiff = lines.join(`
389211
389337
  `);
389338
+ }
389339
+ if (this.pendingSoulMapDiff === this.lastEmittedSoulMapDiff)
389340
+ return null;
389212
389341
  return this.pendingSoulMapDiff;
389213
389342
  }
389214
389343
  commitSoulMapDiff() {
389215
389344
  if (this.pendingSoulMapDiff) {
389216
- this.soulMapDiffChangedFiles.clear();
389345
+ this.lastEmittedSoulMapDiff = this.pendingSoulMapDiff;
389217
389346
  this.pendingSoulMapDiff = null;
389218
389347
  }
389219
389348
  }
@@ -389447,7 +389576,7 @@ function rebuildCoreMessages(messages) {
389447
389576
  }
389448
389577
 
389449
389578
  // src/core/sessions/manager.ts
389450
- import { existsSync as existsSync23, mkdirSync as mkdirSync9, readdirSync as readdirSync8, readFileSync as readFileSync13, renameSync as renameSync2, rmSync as rmSync4, statSync as statSync7, writeFileSync as writeFileSync8 } from "fs";
389579
+ import { existsSync as existsSync24, mkdirSync as mkdirSync9, readdirSync as readdirSync8, readFileSync as readFileSync13, renameSync as renameSync2, rmSync as rmSync4, statSync as statSync7, writeFileSync as writeFileSync8 } from "fs";
389451
389580
  import { rename as rename3, writeFile as writeFile13 } from "fs/promises";
389452
389581
  import { join as join30 } from "path";
389453
389582
 
@@ -389457,7 +389586,7 @@ class SessionManager {
389457
389586
  this.dir = join30(cwd2, ".soulforge", "sessions");
389458
389587
  }
389459
389588
  ensureDir() {
389460
- if (!existsSync23(this.dir)) {
389589
+ if (!existsSync24(this.dir)) {
389461
389590
  mkdirSync9(this.dir, {
389462
389591
  recursive: true
389463
389592
  });
@@ -389480,7 +389609,7 @@ class SessionManager {
389480
389609
  await io.saveSession(sessionDir, meta3, [...tabMessages.entries()]);
389481
389610
  return;
389482
389611
  } catch {}
389483
- if (!existsSync23(sessionDir)) {
389612
+ if (!existsSync24(sessionDir)) {
389484
389613
  mkdirSync9(sessionDir, {
389485
389614
  recursive: true,
389486
389615
  mode: 448
@@ -389529,13 +389658,13 @@ class SessionManager {
389529
389658
  loadSession(id) {
389530
389659
  const sessionDir = join30(this.dir, id);
389531
389660
  const metaPath = join30(sessionDir, "meta.json");
389532
- if (!existsSync23(metaPath))
389661
+ if (!existsSync24(metaPath))
389533
389662
  return null;
389534
389663
  try {
389535
389664
  const meta3 = JSON.parse(readFileSync13(metaPath, "utf-8"));
389536
389665
  const jsonlPath = join30(sessionDir, "messages.jsonl");
389537
389666
  const allMessages = [];
389538
- if (existsSync23(jsonlPath)) {
389667
+ if (existsSync24(jsonlPath)) {
389539
389668
  const content = readFileSync13(jsonlPath, "utf-8").trim();
389540
389669
  if (content) {
389541
389670
  for (const line2 of content.split(`
@@ -389601,21 +389730,21 @@ class SessionManager {
389601
389730
  };
389602
389731
  }
389603
389732
  findByPrefix(prefix) {
389604
- if (!existsSync23(this.dir))
389733
+ if (!existsSync24(this.dir))
389605
389734
  return null;
389606
389735
  const normalizedPrefix = prefix.toLowerCase();
389607
389736
  const entries2 = readdirSync8(this.dir);
389608
389737
  for (const entry of entries2) {
389609
389738
  if (entry.toLowerCase().startsWith(normalizedPrefix)) {
389610
389739
  const metaPath = join30(this.dir, entry, "meta.json");
389611
- if (existsSync23(metaPath))
389740
+ if (existsSync24(metaPath))
389612
389741
  return entry;
389613
389742
  }
389614
389743
  }
389615
389744
  return null;
389616
389745
  }
389617
389746
  listSessions() {
389618
- if (!existsSync23(this.dir))
389747
+ if (!existsSync24(this.dir))
389619
389748
  return [];
389620
389749
  try {
389621
389750
  const entries2 = readdirSync8(this.dir);
@@ -389627,7 +389756,7 @@ class SessionManager {
389627
389756
  if (!s.isDirectory())
389628
389757
  continue;
389629
389758
  const metaPath = join30(fullPath, "meta.json");
389630
- if (!existsSync23(metaPath))
389759
+ if (!existsSync24(metaPath))
389631
389760
  continue;
389632
389761
  const raw = readFileSync13(metaPath, "utf-8");
389633
389762
  const meta3 = JSON.parse(raw);
@@ -389650,7 +389779,7 @@ class SessionManager {
389650
389779
  saveSessionSync(meta3, tabMessages) {
389651
389780
  this.ensureDir();
389652
389781
  const sessionDir = join30(this.dir, meta3.id);
389653
- if (!existsSync23(sessionDir)) {
389782
+ if (!existsSync24(sessionDir)) {
389654
389783
  mkdirSync9(sessionDir, {
389655
389784
  recursive: true,
389656
389785
  mode: 448
@@ -389697,7 +389826,7 @@ class SessionManager {
389697
389826
  }
389698
389827
  deleteSession(id) {
389699
389828
  const dir = join30(this.dir, id);
389700
- if (!existsSync23(dir))
389829
+ if (!existsSync24(dir))
389701
389830
  return false;
389702
389831
  rmSync4(dir, {
389703
389832
  recursive: true
@@ -389705,7 +389834,7 @@ class SessionManager {
389705
389834
  return true;
389706
389835
  }
389707
389836
  clearAllSessions() {
389708
- if (!existsSync23(this.dir))
389837
+ if (!existsSync24(this.dir))
389709
389838
  return 0;
389710
389839
  const entries2 = readdirSync8(this.dir);
389711
389840
  let count = 0;
@@ -389721,12 +389850,12 @@ class SessionManager {
389721
389850
  return count;
389722
389851
  }
389723
389852
  totalSizeBytes() {
389724
- if (!existsSync23(this.dir))
389853
+ if (!existsSync24(this.dir))
389725
389854
  return 0;
389726
389855
  return this.listSessions().reduce((sum, s) => sum + s.sizeBytes, 0);
389727
389856
  }
389728
389857
  sessionCount() {
389729
- if (!existsSync23(this.dir))
389858
+ if (!existsSync24(this.dir))
389730
389859
  return 0;
389731
389860
  try {
389732
389861
  return readdirSync8(this.dir).filter((e) => {
@@ -408255,7 +408384,7 @@ __export(exports_instructions, {
408255
408384
  buildInstructionPrompt: () => buildInstructionPrompt,
408256
408385
  INSTRUCTION_SOURCES: () => INSTRUCTION_SOURCES
408257
408386
  });
408258
- import { existsSync as existsSync24, readFileSync as readFileSync14 } from "fs";
408387
+ import { existsSync as existsSync25, readFileSync as readFileSync14 } from "fs";
408259
408388
  import { join as join31 } from "path";
408260
408389
  function parseInstructionStructure(content) {
408261
408390
  const tokens = g.lexer(content);
@@ -408311,7 +408440,7 @@ function loadInstructions(cwd2, enabledIds) {
408311
408440
  continue;
408312
408441
  for (const file2 of source.files) {
408313
408442
  const fullPath = join31(cwd2, file2);
408314
- if (!existsSync24(fullPath))
408443
+ if (!existsSync25(fullPath))
408315
408444
  continue;
408316
408445
  try {
408317
408446
  const content = readFileSync14(fullPath, "utf-8").trim();
@@ -408393,7 +408522,7 @@ var init_instructions = __esm(() => {
408393
408522
  });
408394
408523
 
408395
408524
  // src/headless/run.ts
408396
- import { existsSync as existsSync25, readFileSync as readFileSync15 } from "fs";
408525
+ import { existsSync as existsSync26, readFileSync as readFileSync15 } from "fs";
408397
408526
  import { resolve as resolve34 } from "path";
408398
408527
  async function setupAgent(opts, merged) {
408399
408528
  const cwd2 = opts.cwd ?? process.cwd();
@@ -408698,7 +408827,7 @@ async function runPrompt(opts, merged) {
408698
408827
  const fileParts = [];
408699
408828
  for (const file2 of opts.include) {
408700
408829
  const fullPath = resolve34(env.cwd, file2);
408701
- if (!existsSync25(fullPath)) {
408830
+ if (!existsSync26(fullPath)) {
408702
408831
  stderrWarn(`--include file not found: ${file2}`);
408703
408832
  continue;
408704
408833
  }
@@ -409443,7 +409572,7 @@ __export(exports_icons, {
409443
409572
  function detectNerdFont() {
409444
409573
  try {
409445
409574
  const {
409446
- existsSync: existsSync26
409575
+ existsSync: existsSync27
409447
409576
  } = __require("fs");
409448
409577
  const {
409449
409578
  homedir: homedir18
@@ -409452,10 +409581,10 @@ function detectNerdFont() {
409452
409581
  join: join32
409453
409582
  } = __require("path");
409454
409583
  const fontDir = join32(homedir18(), "Library", "Fonts");
409455
- if (existsSync26(join32(fontDir, "SymbolsNerdFont-Regular.ttf")))
409584
+ if (existsSync27(join32(fontDir, "SymbolsNerdFont-Regular.ttf")))
409456
409585
  return true;
409457
409586
  const linuxFontDir = join32(homedir18(), ".local", "share", "fonts");
409458
- if (existsSync26(join32(linuxFontDir, "SymbolsNerdFont-Regular.ttf")))
409587
+ if (existsSync27(join32(linuxFontDir, "SymbolsNerdFont-Regular.ttf")))
409459
409588
  return true;
409460
409589
  } catch {}
409461
409590
  const term = process.env.TERM_PROGRAM?.toLowerCase() ?? "";
@@ -409885,7 +410014,7 @@ import { EventEmitter as EventEmitter2 } from "events";
409885
410014
  import { resolve as resolve35, dirname as dirname12 } from "path";
409886
410015
  import { fileURLToPath } from "url";
409887
410016
  import { resolve as resolve210, isAbsolute, parse as parse7 } from "path";
409888
- import { existsSync as existsSync26 } from "fs";
410017
+ import { existsSync as existsSync27 } from "fs";
409889
410018
  import { basename as basename8, join as join32 } from "path";
409890
410019
  import os from "os";
409891
410020
  import path from "path";
@@ -409897,7 +410026,7 @@ import { mkdir as mkdir8, readFile as readFile19, writeFile as writeFile14 } fro
409897
410026
  import * as path3 from "path";
409898
410027
  import { readdir as readdir7 } from "fs/promises";
409899
410028
  import { dlopen, toArrayBuffer as toArrayBuffer4, JSCallback, ptr as ptr4 } from "bun:ffi";
409900
- import { existsSync as existsSync27, writeFileSync as writeFileSync9 } from "fs";
410029
+ import { existsSync as existsSync28, writeFileSync as writeFileSync9 } from "fs";
409901
410030
  import { EventEmitter as EventEmitter4 } from "events";
409902
410031
  import { toArrayBuffer, ptr } from "bun:ffi";
409903
410032
  import { ptr as ptr2, toArrayBuffer as toArrayBuffer2 } from "bun:ffi";
@@ -424099,7 +424228,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
424099
424228
  worker_path = this.options.workerPath;
424100
424229
  } else {
424101
424230
  worker_path = new URL("./parser.worker.js", import.meta.url).href;
424102
- if (!existsSync26(resolve210(import.meta.dirname, "parser.worker.js"))) {
424231
+ if (!existsSync27(resolve210(import.meta.dirname, "parser.worker.js"))) {
424103
424232
  worker_path = new URL("./parser.worker.ts", import.meta.url).href;
424104
424233
  }
424105
424234
  }
@@ -424931,7 +425060,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
424931
425060
  if (isBunfsPath(targetLibPath)) {
424932
425061
  targetLibPath = targetLibPath.replace("../", "");
424933
425062
  }
424934
- if (!existsSync27(targetLibPath)) {
425063
+ if (!existsSync28(targetLibPath)) {
424935
425064
  throw new Error(`opentui is not supported on the current platform: ${process.platform}-${process.arch}`);
424936
425065
  }
424937
425066
  registerEnvVar({
@@ -453161,7 +453290,7 @@ var init_shallow2 = __esm(() => {
453161
453290
  });
453162
453291
 
453163
453292
  // src/core/commands/utils.ts
453164
- import { existsSync as existsSync28, readdirSync as readdirSync9, statSync as statSync8 } from "fs";
453293
+ import { existsSync as existsSync29, readdirSync as readdirSync9, statSync as statSync8 } from "fs";
453165
453294
  import { homedir as homedir18 } from "os";
453166
453295
  import { join as join35 } from "path";
453167
453296
  function sysMsg(ctx, content) {
@@ -453180,7 +453309,7 @@ function formatBytes(bytes) {
453180
453309
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
453181
453310
  }
453182
453311
  function dirSize(dirPath) {
453183
- if (!existsSync28(dirPath))
453312
+ if (!existsSync29(dirPath))
453184
453313
  return 0;
453185
453314
  let total = 0;
453186
453315
  for (const entry of readdirSync9(dirPath)) {
@@ -453580,7 +453709,7 @@ __export(exports_terminal_font, {
453580
453709
  detectTerminal: () => detectTerminal
453581
453710
  });
453582
453711
  import { execSync as execSync6 } from "child_process";
453583
- import { existsSync as existsSync30, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
453712
+ import { existsSync as existsSync31, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
453584
453713
  import { homedir as homedir19 } from "os";
453585
453714
  import { join as join36 } from "path";
453586
453715
  function detectTerminal() {
@@ -453688,7 +453817,7 @@ function getCurrentFont() {
453688
453817
  switch (term.id) {
453689
453818
  case "kitty": {
453690
453819
  const conf = join36(homedir19(), ".config", "kitty", "kitty.conf");
453691
- if (!existsSync30(conf))
453820
+ if (!existsSync31(conf))
453692
453821
  return null;
453693
453822
  const content = readFileSync16(conf, "utf-8");
453694
453823
  const match2 = content.match(/^font_family\s+(.+)$/m);
@@ -453704,7 +453833,7 @@ function getCurrentFont() {
453704
453833
  }
453705
453834
  case "ghostty": {
453706
453835
  const conf = join36(homedir19(), ".config", "ghostty", "config");
453707
- if (!existsSync30(conf))
453836
+ if (!existsSync31(conf))
453708
453837
  return null;
453709
453838
  const content = readFileSync16(conf, "utf-8");
453710
453839
  const match2 = content.match(/^font-family\s*=\s*(.+)$/m);
@@ -453712,7 +453841,7 @@ function getCurrentFont() {
453712
453841
  }
453713
453842
  case "foot": {
453714
453843
  const conf = join36(homedir19(), ".config", "foot", "foot.ini");
453715
- if (!existsSync30(conf))
453844
+ if (!existsSync31(conf))
453716
453845
  return null;
453717
453846
  const content = readFileSync16(conf, "utf-8");
453718
453847
  const match2 = content.match(/^font\s*=\s*([^:]+)/m);
@@ -453766,7 +453895,7 @@ function setKittyFont(family, size) {
453766
453895
  mkdirSync10(confDir, {
453767
453896
  recursive: true
453768
453897
  });
453769
- let content = existsSync30(conf) ? readFileSync16(conf, "utf-8") : "";
453898
+ let content = existsSync31(conf) ? readFileSync16(conf, "utf-8") : "";
453770
453899
  if (/^font_family\s/m.test(content)) {
453771
453900
  content = content.replace(/^font_family\s+.+$/m, `font_family ${family}`);
453772
453901
  } else {
@@ -453796,7 +453925,7 @@ ${content}`;
453796
453925
  }
453797
453926
  function findAlacrittyConfig() {
453798
453927
  const paths = [join36(homedir19(), ".config", "alacritty", "alacritty.toml"), join36(homedir19(), ".config", "alacritty", "alacritty.yml"), join36(homedir19(), ".alacritty.toml"), join36(homedir19(), ".alacritty.yml")];
453799
- return paths.find((p2) => existsSync30(p2)) ?? null;
453928
+ return paths.find((p2) => existsSync31(p2)) ?? null;
453800
453929
  }
453801
453930
  function setAlacrittyFont(family, size) {
453802
453931
  const confDir = join36(homedir19(), ".config", "alacritty");
@@ -453915,7 +454044,7 @@ function setGhosttyFont(family, size) {
453915
454044
  mkdirSync10(confDir, {
453916
454045
  recursive: true
453917
454046
  });
453918
- let content = existsSync30(conf) ? readFileSync16(conf, "utf-8") : "";
454047
+ let content = existsSync31(conf) ? readFileSync16(conf, "utf-8") : "";
453919
454048
  if (/^font-family\s*=/m.test(content)) {
453920
454049
  content = content.replace(/^font-family\s*=\s*.+$/m, `font-family = ${family}`);
453921
454050
  } else {
@@ -453941,7 +454070,7 @@ function setFootFont(family, size) {
453941
454070
  mkdirSync10(confDir, {
453942
454071
  recursive: true
453943
454072
  });
453944
- let content = existsSync30(conf) ? readFileSync16(conf, "utf-8") : `[main]
454073
+ let content = existsSync31(conf) ? readFileSync16(conf, "utf-8") : `[main]
453945
454074
  `;
453946
454075
  const fontLine = `font=${family}:size=${String(size)}`;
453947
454076
  if (/^font\s*=/m.test(content)) {
@@ -456382,7 +456511,7 @@ __export(exports_prerequisites, {
456382
456511
  checkPrerequisites: () => checkPrerequisites
456383
456512
  });
456384
456513
  import { execSync as execSync7 } from "child_process";
456385
- import { existsSync as existsSync31 } from "fs";
456514
+ import { existsSync as existsSync32 } from "fs";
456386
456515
  import { homedir as homedir20, platform as platform2 } from "os";
456387
456516
  import { join as join37 } from "path";
456388
456517
  function commandExists3(cmd) {
@@ -456404,7 +456533,7 @@ function lspExists(...cmds) {
456404
456533
  for (const cmd of cmds) {
456405
456534
  if (commandExists3(cmd))
456406
456535
  return true;
456407
- if (existsSync31(join37(MASON_BIN, cmd)))
456536
+ if (existsSync32(join37(MASON_BIN, cmd)))
456408
456537
  return true;
456409
456538
  }
456410
456539
  return false;
@@ -459236,7 +459365,7 @@ var init_session = __esm(() => {
459236
459365
 
459237
459366
  // src/core/commands/storage.ts
459238
459367
  import { Database as Database3 } from "bun:sqlite";
459239
- import { existsSync as existsSync32, rmSync as rmSync5 } from "fs";
459368
+ import { existsSync as existsSync33, rmSync as rmSync5 } from "fs";
459240
459369
  import { join as join39 } from "path";
459241
459370
  function openStorageMenu(ctx) {
459242
459371
  const show = () => {
@@ -459322,7 +459451,7 @@ function openStorageMenu(ctx) {
459322
459451
  sysMsg(ctx, `Cleared ${String(cleared)} sessions (freed ~${formatBytes(s2.sessions)}).`);
459323
459452
  } else if (value === "clear-history") {
459324
459453
  const historyPath = join39(s2.globalDir, "history.db");
459325
- if (existsSync32(historyPath) && s2.history > 0) {
459454
+ if (existsSync33(historyPath) && s2.history > 0) {
459326
459455
  try {
459327
459456
  const db = new Database3(historyPath);
459328
459457
  db.run("DELETE FROM history");
@@ -459335,7 +459464,7 @@ function openStorageMenu(ctx) {
459335
459464
  }
459336
459465
  } else if (value === "clear-plans") {
459337
459466
  const plansDir = join39(s2.projectDir, "plans");
459338
- if (existsSync32(plansDir) && s2.plans > 0) {
459467
+ if (existsSync33(plansDir) && s2.plans > 0) {
459339
459468
  rmSync5(plansDir, {
459340
459469
  recursive: true
459341
459470
  });
@@ -459345,7 +459474,7 @@ function openStorageMenu(ctx) {
459345
459474
  let freed = 0;
459346
459475
  const dbs = [join39(s2.projectDir, "repomap.db"), join39(s2.projectDir, "memory.db"), join39(s2.globalDir, "history.db"), join39(s2.globalDir, "memory.db")];
459347
459476
  for (const dbPath of dbs) {
459348
- if (!existsSync32(dbPath))
459477
+ if (!existsSync33(dbPath))
459349
459478
  continue;
459350
459479
  try {
459351
459480
  const before2 = fileSize(dbPath);
@@ -465478,6 +465607,80 @@ function resolveTaskModel(taskType, taskRouter, activeModel) {
465478
465607
  return taskRouter.default ?? activeModel;
465479
465608
  }
465480
465609
 
465610
+ // src/core/mcp/mempalace.ts
465611
+ function isConnected2() {
465612
+ try {
465613
+ return getMCPManager().isConnected(SERVER_NAME);
465614
+ } catch {
465615
+ return false;
465616
+ }
465617
+ }
465618
+ async function callTool(tool4, args2) {
465619
+ if (!isConnected2())
465620
+ return null;
465621
+ try {
465622
+ return await getMCPManager().callTool(SERVER_NAME, tool4, args2);
465623
+ } catch {
465624
+ return null;
465625
+ }
465626
+ }
465627
+ function saveCompactionDrawer(summary, projectName) {
465628
+ callTool("mempalace_add_drawer", {
465629
+ content: summary,
465630
+ wing: projectName,
465631
+ room: "compaction"
465632
+ });
465633
+ }
465634
+ function saveDecisions(decisions, projectName) {
465635
+ for (const decision of decisions) {
465636
+ callTool("mempalace_kg_add", {
465637
+ subject: projectName,
465638
+ predicate: "decided",
465639
+ object: decision
465640
+ });
465641
+ }
465642
+ }
465643
+ function saveDiscoveries(discoveries, projectName) {
465644
+ for (const discovery of discoveries) {
465645
+ callTool("mempalace_kg_add", {
465646
+ subject: projectName,
465647
+ predicate: "discovered",
465648
+ object: discovery
465649
+ });
465650
+ }
465651
+ }
465652
+ function saveFailures(failures, projectName) {
465653
+ for (const failure of failures) {
465654
+ callTool("mempalace_kg_add", {
465655
+ subject: projectName,
465656
+ predicate: "failed",
465657
+ object: failure
465658
+ });
465659
+ }
465660
+ }
465661
+ function writeDiary(agentName, entry) {
465662
+ callTool("mempalace_diary_write", {
465663
+ agent_name: agentName,
465664
+ entry
465665
+ });
465666
+ }
465667
+ function onCompaction(summary, state, cwd2) {
465668
+ if (!isConnected2())
465669
+ return;
465670
+ const projectName = cwd2.split("/").pop() ?? "unknown";
465671
+ saveCompactionDrawer(summary, projectName);
465672
+ if (state.decisions.length > 0)
465673
+ saveDecisions(state.decisions, projectName);
465674
+ if (state.discoveries.length > 0)
465675
+ saveDiscoveries(state.discoveries, projectName);
465676
+ if (state.failures.length > 0)
465677
+ saveFailures(state.failures, projectName);
465678
+ }
465679
+ var SERVER_NAME = "mempalace";
465680
+ var init_mempalace = __esm(() => {
465681
+ init_mcp2();
465682
+ });
465683
+
465481
465684
  // src/core/thinking-parser.ts
465482
465685
  function createThinkingParser() {
465483
465686
  let state = "outside";
@@ -466306,6 +466509,23 @@ function useChat({
466306
466509
  });
466307
466510
  summary = v2Result.summary;
466308
466511
  compactUsage = v2Result.usage;
466512
+ const ws = wsm.getState();
466513
+ onCompaction(summary, {
466514
+ decisions: [...ws.decisions],
466515
+ discoveries: [...ws.discoveries],
466516
+ failures: [...ws.failures]
466517
+ }, cwd2);
466518
+ if (ws.task) {
466519
+ const diaryParts = [`TASK:${ws.task}`];
466520
+ if (ws.decisions.length > 0)
466521
+ diaryParts.push(`DECISIONS:${ws.decisions.join("|")}`);
466522
+ if (ws.discoveries.length > 0)
466523
+ diaryParts.push(`DISCOVERIES:${ws.discoveries.join("|")}`);
466524
+ if (ws.failures.length > 0)
466525
+ diaryParts.push(`FAILURES:${ws.failures.join("|")}`);
466526
+ diaryParts.push(`FILES:${[...ws.files.keys()].slice(0, 10).join("|")}`);
466527
+ writeDiary("forge", diaryParts.join("|"));
466528
+ }
466309
466529
  wsm.reset();
466310
466530
  } else {
466311
466531
  const formatMessage = (m5, charLimit) => {
@@ -466498,7 +466718,7 @@ INCLUDE the plan progress above VERBATIM in ## Current State so the agent knows
466498
466718
  });
466499
466719
  }
466500
466720
  }
466501
- }, [setTokenUsage, effectiveConfig, contextManager]);
466721
+ }, [setTokenUsage, effectiveConfig, contextManager, cwd2]);
466502
466722
  summarizeConversationRef.current = summarizeConversation;
466503
466723
  const autoSummarizedRef = import_react33.useRef(false);
466504
466724
  import_react33.useEffect(() => {
@@ -468308,6 +468528,7 @@ var init_useChat = __esm(() => {
468308
468528
  init_models();
468309
468529
  init_provider();
468310
468530
  init_provider_options();
468531
+ init_mempalace();
468311
468532
  init_manager5();
468312
468533
  init_thinking_parser();
468313
468534
  init_file_events();
@@ -468341,7 +468562,7 @@ var init_useChat = __esm(() => {
468341
468562
 
468342
468563
  // src/core/history/db.ts
468343
468564
  import { Database as Database4 } from "bun:sqlite";
468344
- import { existsSync as existsSync33, mkdirSync as mkdirSync12 } from "fs";
468565
+ import { existsSync as existsSync34, mkdirSync as mkdirSync12 } from "fs";
468345
468566
  import { dirname as dirname16 } from "path";
468346
468567
 
468347
468568
  class HistoryDB {
@@ -468349,7 +468570,7 @@ class HistoryDB {
468349
468570
  writesSincePrune = 0;
468350
468571
  constructor(dbPath) {
468351
468572
  const dir = dirname16(dbPath);
468352
- if (!existsSync33(dir))
468573
+ if (!existsSync34(dir))
468353
468574
  mkdirSync12(dir, {
468354
468575
  recursive: true
468355
468576
  });
@@ -470005,7 +470226,7 @@ var init_LockInStreamView = __esm(async () => {
470005
470226
  });
470006
470227
 
470007
470228
  // src/core/utils/syntax.ts
470008
- import { existsSync as existsSync34, readdirSync as readdirSync10 } from "fs";
470229
+ import { existsSync as existsSync35, readdirSync as readdirSync10 } from "fs";
470009
470230
  import { homedir as homedir22 } from "os";
470010
470231
  import { dirname as dirname17, join as join42, resolve as resolve37 } from "path";
470011
470232
  function discoverParsers() {
@@ -470026,8 +470247,8 @@ function discoverParsers() {
470026
470247
  continue;
470027
470248
  const highlights = resolve37(langDir, "highlights.scm");
470028
470249
  const injections = resolve37(langDir, "injections.scm");
470029
- const hasHighlights = existsSync34(highlights);
470030
- const hasInjections = existsSync34(injections);
470250
+ const hasHighlights = existsSync35(highlights);
470251
+ const hasInjections = existsSync35(injections);
470031
470252
  if (!hasHighlights)
470032
470253
  continue;
470033
470254
  const parser = {
@@ -470084,14 +470305,14 @@ var init_syntax = __esm(async () => {
470084
470305
  if (IS_COMPILED3) {
470085
470306
  coreAssetsDir = bundledAssets;
470086
470307
  } else if (IS_DIST3) {
470087
- coreAssetsDir = existsSync34(distAssets) ? distAssets : bundledAssets;
470308
+ coreAssetsDir = existsSync35(distAssets) ? distAssets : bundledAssets;
470088
470309
  } else {
470089
470310
  try {
470090
470311
  coreAssetsDir = resolve37(dirname17(__require.resolve("@opentui/core")), "assets");
470091
470312
  } catch {
470092
470313
  coreAssetsDir = bundledAssets;
470093
470314
  }
470094
- if (!existsSync34(coreAssetsDir))
470315
+ if (!existsSync35(coreAssetsDir))
470095
470316
  coreAssetsDir = bundledAssets;
470096
470317
  }
470097
470318
  MARKDOWN_INJECTION_MAP = {
@@ -470150,7 +470371,7 @@ var init_syntax = __esm(async () => {
470150
470371
  } else if (IS_DIST3) {
470151
470372
  try {
470152
470373
  const coreWorker = resolve37(dirname17(__require.resolve("@opentui/core")), "parser.worker.js");
470153
- if (existsSync34(coreWorker)) {
470374
+ if (existsSync35(coreWorker)) {
470154
470375
  process.env.OTUI_TREE_SITTER_WORKER_PATH = coreWorker;
470155
470376
  }
470156
470377
  } catch {}
@@ -501930,7 +502151,7 @@ var init_EditorSettings = __esm(async () => {
501930
502151
 
501931
502152
  // src/core/intelligence/backends/lsp/installer.ts
501932
502153
  import { execSync as execSync8, spawn as spawn17 } from "child_process";
501933
- import { chmodSync as chmodSync4, existsSync as existsSync35, mkdirSync as mkdirSync13, readFileSync as readFileSync18, unlinkSync as unlinkSync6, writeFileSync as writeFileSync12 } from "fs";
502154
+ import { chmodSync as chmodSync4, existsSync as existsSync36, mkdirSync as mkdirSync13, readFileSync as readFileSync18, unlinkSync as unlinkSync6, writeFileSync as writeFileSync12 } from "fs";
501934
502155
  import { homedir as homedir23 } from "os";
501935
502156
  import { join as join45 } from "path";
501936
502157
  function parsePurl(id) {
@@ -501988,14 +502209,14 @@ function getToolchainRequirement(method) {
501988
502209
  function loadRegistry() {
501989
502210
  if (registryCache)
501990
502211
  return registryCache;
501991
- if (existsSync35(MASON_REGISTRY_LOCAL)) {
502212
+ if (existsSync36(MASON_REGISTRY_LOCAL)) {
501992
502213
  try {
501993
502214
  const raw2 = readFileSync18(MASON_REGISTRY_LOCAL, "utf-8");
501994
502215
  registryCache = JSON.parse(raw2);
501995
502216
  return registryCache;
501996
502217
  } catch {}
501997
502218
  }
501998
- if (existsSync35(REGISTRY_CACHE)) {
502219
+ if (existsSync36(REGISTRY_CACHE)) {
501999
502220
  try {
502000
502221
  const raw2 = readFileSync18(REGISTRY_CACHE, "utf-8");
502001
502222
  registryCache = JSON.parse(raw2);
@@ -502088,17 +502309,17 @@ function checkPackageStatus(pkg) {
502088
502309
  source = "PATH";
502089
502310
  break;
502090
502311
  }
502091
- if (existsSync35(join45(SOULFORGE_LSP_DIR, "node_modules", ".bin", bin))) {
502312
+ if (existsSync36(join45(SOULFORGE_LSP_DIR, "node_modules", ".bin", bin))) {
502092
502313
  installed = true;
502093
502314
  source = "soulforge";
502094
502315
  break;
502095
502316
  }
502096
- if (existsSync35(join45(SOULFORGE_LSP_DIR, "bin", bin))) {
502317
+ if (existsSync36(join45(SOULFORGE_LSP_DIR, "bin", bin))) {
502097
502318
  installed = true;
502098
502319
  source = "soulforge";
502099
502320
  break;
502100
502321
  }
502101
- if (existsSync35(join45(MASON_BIN_DIR2, bin))) {
502322
+ if (existsSync36(join45(MASON_BIN_DIR2, bin))) {
502102
502323
  installed = true;
502103
502324
  source = "mason";
502104
502325
  break;
@@ -502183,7 +502404,7 @@ async function installPackage(pkg, onProgress) {
502183
502404
  return "bun";
502184
502405
  } catch {
502185
502406
  const sfBin = join45(homedir23(), ".soulforge", "bin", "bun");
502186
- if (existsSync35(sfBin))
502407
+ if (existsSync36(sfBin))
502187
502408
  return sfBin;
502188
502409
  return "bun";
502189
502410
  }
@@ -502267,7 +502488,7 @@ PYTHONPATH="${pipDir}:$PYTHONPATH" exec python3 -m ${purl.name.replace(/-/g, "_"
502267
502488
  const resolvedBin = binPath.includes("{{") ? asset.bin ?? binName : binPath;
502268
502489
  const candidates = [join45(tmpDir, resolvedBin), join45(tmpDir, binName), join45(tmpDir, purl.name, resolvedBin), join45(tmpDir, purl.name, binName)];
502269
502490
  for (const candidate of candidates) {
502270
- if (existsSync35(candidate)) {
502491
+ if (existsSync36(candidate)) {
502271
502492
  const {
502272
502493
  copyFileSync
502273
502494
  } = await import("fs");
@@ -502494,7 +502715,7 @@ var init_installer = __esm(() => {
502494
502715
  });
502495
502716
 
502496
502717
  // src/components/settings/LspInstallSearch.tsx
502497
- import { existsSync as existsSync36 } from "fs";
502718
+ import { existsSync as existsSync37 } from "fs";
502498
502719
  import { join as join46 } from "path";
502499
502720
  function methodLabel(status) {
502500
502721
  if (status.requiresToolchain && !status.toolchainAvailable) {
@@ -502732,7 +502953,7 @@ function LspInstallSearch({
502732
502953
  const defaultScopeCursor = detectScope("disabledLspServers") === "project" ? 0 : 1;
502733
502954
  const [scopeCursor, setScopeCursor] = import_react110.useState(defaultScopeCursor);
502734
502955
  const downloadAttemptedRef = import_react110.useRef(false);
502735
- const isInProject = existsSync36(join46(cwd2, ".git"));
502956
+ const isInProject = existsSync37(join46(cwd2, ".git"));
502736
502957
  const {
502737
502958
  width: termCols,
502738
502959
  height: termRows
@@ -509278,7 +509499,7 @@ var init_RouterSettings = __esm(async () => {
509278
509499
  });
509279
509500
 
509280
509501
  // src/components/settings/SkillSearch.tsx
509281
- import { existsSync as existsSync37 } from "fs";
509502
+ import { existsSync as existsSync38 } from "fs";
509282
509503
  import { join as join47 } from "path";
509283
509504
  function SearchSkillRow(t0) {
509284
509505
  const $5 = import_compiler_runtime69.c(33);
@@ -509753,7 +509974,7 @@ function SkillSearch(t0) {
509753
509974
  const debounceRef = import_react120.useRef(null);
509754
509975
  let t5;
509755
509976
  if ($5[4] === Symbol.for("react.memo_cache_sentinel")) {
509756
- t5 = existsSync37(join47(process.cwd(), ".git"));
509977
+ t5 = existsSync38(join47(process.cwd(), ".git"));
509757
509978
  $5[4] = t5;
509758
509979
  } else {
509759
509980
  t5 = $5[4];
@@ -512522,7 +512743,7 @@ var init_App = __esm(async () => {
512522
512743
  init_theme();
512523
512744
  init_splash();
512524
512745
  init_errors();
512525
- import { existsSync as existsSync38, readFileSync as readFileSync19 } from "fs";
512746
+ import { existsSync as existsSync39, readFileSync as readFileSync19 } from "fs";
512526
512747
  import { homedir as homedir24 } from "os";
512527
512748
  import { join as join49 } from "path";
512528
512749
  globalThis.AI_SDK_LOG_WARNINGS = false;
@@ -512541,7 +512762,7 @@ if (hasCli) {
512541
512762
  var isCompiledBinary = import.meta.url.includes("$bunfs");
512542
512763
  if (isCompiledBinary) {
512543
512764
  const bundledWorker = join49(homedir24(), ".soulforge", "opentui-assets", "parser.worker.js");
512544
- if (!process.env.OTUI_TREE_SITTER_WORKER_PATH && existsSync38(bundledWorker)) {
512765
+ if (!process.env.OTUI_TREE_SITTER_WORKER_PATH && existsSync39(bundledWorker)) {
512545
512766
  process.env.OTUI_TREE_SITTER_WORKER_PATH = bundledWorker;
512546
512767
  }
512547
512768
  }
@@ -24160,6 +24160,7 @@ class RepoMap {
24160
24160
  onStaleSymbols = null;
24161
24161
  onError = null;
24162
24162
  indexErrors = 0;
24163
+ lastRenderedPaths = [];
24163
24164
  constructor(cwd) {
24164
24165
  this.cwd = cwd;
24165
24166
  const dbDir = join5(cwd, ".soulforge");
@@ -26418,6 +26419,7 @@ class RepoMap {
26418
26419
  this.onStaleSymbols?.(stale);
26419
26420
  }, 2000);
26420
26421
  }
26422
+ this.lastRenderedPaths = currentPaths;
26421
26423
  return lines.join(`
26422
26424
  `);
26423
26425
  }
@@ -26559,6 +26561,26 @@ class RepoMap {
26559
26561
  endLine: r4.end_line
26560
26562
  }));
26561
26563
  }
26564
+ getFileDiffBlock(relPath) {
26565
+ const fileRow = this.db.query("SELECT id FROM files WHERE path = ?").get(relPath);
26566
+ if (!fileRow)
26567
+ return {
26568
+ blastRadius: 0,
26569
+ symbols: []
26570
+ };
26571
+ const blastRadius = this.db.query("SELECT COUNT(DISTINCT source_file_id) AS c FROM edges WHERE target_file_id = ?").get(fileRow.id)?.c ?? 0;
26572
+ const symbols = this.db.query(`SELECT s.name, s.kind, s.signature, s.line
26573
+ FROM symbols s
26574
+ WHERE s.file_id = ?
26575
+ AND s.is_exported = 1
26576
+ AND s.kind IN ('interface','type','class','function','enum','method')
26577
+ ORDER BY s.line
26578
+ LIMIT 10`).all(fileRow.id);
26579
+ return {
26580
+ blastRadius,
26581
+ symbols
26582
+ };
26583
+ }
26562
26584
  getFileSymbolRanges(relPath) {
26563
26585
  return this.db.query(`SELECT s.name, s.qualified_name, s.kind, s.line, s.end_line
26564
26586
  FROM symbols s JOIN files f ON f.id = s.file_id
@@ -273054,7 +273076,14 @@ var handlers = {
273054
273076
  },
273055
273077
  onFileChanged: (absPath) => requireRepoMap().onFileChanged(absPath),
273056
273078
  recheckModifiedFiles: () => requireRepoMap().recheckModifiedFiles(),
273057
- render: (opts) => requireRepoMap().render(opts),
273079
+ render: (opts) => {
273080
+ const repoMap2 = requireRepoMap();
273081
+ const content = repoMap2.render(opts);
273082
+ return {
273083
+ content,
273084
+ paths: repoMap2.lastRenderedPaths
273085
+ };
273086
+ },
273058
273087
  findSymbols: (name2) => requireRepoMap().findSymbols(name2),
273059
273088
  findSymbol: (name2) => requireRepoMap().findSymbol(name2),
273060
273089
  searchSymbolsSubstring: (query, limit) => requireRepoMap().searchSymbolsSubstring(query, limit),
@@ -273069,6 +273098,7 @@ var handlers = {
273069
273098
  getFileCoChanges: (relPath) => requireRepoMap().getFileCoChanges(relPath),
273070
273099
  getFileExportCount: (relPath) => requireRepoMap().getFileExportCount(relPath),
273071
273100
  getFileBlastRadius: (relPath) => requireRepoMap().getFileBlastRadius(relPath),
273101
+ getFileDiffBlock: (relPath) => requireRepoMap().getFileDiffBlock(relPath),
273072
273102
  getFilesByPackage: (pkg) => requireRepoMap().getFilesByPackage(pkg),
273073
273103
  listDirectory: (dirPath) => requireRepoMap().listDirectory(dirPath),
273074
273104
  getIdentifierFrequency: (limit) => requireRepoMap().getIdentifierFrequency(limit),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proxysoul/soulforge",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "Graph-powered code intelligence — multi-agent coding with codebase-aware AI",
5
5
  "repository": {
6
6
  "type": "git",