@wrongstack/tools 0.73.1 → 0.77.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.
@@ -1,5 +1,5 @@
1
- import { I as IndexResult, d as Symbol, F as FileMeta, e as SymbolKind, f as SymbolLang, c as SearchResult, b as IndexStats, R as Ref } from '../background-indexer-sbsseCCC.js';
2
- export { a as FileSymbols, S as SCHEMA_VERSION, g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as isIndexableFile, r as runStartupIndex } from '../background-indexer-sbsseCCC.js';
1
+ import { I as IndexResult, d as Symbol, F as FileMeta, e as SymbolKind, f as SymbolLang, c as SearchResult, b as IndexStats, R as Ref } from '../background-indexer-C70RD7LU.js';
2
+ export { a as FileSymbols, S as SCHEMA_VERSION, g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as getIndexState, m as isIndexReady, n as isIndexableFile, o as isIndexing, p as onIndexStateChange, r as runStartupIndex } from '../background-indexer-C70RD7LU.js';
3
3
  import { Context } from '@wrongstack/core';
4
4
 
5
5
  /**
@@ -1670,7 +1670,130 @@ async function loadGitignoreMatcher(projectRoot) {
1670
1670
  return compileGitignore(lines);
1671
1671
  }
1672
1672
 
1673
+ // src/codebase-index/background-indexer.ts
1674
+ var _ready = false;
1675
+ var _indexing = false;
1676
+ var _currentFile = 0;
1677
+ var _totalFiles = 0;
1678
+ var _lastError = null;
1679
+ function isIndexReady() {
1680
+ return _ready;
1681
+ }
1682
+ function setIndexReady() {
1683
+ _ready = true;
1684
+ }
1685
+ function isIndexing() {
1686
+ return _indexing;
1687
+ }
1688
+ function getIndexState() {
1689
+ return {
1690
+ ready: _ready,
1691
+ indexing: _indexing,
1692
+ currentFile: _currentFile,
1693
+ totalFiles: _totalFiles,
1694
+ lastError: _lastError
1695
+ };
1696
+ }
1697
+ var _listeners = [];
1698
+ function onIndexStateChange(listener) {
1699
+ _listeners.push(listener);
1700
+ return () => {
1701
+ _listeners = _listeners.filter((l) => l !== listener);
1702
+ };
1703
+ }
1704
+ function emitState() {
1705
+ const state = getIndexState();
1706
+ for (const l of _listeners) l(state);
1707
+ }
1708
+ function _setIndexProgress(current, total) {
1709
+ _currentFile = current;
1710
+ _totalFiles = total;
1711
+ emitState();
1712
+ }
1713
+ function stubCtx(projectRoot) {
1714
+ return {
1715
+ projectRoot,
1716
+ cwd: projectRoot,
1717
+ messages: [],
1718
+ todos: [],
1719
+ readFiles: /* @__PURE__ */ new Set(),
1720
+ fileMtimes: /* @__PURE__ */ new Map()
1721
+ };
1722
+ }
1723
+ var chain = Promise.resolve();
1724
+ function withMutex(job) {
1725
+ const run = chain.then(job, job);
1726
+ chain = run.then(
1727
+ () => void 0,
1728
+ () => void 0
1729
+ );
1730
+ return run;
1731
+ }
1732
+ var DEFAULT_DEBOUNCE_MS = 400;
1733
+ var debounceTimers = /* @__PURE__ */ new Map();
1734
+ function debounceKey(indexDir, file) {
1735
+ return `${indexDir ?? ""}|${file}`;
1736
+ }
1737
+ function isIndexableFile(filePath) {
1738
+ return detectLang(filePath) !== null;
1739
+ }
1740
+ async function runStartupIndex(opts) {
1741
+ _indexing = true;
1742
+ _currentFile = 0;
1743
+ _totalFiles = 0;
1744
+ _lastError = null;
1745
+ emitState();
1746
+ try {
1747
+ const result = await withMutex(
1748
+ () => runIndexer(stubCtx(opts.projectRoot), {
1749
+ projectRoot: opts.projectRoot,
1750
+ indexDir: opts.indexDir,
1751
+ force: opts.force
1752
+ })
1753
+ );
1754
+ _ready = true;
1755
+ return result;
1756
+ } catch (err) {
1757
+ _lastError = err instanceof Error ? err.message : String(err);
1758
+ _ready = true;
1759
+ throw err;
1760
+ } finally {
1761
+ _indexing = false;
1762
+ emitState();
1763
+ }
1764
+ }
1765
+ function enqueueReindex(opts) {
1766
+ const files = opts.files.filter(isIndexableFile);
1767
+ if (files.length === 0) return;
1768
+ const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
1769
+ for (const file of files) {
1770
+ const key = debounceKey(opts.indexDir, file);
1771
+ const existing = debounceTimers.get(key);
1772
+ if (existing) clearTimeout(existing);
1773
+ const timer = setTimeout(() => {
1774
+ debounceTimers.delete(key);
1775
+ void withMutex(
1776
+ () => runIndexer(stubCtx(opts.projectRoot), {
1777
+ projectRoot: opts.projectRoot,
1778
+ files: [file],
1779
+ indexDir: opts.indexDir
1780
+ })
1781
+ ).catch((err) => opts.onError?.(err));
1782
+ }, ms);
1783
+ timer.unref?.();
1784
+ debounceTimers.set(key, timer);
1785
+ }
1786
+ }
1787
+ function cancelPendingReindexes() {
1788
+ for (const t of debounceTimers.values()) clearTimeout(t);
1789
+ debounceTimers.clear();
1790
+ }
1791
+
1673
1792
  // src/codebase-index/indexer.ts
