@proxysoul/soulforge 2.15.7 → 2.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25911,67 +25911,70 @@ class RepoMap {
25911
25911
  async generateSemanticSummaries(maxSymbols = 500) {
25912
25912
  if (!this.summaryGenerator || !this.ready)
25913
25913
  return 0;
25914
- const topSymbols = this.db.query(`SELECT s.id AS sym_id, s.name, s.kind, s.signature, s.line, s.end_line,
25915
- f.path AS file_path, f.id AS file_id, f.mtime_ms AS file_mtime
25916
- FROM symbols s
25917
- JOIN files f ON f.id = s.file_id
25918
- WHERE s.is_exported = 1
25919
- AND s.kind IN ('function', 'method', 'class')
25920
- ORDER BY f.pagerank DESC, s.line ASC
25921
- LIMIT ?`).all(maxSymbols);
25922
- const existingById = new Map;
25923
- const existingByKey = new Map;
25924
- for (const row of this.db.query("SELECT symbol_id, file_mtime, file_path, symbol_name FROM semantic_summaries WHERE source = 'llm'").all()) {
25925
- existingById.set(row.symbol_id, row.file_mtime);
25926
- if (row.file_path && row.symbol_name) {
25927
- existingByKey.set(`${row.file_path}\x00${row.symbol_name}`, row.file_mtime);
25928
- }
25929
- }
25930
- const needed = [];
25931
- for (const sym of topSymbols) {
25932
- const cachedMtime = existingById.get(sym.sym_id) ?? existingByKey.get(`${sym.file_path}\x00${sym.name}`);
25933
- if (cachedMtime === sym.file_mtime)
25934
- continue;
25935
- const absPath = join6(this.cwd, sym.file_path);
25936
- let code = "";
25937
- let lineSpan = sym.end_line - sym.line;
25914
+ const PAGE_SIZE = 100;
25915
+ const BATCH_SIZE = 10;
25916
+ const SNIPPET_BUDGET = 2000;
25917
+ const BODY_SCAN_LIMIT = 80;
25918
+ const MIN_LINE_SPAN = 5;
25919
+ const candidates = this.db.query(`SELECT s.id AS sym_id, s.name, s.kind, s.signature, s.line, s.end_line,
25920
+ f.path AS file_path, f.mtime_ms AS file_mtime
25921
+ FROM symbols s
25922
+ JOIN files f ON f.id = s.file_id
25923
+ WHERE s.is_exported = 1
25924
+ AND s.kind IN ('function', 'method', 'class')
25925
+ ORDER BY f.pagerank DESC, s.line ASC
25926
+ LIMIT ? OFFSET ?`);
25927
+ const summaryByIdQuery = this.db.prepare("SELECT file_mtime FROM semantic_summaries WHERE source = 'llm' AND symbol_id = ?");
25928
+ const summaryByKeyQuery = this.db.prepare("SELECT file_mtime FROM semantic_summaries WHERE source = 'llm' AND file_path = ? AND symbol_name = ?");
25929
+ const upsert = this.db.prepare(`INSERT OR REPLACE INTO semantic_summaries (symbol_id, source, summary, file_mtime, file_path, symbol_name)
25930
+ VALUES (?, 'llm', ?, ?, ?, ?)`);
25931
+ const symExists = this.db.prepare("SELECT 1 FROM symbols WHERE id = ?");
25932
+ const prepareSymbol = (sym) => {
25933
+ const byId = summaryByIdQuery.get(sym.sym_id);
25934
+ if (byId && byId.file_mtime === sym.file_mtime)
25935
+ return null;
25936
+ if (!byId) {
25937
+ const byKey = summaryByKeyQuery.get(sym.file_path, sym.name);
25938
+ if (byKey && byKey.file_mtime === sym.file_mtime)
25939
+ return null;
25940
+ }
25941
+ let content;
25938
25942
  try {
25939
- const content = readFileSync3(absPath, "utf-8");
25940
- const lines = content.split(`
25943
+ content = readFileSync3(join6(this.cwd, sym.file_path), "utf-8");
25944
+ } catch {
25945
+ return null;
25946
+ }
25947
+ const lines = content.split(`
25941
25948
  `);
25942
- const startLine = Math.max(0, sym.line - 1);
25943
- let endLine = sym.end_line;
25944
- if (endLine <= sym.line) {
25945
- const limit = Math.min(startLine + 80, lines.length);
25946
- let depth = 0;
25947
- for (let k3 = startLine;k3 < limit; k3++) {
25948
- const l3 = lines[k3] ?? "";
25949
- for (const ch of l3) {
25950
- if (ch === "{" || ch === "(")
25951
- depth++;
25952
- else if (ch === "}" || ch === ")")
25953
- depth--;
25954
- }
25955
- if (depth <= 0 && k3 > startLine) {
25956
- endLine = k3 + 1;
25957
- break;
25958
- }
25949
+ const startLine = Math.max(0, sym.line - 1);
25950
+ let endLine = sym.end_line;
25951
+ if (endLine <= sym.line) {
25952
+ const limit = Math.min(startLine + BODY_SCAN_LIMIT, lines.length);
25953
+ let depth = 0;
25954
+ for (let k3 = startLine;k3 < limit; k3++) {
25955
+ const l3 = lines[k3] ?? "";
25956
+ for (const ch of l3) {
25957
+ if (ch === "{" || ch === "(")
25958
+ depth++;
25959
+ else if (ch === "}" || ch === ")")
25960
+ depth--;
25961
+ }
25962
+ if (depth <= 0 && k3 > startLine) {
25963
+ endLine = k3 + 1;
25964
+ break;
25959
25965
  }
25960
- if (endLine <= sym.line)
25961
- endLine = Math.min(startLine + 20, lines.length);
25962
25966
  }
25963
- endLine = Math.min(lines.length, endLine);
25964
- lineSpan = endLine - startLine;
25965
- if (lineSpan < 5)
25966
- continue;
25967
- const snippet = lines.slice(startLine, endLine).join(`
25968
- `);
25969
- code = snippet.length > 2000 ? `${snippet.slice(0, 2000)}...` : snippet;
25970
- } catch {
25971
- continue;
25967
+ if (endLine <= sym.line)
25968
+ endLine = Math.min(startLine + 20, lines.length);
25972
25969
  }
25973
- const dependents = this.getFileBlastRadius(sym.file_path);
25974
- needed.push({
25970
+ endLine = Math.min(lines.length, endLine);
25971
+ const lineSpan = endLine - startLine;
25972
+ if (lineSpan < MIN_LINE_SPAN)
25973
+ return null;
25974
+ const snippet = lines.slice(startLine, endLine).join(`
25975
+ `);
25976
+ const code = snippet.length > SNIPPET_BUDGET ? `${snippet.slice(0, SNIPPET_BUDGET)}...` : snippet;
25977
+ return {
25975
25978
  symId: sym.sym_id,
25976
25979
  name: sym.name,
25977
25980
  kind: sym.kind,
@@ -25979,19 +25982,13 @@ class RepoMap {
25979
25982
  code,
25980
25983
  filePath: sym.file_path,
25981
25984
  fileMtime: sym.file_mtime,
25982
- dependents,
25985
+ dependents: this.getFileBlastRadius(sym.file_path),
25983
25986
  lineSpan
25984
- });
25985
- }
25986
- if (needed.length === 0)
25987
- return 0;
25988
- const upsert = this.db.prepare(`INSERT OR REPLACE INTO semantic_summaries (symbol_id, source, summary, file_mtime, file_path, symbol_name)
25989
- VALUES (?, 'llm', ?, ?, ?, ?)`);
25990
- const symExists = this.db.prepare("SELECT 1 FROM symbols WHERE id = ?");
25991
- let count = 0;
25992
- const SAVE_CHUNK = 10;
25993
- for (let ci = 0;ci < needed.length; ci += SAVE_CHUNK) {
25994
- const chunk = needed.slice(ci, ci + SAVE_CHUNK);
25987
+ };
25988
+ };
25989
+ const flushChunk = async (chunk) => {
25990
+ if (!this.summaryGenerator || chunk.length === 0)
25991
+ return 0;
25995
25992
  const batch = chunk.map((s2) => ({
25996
25993
  name: s2.name,
25997
25994
  kind: s2.kind,
@@ -26001,25 +25998,65 @@ class RepoMap {
26001
25998
  dependents: s2.dependents,
26002
25999
  lineSpan: s2.lineSpan
26003
26000
  }));
26004
- const results = await this.summaryGenerator(batch, needed.length);
26001
+ const results = await this.summaryGenerator(batch, maxSymbols);
26005
26002
  if (results.length === 0)
26006
- break;
26007
- const summaryMap = new Map;
26008
- const summaryMapLower = new Map;
26003
+ return -1;
26004
+ const lookup = new Map;
26009
26005
  for (const r4 of results) {
26010
- summaryMap.set(r4.name, r4.summary);
26011
- summaryMapLower.set(r4.name.toLowerCase(), r4.summary);
26006
+ lookup.set(r4.name, r4.summary);
26007
+ const lc = r4.name.toLowerCase();
26008
+ if (!lookup.has(lc))
26009
+ lookup.set(lc, r4.summary);
26012
26010
  }
26011
+ let written = 0;
26013
26012
  const tx = this.db.transaction(() => {
26014
26013
  for (const sym of chunk) {
26015
- const summary = summaryMap.get(sym.name) ?? summaryMapLower.get(sym.name.toLowerCase());
26014
+ const summary = lookup.get(sym.name) ?? lookup.get(sym.name.toLowerCase());
26016
26015
  if (summary && symExists.get(sym.symId)) {
26017
26016
  upsert.run(sym.symId, summary, sym.fileMtime, sym.filePath, sym.name);
26018
- count++;
26017
+ written++;
26019
26018
  }
26020
26019
  }
26021
26020
  });
26022
26021
  tx();
26022
+ return written;
26023
+ };
26024
+ let count = 0;
26025
+ let offset = 0;
26026
+ let pending = [];
26027
+ let processed = 0;
26028
+ let aborted = false;
26029
+ outer:
26030
+ while (processed < maxSymbols) {
26031
+ const page = candidates.all(PAGE_SIZE, offset);
26032
+ if (page.length === 0)
26033
+ break;
26034
+ offset += page.length;
26035
+ for (const row of page) {
26036
+ if (processed >= maxSymbols)
26037
+ break outer;
26038
+ const prepared = prepareSymbol(row);
26039
+ if (!prepared)
26040
+ continue;
26041
+ pending.push(prepared);
26042
+ processed++;
26043
+ if (pending.length >= BATCH_SIZE) {
26044
+ const chunk = pending;
26045
+ pending = [];
26046
+ const written = await flushChunk(chunk);
26047
+ if (written < 0) {
26048
+ aborted = true;
26049
+ break outer;
26050
+ }
26051
+ count += written;
26052
+ }
26053
+ }
26054
+ }
26055
+ if (!aborted && pending.length > 0) {
26056
+ const written = await flushChunk(pending);
26057
+ if (written > 0)
26058
+ count += written;
26059
+ pending = [];
26023
26060
  }
26024
26061
  if (count > 0) {
26025
26062
  try {
@@ -1690,6 +1690,7 @@ __export(exports_status, {
1690
1690
  gitStash: () => gitStash,
1691
1691
  gitShow: () => gitShow,
1692
1692
  gitRestore: () => gitRestore,
1693
+ gitResetHard: () => gitResetHard,
1693
1694
  gitReset: () => gitReset,
1694
1695
  gitRebase: () => gitRebase,
1695
1696
  gitPush: () => gitPush,
@@ -2318,6 +2319,16 @@ async function buildGitContext(cwd) {
2318
2319
  return lines.join(`
2319
2320
  `);
2320
2321
  }
2322
+ async function gitResetHard(cwd) {
2323
+ const {
2324
+ ok,
2325
+ stdout
2326
+ } = await run(["reset", "--hard", "HEAD"], cwd);
2327
+ return {
2328
+ ok,
2329
+ output: stdout
2330
+ };
2331
+ }
2321
2332
  var encoder, NAMED_ESCAPES, CO_AUTHOR_LINE = "Co-Authored-By: SoulForge <soulforge@proxysoul.com>", _coAuthorEnabled = true;
2322
2333
  var init_status = __esm(() => {
2323
2334
  init_spawn();
@@ -42623,7 +42634,8 @@ var init_ui = __esm(() => {
42623
42634
  reasoningExpanded: {},
42624
42635
  suspended: false,
42625
42636
  editorSplit: 60,
42626
- lockIn: true,
42637
+ verboseByTab: {},
42638
+ messageToolExpanded: {},
42627
42639
  openModal: (name21) => set2(() => ({
42628
42640
  modals: {
42629
42641
  ...INITIAL_MODALS,
@@ -42736,12 +42748,66 @@ var init_ui = __esm(() => {
42736
42748
  setSuspended: (v) => set2({
42737
42749
  suspended: v
42738
42750
  }),
42739
- setLockIn: (v) => set2({
42740
- lockIn: v
42741
- }),
42742
- toggleLockIn: () => set2((s) => ({
42743
- lockIn: !s.lockIn
42751
+ setTabVerbose: (tabId, v) => set2((s) => ({
42752
+ verboseByTab: {
42753
+ ...s.verboseByTab,
42754
+ [tabId]: v
42755
+ }
42756
+ })),
42757
+ toggleTabVerbose: (tabId) => set2((s) => ({
42758
+ verboseByTab: {
42759
+ ...s.verboseByTab,
42760
+ [tabId]: !s.verboseByTab[tabId]
42761
+ }
42744
42762
  })),
42763
+ ensureTabVerboseDefault: (tabId, def) => set2((s) => {
42764
+ if (s.verboseByTab[tabId] !== undefined)
42765
+ return {};
42766
+ return {
42767
+ verboseByTab: {
42768
+ ...s.verboseByTab,
42769
+ [tabId]: def ?? false
42770
+ }
42771
+ };
42772
+ }),
42773
+ pruneTabVerbose: (tabId) => set2((s) => {
42774
+ const {
42775
+ [tabId]: _v,
42776
+ ...verboseByTab
42777
+ } = s.verboseByTab;
42778
+ return {
42779
+ verboseByTab
42780
+ };
42781
+ }),
42782
+ toggleMessageTool: (msgId, toolId) => set2((s) => {
42783
+ const cur = s.messageToolExpanded[msgId] ?? {};
42784
+ return {
42785
+ messageToolExpanded: {
42786
+ ...s.messageToolExpanded,
42787
+ [msgId]: {
42788
+ ...cur,
42789
+ [toolId]: !cur[toolId]
42790
+ }
42791
+ }
42792
+ };
42793
+ }),
42794
+ pruneMessageTools: (msgIds) => set2((s) => {
42795
+ if (msgIds.length === 0)
42796
+ return {};
42797
+ const next = {
42798
+ ...s.messageToolExpanded
42799
+ };
42800
+ let changed = false;
42801
+ for (const id of msgIds) {
42802
+ if (id in next) {
42803
+ delete next[id];
42804
+ changed = true;
42805
+ }
42806
+ }
42807
+ return changed ? {
42808
+ messageToolExpanded: next
42809
+ } : {};
42810
+ }),
42745
42811
  cycleEditorSplit: () => set2((s) => {
42746
42812
  const splits = [40, 50, 60, 70];
42747
42813
  const idx = splits.indexOf(s.editorSplit);
@@ -50511,7 +50577,7 @@ var package_default;
50511
50577
  var init_package = __esm(() => {
50512
50578
  package_default = {
50513
50579
  name: "@proxysoul/soulforge",
50514
- version: "2.15.7",
50580
+ version: "2.16.1",
50515
50581
  description: "Graph-powered code intelligence \u2014 multi-agent coding with codebase-aware AI",
50516
50582
  repository: {
50517
50583
  type: "git",
@@ -81579,7 +81645,20 @@ function supportsTemperature(modelId) {
81579
81645
  return true;
81580
81646
  return v.major < 5 && (v.major < 4 || v.minor < 7);
81581
81647
  }
81582
- var NO_SUPPORT, ANTHROPIC_FULL, OPENAI_FULL, GOOGLE_FULL, XAI_FULL, DEEPSEEK_FULL, OPENROUTER_FULL, GATEWAY_FULL, COMPAT_ONLY, PROVIDER_CONSTRAINTS;
81648
+ function buildCacheProviderOptions(ttl) {
81649
+ const cache = ttl === "1h" ? CACHE_EPHEMERAL_1H : CACHE_EPHEMERAL_5M;
81650
+ return {
81651
+ anthropic: cache,
81652
+ google: cache,
81653
+ proxy: cache,
81654
+ llmgateway: cache,
81655
+ opencode_zen: cache,
81656
+ opencode_go: cache,
81657
+ openrouter: cache,
81658
+ vercel_gateway: cache
81659
+ };
81660
+ }
81661
+ var NO_SUPPORT, ANTHROPIC_FULL, OPENAI_FULL, GOOGLE_FULL, XAI_FULL, DEEPSEEK_FULL, OPENROUTER_FULL, GATEWAY_FULL, COMPAT_ONLY, PROVIDER_CONSTRAINTS, CACHE_EPHEMERAL_5M, CACHE_EPHEMERAL_1H, EPHEMERAL_CACHE_5M, EPHEMERAL_CACHE_1H;
81583
81662
  var init_provider_options = __esm(() => {
81584
81663
  init_models();
81585
81664
  init_providers();
@@ -81688,6 +81767,20 @@ var init_provider_options = __esm(() => {
81688
81767
  interleavedThinking: false
81689
81768
  }
81690
81769
  };
81770
+ CACHE_EPHEMERAL_5M = {
81771
+ cacheControl: {
81772
+ type: "ephemeral",
81773
+ ttl: "5m"
81774
+ }
81775
+ };
81776
+ CACHE_EPHEMERAL_1H = {
81777
+ cacheControl: {
81778
+ type: "ephemeral",
81779
+ ttl: "1h"
81780
+ }
81781
+ };
81782
+ EPHEMERAL_CACHE_5M = buildCacheProviderOptions("5m");
81783
+ EPHEMERAL_CACHE_1H = buildCacheProviderOptions("1h");
81691
81784
  });
81692
81785
 
81693
81786
  // src/core/compaction/summarize.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proxysoul/soulforge",
3
- "version": "2.15.7",
3
+ "version": "2.16.1",
4
4
  "description": "Graph-powered code intelligence — multi-agent coding with codebase-aware AI",
5
5
  "repository": {
6
6
  "type": "git",