1793
+ var YIELD_EVERY_N = 50;
1794
+ function yieldEventLoop() {
1795
+ return new Promise((resolve2) => setImmediate(resolve2));
1796
+ }
1674
1797
  var DEFAULT_IGNORE = [
1675
1798
  "node_modules",
1676
1799
  ".git",
@@ -1774,7 +1897,12 @@ async function runIndexer(_ctx, opts) {
1774
1897
  if (!force) {
1775
1898
  for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
1776
1899
  }
1777
- for (const file of files) {
1900
+ for (let fi = 0; fi < files.length; fi++) {
1901
+ const file = files[fi];
1902
+ _setIndexProgress(fi + 1, files.length);
1903
+ if (fi > 0 && fi % YIELD_EVERY_N === 0) {
1904
+ await yieldEventLoop();
1905
+ }
1778
1906
  let stat2;
1779
1907
  try {
1780
1908
  stat2 = await fs3.stat(file);
@@ -1888,12 +2016,23 @@ var codebaseIndexTool = {
1888
2016
  }
1889
2017
  },
1890
2018
  async execute(input, ctx) {
2019
+ if (isIndexing()) {
2020
+ return {
2021
+ filesIndexed: 0,
2022
+ symbolsIndexed: 0,
2023
+ langStats: {},
2024
+ durationMs: 0,
2025
+ errors: [],
2026
+ note: "A full index is already in progress. Retry codebase-index after it completes (check codebase-stats)."
2027
+ };
2028
+ }
1891
2029
  const result = await runIndexer(ctx, {
1892
2030
  projectRoot: ctx.projectRoot,
1893
2031
  force: input.force ?? false,
1894
2032
  langs: input.langs,
1895
2033
  indexDir: codebaseIndexDirOverride(ctx)
1896
2034
  });
2035
+ setIndexReady();
1897
2036
  return result;
1898
2037
  }
1899
2038
  };
@@ -2029,6 +2168,31 @@ var codebaseSearchTool = {
2029
2168
  required: ["query"]
2030
2169
  },
2031
2170
  async execute(input, ctx) {
2171
+ const state = getIndexState();
2172
+ if (!state.ready) {
2173
+ return {
2174
+ results: [],
2175
+ total: 0,
2176
+ query: input.query,
2177
+ indexStatus: state.indexing ? `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.` : "Index not yet built. The codebase is being indexed at startup \u2014 search will be available shortly."
2178
+ };
2179
+ }
2180
+ if (state.indexing) {
2181
+ return {
2182
+ results: [],
2183
+ total: 0,
2184
+ query: input.query,
2185
+ indexStatus: `Index refresh in progress (${state.currentFile}/${state.totalFiles} files). Results may be incomplete.`
2186
+ };
2187
+ }
2188
+ if (state.lastError) {
2189
+ return {
2190
+ results: [],
2191
+ total: 0,
2192
+ query: input.query,
2193
+ indexStatus: `Index build failed: ${state.lastError}. Try /codebase-reindex.`
2194
+ };
2195
+ }
2032
2196
  const store = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
2033
2197
  try {
2034
2198
  const limit = Math.min(input.limit ?? 20, 100);
@@ -2086,6 +2250,32 @@ var codebaseStatsTool = {
2086
2250
  additionalProperties: false
2087
2251
  },
2088
2252
  async execute(_input, ctx) {
2253
+ const idxState = getIndexState();
2254
+ if (!idxState.ready) {
2255
+ return {
2256
+ totalSymbols: 0,
2257
+ totalFiles: 0,
2258
+ byLang: {},
2259
+ byKind: {},
2260
+ lastIndexed: null,
2261
+ sizeBytes: 0,
2262
+ indexPath: "",
2263
+ version: SCHEMA_VERSION,
2264
+ indexStatus: idxState.indexing ? `Indexing in progress (${idxState.currentFile}/${idxState.totalFiles} files).` : "Index not yet built."
2265
+ };
2266
+ }
2267
+ if (idxState.indexing) {
2268
+ const store2 = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
2269
+ try {
2270
+ const stats = store2.getStats();
2271
+ return {
2272
+ ...stats,
2273
+ indexStatus: `Index refresh in progress (${idxState.currentFile}/${idxState.totalFiles} files). Stats may be incomplete.`
2274
+ };
2275
+ } finally {
2276
+ store2.close();
2277
+ }
2278
+ }
2089
2279
  const store = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
2090
2280
  try {
2091
2281
  const stats = store.getStats();
@@ -2105,70 +2295,6 @@ var codebaseStatsTool = {
2105
2295
  }
2106
2296
  };
2107
2297
 
2108
- // src/codebase-index/background-indexer.ts
2109
- function stubCtx(projectRoot) {
2110
- return {
2111
- projectRoot,
2112
- cwd: projectRoot,
2113
- messages: [],
2114
- todos: [],
2115
- readFiles: /* @__PURE__ */ new Set(),
2116
- fileMtimes: /* @__PURE__ */ new Map()
2117
- };
2118
- }
2119
- var chain = Promise.resolve();
2120
- function withMutex(job) {
2121
- const run = chain.then(job, job);
2122
- chain = run.then(
2123
- () => void 0,
2124
- () => void 0
2125
- );
2126
- return run;
2127
- }
2128
- var DEFAULT_DEBOUNCE_MS = 400;
2129
- var debounceTimers = /* @__PURE__ */ new Map();
2130
- function debounceKey(indexDir, file) {
2131
- return `${indexDir ?? ""}|${file}`;
2132
- }
2133
- function isIndexableFile(filePath) {
2134
- return detectLang(filePath) !== null;
2135
- }
2136
- function runStartupIndex(opts) {
2137
- return withMutex(
2138
- () => runIndexer(stubCtx(opts.projectRoot), {
2139
- projectRoot: opts.projectRoot,
2140
- indexDir: opts.indexDir,
2141
- force: opts.force
2142
- })
2143
- );
2144
- }
2145
- function enqueueReindex(opts) {
2146
- const files = opts.files.filter(isIndexableFile);
2147
- if (files.length === 0) return;
2148
- const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
2149
- for (const file of files) {
2150
- const key = debounceKey(opts.indexDir, file);
2151
- const existing = debounceTimers.get(key);
2152
- if (existing) clearTimeout(existing);
2153
- const timer = setTimeout(() => {
2154
- debounceTimers.delete(key);
2155
- void withMutex(
2156
- () => runIndexer(stubCtx(opts.projectRoot), {
2157
- projectRoot: opts.projectRoot,
2158
- files: [file],
2159
- indexDir: opts.indexDir
2160
- })
2161
- ).catch((err) => opts.onError?.(err));
2162
- }, ms);
2163
- timer.unref?.();
2164
- debounceTimers.set(key, timer);
2165
- }
2166
- }
2167
- function cancelPendingReindexes() {
2168
- for (const t of debounceTimers.values()) clearTimeout(t);
2169
- debounceTimers.clear();
2170
- }
2171
-
2172
- export { IndexStore, SCHEMA_VERSION, buildBm25Index, buildIndexableText, cancelPendingReindexes, codebaseIndexDirOverride, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, enqueueReindex, internalKindToLspKind, isIndexableFile, lspKindToInternalKind, resolveIndexDir, runIndexer, runStartupIndex, tokenise };
2298
+ export { IndexStore, SCHEMA_VERSION, buildBm25Index, buildIndexableText, cancelPendingReindexes, codebaseIndexDirOverride, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, enqueueReindex, getIndexState, internalKindToLspKind, isIndexReady, isIndexableFile, isIndexing, lspKindToInternalKind, onIndexStateChange, resolveIndexDir, runIndexer, runStartupIndex, tokenise };
2173
2299
  //# sourceMappingURL=index.js.map
2174
2300
  //# sourceMappingURL=index.js.map