@pratik7368patil/anchor-core 0.1.24 → 0.1.25

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/dist/index.js CHANGED
@@ -1535,6 +1535,10 @@ function calculateCoverage(input) {
1535
1535
  }
1536
1536
 
1537
1537
  // src/db/database.ts
1538
+ var CODE_WRITE_PROGRESS_INTERVAL = 500;
1539
+ function shouldEmitCodeWriteProgress(current, total) {
1540
+ return current === 0 || current === 1 || current === total || current % CODE_WRITE_PROGRESS_INTERVAL === 0;
1541
+ }
1538
1542
  function defaultDatabasePath(cwd) {
1539
1543
  return path4.join(cwd, ".anchor", "index.sqlite");
1540
1544
  }
@@ -1902,13 +1906,22 @@ function upsertPullRequest(db, pr, wisdomUnits, regressionEvents = []) {
1902
1906
  regressions: regressionEvents.length
1903
1907
  };
1904
1908
  }
1905
- function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, architecture = { components: [], patterns: [], imports: [] }) {
1909
+ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, architecture = { components: [], patterns: [], imports: [] }, options = {}) {
1906
1910
  initializeSchema(db);
1907
1911
  const repoId = ensureRepository(db, repo);
1908
1912
  const now = (/* @__PURE__ */ new Date()).toISOString();
1913
+ options.onProgress?.({ stage: "writing_code_index", repo, phase: "Inferring test awareness" });
1909
1914
  const testAwareness = inferTestAwareness(repo, codeFiles, codeChunks);
1915
+ options.onProgress?.({ stage: "writing_code_index", repo, phase: "Writing code index" });
1910
1916
  const transaction = db.transaction(() => {
1911
1917
  const existingChunks = db.prepare("SELECT id FROM code_chunks WHERE repo_id = ?").all(repoId);
1918
+ const existingPatternCount = db.prepare("SELECT COUNT(*) AS count FROM architecture_patterns WHERE repo_id = ?").get(repoId).count;
1919
+ options.onProgress?.({
1920
+ stage: "deleting_existing_code_index",
1921
+ repo,
1922
+ chunks: existingChunks.length,
1923
+ patterns: existingPatternCount
1924
+ });
1912
1925
  const deleteFts = db.prepare("DELETE FROM code_chunks_fts WHERE chunkId = ?");
1913
1926
  for (const row of existingChunks) deleteFts.run(row.id);
1914
1927
  db.prepare("DELETE FROM code_chunks WHERE repo_id = ?").run(repoId);
@@ -1921,7 +1934,13 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
1921
1934
  (repo_id, path, language, size_bytes, content_hash, updated_at)
1922
1935
  VALUES (?, ?, ?, ?, ?, ?)`
1923
1936
  );
1924
- for (const file of codeFiles) {
1937
+ options.onProgress?.({
1938
+ stage: "writing_code_files",
1939
+ repo,
1940
+ current: 0,
1941
+ total: codeFiles.length
1942
+ });
1943
+ for (const [index, file] of codeFiles.entries()) {
1925
1944
  insertFile.run(
1926
1945
  repoId,
1927
1946
  file.path,
@@ -1930,6 +1949,16 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
1930
1949
  file.contentHash,
1931
1950
  file.updatedAt
1932
1951
  );
1952
+ const current = index + 1;
1953
+ if (shouldEmitCodeWriteProgress(current, codeFiles.length)) {
1954
+ options.onProgress?.({
1955
+ stage: "writing_code_files",
1956
+ repo,
1957
+ current,
1958
+ total: codeFiles.length,
1959
+ filePath: file.path
1960
+ });
1961
+ }
1933
1962
  }
1934
1963
  const fileRows = db.prepare("SELECT id, path FROM code_files WHERE repo_id = ?").all(repoId);
1935
1964
  const fileIds = new Map(fileRows.map((row) => [row.path, row.id]));
@@ -1944,7 +1973,15 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
1944
1973
  (chunkId, sanitizedText, filePath, symbols, language)
1945
1974
  VALUES (?, ?, ?, ?, ?)`
1946
1975
  );
1947
- for (const chunk of codeChunks) {
1976
+ options.onProgress?.({
1977
+ stage: "writing_code_chunks",
1978
+ repo,
1979
+ current: 0,
1980
+ total: codeChunks.length,
1981
+ chunks: 0
1982
+ });
1983
+ let writtenChunks = 0;
1984
+ for (const [index, chunk] of codeChunks.entries()) {
1948
1985
  const fileId = fileIds.get(chunk.filePath);
1949
1986
  if (!fileId) continue;
1950
1987
  insertChunk.run(
@@ -1968,10 +2005,23 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
1968
2005
  chunk.symbols.join(" "),
1969
2006
  chunk.language ?? ""
1970
2007
  );
2008
+ writtenChunks += 1;
2009
+ const current = index + 1;
2010
+ if (shouldEmitCodeWriteProgress(current, codeChunks.length)) {
2011
+ options.onProgress?.({
2012
+ stage: "writing_code_chunks",
2013
+ repo,
2014
+ current,
2015
+ total: codeChunks.length,
2016
+ filePath: chunk.filePath,
2017
+ chunks: writtenChunks
2018
+ });
2019
+ }
1971
2020
  }
1972
- insertTestAwareness(db, repoId, testAwareness.testFiles, testAwareness.testLinks);
1973
- insertArchitectureData(db, repoId, architecture);
1974
- insertArchitectureMapEdges(db, repoId, repo, architecture, testAwareness.testLinks);
2021
+ insertTestAwareness(db, repoId, repo, testAwareness.testFiles, testAwareness.testLinks, options);
2022
+ insertArchitectureData(db, repoId, repo, architecture, options);
2023
+ insertArchitectureMapEdges(db, repoId, repo, architecture, testAwareness.testLinks, options);
2024
+ options.onProgress?.({ stage: "writing_code_index", repo, phase: "Updating index state" });
1975
2025
  db.prepare(
1976
2026
  `INSERT INTO code_index_state (repo, last_indexed_at, indexed_files, code_chunks, skipped_files)
1977
2027
  VALUES (?, ?, ?, ?, ?)
@@ -2019,13 +2069,20 @@ function deleteExistingArchitectureData(db, repoId) {
2019
2069
  db.prepare("DELETE FROM code_imports WHERE repo_id = ?").run(repoId);
2020
2070
  db.prepare("DELETE FROM architecture_map_edges WHERE repo_id = ?").run(repoId);
2021
2071
  }
2022
- function insertArchitectureData(db, repoId, architecture) {
2072
+ function insertArchitectureData(db, repoId, repo, architecture, options = {}) {
2023
2073
  const insertImport = db.prepare(
2024
2074
  `INSERT INTO code_imports
2025
2075
  (repo_id, source_path, specifier, imported_path, imported_symbols_json, kind)
2026
2076
  VALUES (?, ?, ?, ?, ?, ?)`
2027
2077
  );
2028
- for (const item of architecture.imports) {
2078
+ options.onProgress?.({
2079
+ stage: "writing_architecture_data",
2080
+ repo,
2081
+ current: 0,
2082
+ total: architecture.imports.length,
2083
+ kind: "imports"
2084
+ });
2085
+ for (const [index, item] of architecture.imports.entries()) {
2029
2086
  insertImport.run(
2030
2087
  repoId,
2031
2088
  item.sourcePath,
@@ -2034,6 +2091,16 @@ function insertArchitectureData(db, repoId, architecture) {
2034
2091
  JSON.stringify(item.importedSymbols),
2035
2092
  item.kind
2036
2093
  );
2094
+ const current = index + 1;
2095
+ if (shouldEmitCodeWriteProgress(current, architecture.imports.length)) {
2096
+ options.onProgress?.({
2097
+ stage: "writing_architecture_data",
2098
+ repo,
2099
+ current,
2100
+ total: architecture.imports.length,
2101
+ kind: "imports"
2102
+ });
2103
+ }
2037
2104
  }
2038
2105
  const insertComponent = db.prepare(
2039
2106
  `INSERT INTO architecture_components
@@ -2041,7 +2108,14 @@ function insertArchitectureData(db, repoId, architecture) {
2041
2108
  confidence, updated_at)
2042
2109
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2043
2110
  );
2044
- for (const component of architecture.components) {
2111
+ options.onProgress?.({
2112
+ stage: "writing_architecture_data",
2113
+ repo,
2114
+ current: 0,
2115
+ total: architecture.components.length,
2116
+ kind: "components"
2117
+ });
2118
+ for (const [index, component] of architecture.components.entries()) {
2045
2119
  insertComponent.run(
2046
2120
  repoId,
2047
2121
  component.path,
@@ -2054,6 +2128,16 @@ function insertArchitectureData(db, repoId, architecture) {
2054
2128
  component.confidence,
2055
2129
  component.updatedAt
2056
2130
  );
2131
+ const current = index + 1;
2132
+ if (shouldEmitCodeWriteProgress(current, architecture.components.length)) {
2133
+ options.onProgress?.({
2134
+ stage: "writing_architecture_data",
2135
+ repo,
2136
+ current,
2137
+ total: architecture.components.length,
2138
+ kind: "components"
2139
+ });
2140
+ }
2057
2141
  }
2058
2142
  const insertPattern = db.prepare(
2059
2143
  `INSERT INTO architecture_patterns
@@ -2065,7 +2149,14 @@ function insertArchitectureData(db, repoId, architecture) {
2065
2149
  `INSERT INTO architecture_patterns_fts (patternId, summary, area, sourceFiles, symbols)
2066
2150
  VALUES (?, ?, ?, ?, ?)`
2067
2151
  );
2068
- for (const pattern of architecture.patterns) {
2152
+ options.onProgress?.({
2153
+ stage: "writing_architecture_data",
2154
+ repo,
2155
+ current: 0,
2156
+ total: architecture.patterns.length,
2157
+ kind: "patterns"
2158
+ });
2159
+ for (const [index, pattern] of architecture.patterns.entries()) {
2069
2160
  insertPattern.run(
2070
2161
  pattern.id,
2071
2162
  repoId,
@@ -2086,9 +2177,19 @@ function insertArchitectureData(db, repoId, architecture) {
2086
2177
  pattern.sourceFiles.join(" "),
2087
2178
  pattern.symbols.join(" ")
2088
2179
  );
2180
+ const current = index + 1;
2181
+ if (shouldEmitCodeWriteProgress(current, architecture.patterns.length)) {
2182
+ options.onProgress?.({
2183
+ stage: "writing_architecture_data",
2184
+ repo,
2185
+ current,
2186
+ total: architecture.patterns.length,
2187
+ kind: "patterns"
2188
+ });
2189
+ }
2089
2190
  }
2090
2191
  }
2091
- function insertArchitectureMapEdges(db, repoId, repo, architecture, testLinks) {
2192
+ function insertArchitectureMapEdges(db, repoId, repo, architecture, testLinks, options = {}) {
2092
2193
  const now = (/* @__PURE__ */ new Date()).toISOString();
2093
2194
  const insert = db.prepare(
2094
2195
  `INSERT INTO architecture_map_edges
@@ -2103,11 +2204,40 @@ function insertArchitectureMapEdges(db, repoId, repo, architecture, testLinks) {
2103
2204
  seen.add(id);
2104
2205
  insert.run(id, repoId, repo, sourcePath, targetPath, relationship, weight, now);
2105
2206
  };
2207
+ const total = architecture.imports.length + testLinks.length;
2208
+ let current = 0;
2209
+ options.onProgress?.({
2210
+ stage: "writing_architecture_map_edges",
2211
+ repo,
2212
+ current,
2213
+ total,
2214
+ edges: 0
2215
+ });
2106
2216
  for (const item of architecture.imports) {
2107
2217
  if (item.importedPath) addEdge(item.sourcePath, item.importedPath, "imports", 0.9);
2218
+ current += 1;
2219
+ if (shouldEmitCodeWriteProgress(current, total)) {
2220
+ options.onProgress?.({
2221
+ stage: "writing_architecture_map_edges",
2222
+ repo,
2223
+ current,
2224
+ total,
2225
+ edges: seen.size
2226
+ });
2227
+ }
2108
2228
  }
2109
2229
  for (const link of testLinks) {
2110
2230
  addEdge(link.sourcePath, link.testPath, "tested_by", link.strength);
2231
+ current += 1;
2232
+ if (shouldEmitCodeWriteProgress(current, total)) {
2233
+ options.onProgress?.({
2234
+ stage: "writing_architecture_map_edges",
2235
+ repo,
2236
+ current,
2237
+ total,
2238
+ edges: seen.size
2239
+ });
2240
+ }
2111
2241
  }
2112
2242
  }
2113
2243
  function insertPrCochangeTestLinks(db, repoId, filePaths) {
@@ -2123,13 +2253,20 @@ function insertPrCochangeTestLinks(db, repoId, filePaths) {
2123
2253
  for (const testPath of testPaths) insert.run(repoId, sourcePath, testPath);
2124
2254
  }
2125
2255
  }
2126
- function insertTestAwareness(db, repoId, testFiles, testLinks) {
2256
+ function insertTestAwareness(db, repoId, repo, testFiles, testLinks, options = {}) {
2127
2257
  const insertTestFile = db.prepare(
2128
2258
  `INSERT INTO test_files
2129
2259
  (repo_id, path, language, size_bytes, content_hash, updated_at)
2130
2260
  VALUES (?, ?, ?, ?, ?, ?)`
2131
2261
  );
2132
- for (const file of testFiles) {
2262
+ options.onProgress?.({
2263
+ stage: "writing_test_awareness",
2264
+ repo,
2265
+ current: 0,
2266
+ total: testFiles.length,
2267
+ kind: "test_files"
2268
+ });
2269
+ for (const [index, file] of testFiles.entries()) {
2133
2270
  insertTestFile.run(
2134
2271
  repoId,
2135
2272
  file.path,
@@ -2138,13 +2275,40 @@ function insertTestAwareness(db, repoId, testFiles, testLinks) {
2138
2275
  file.contentHash,
2139
2276
  file.updatedAt
2140
2277
  );
2278
+ const current = index + 1;
2279
+ if (shouldEmitCodeWriteProgress(current, testFiles.length)) {
2280
+ options.onProgress?.({
2281
+ stage: "writing_test_awareness",
2282
+ repo,
2283
+ current,
2284
+ total: testFiles.length,
2285
+ kind: "test_files"
2286
+ });
2287
+ }
2141
2288
  }
2142
2289
  const insertTestLink = db.prepare(
2143
2290
  `INSERT INTO test_links (repo_id, source_path, test_path, reason, strength)
2144
2291
  VALUES (?, ?, ?, ?, ?)`
2145
2292
  );
2146
- for (const link of testLinks) {
2293
+ options.onProgress?.({
2294
+ stage: "writing_test_awareness",
2295
+ repo,
2296
+ current: 0,
2297
+ total: testLinks.length,
2298
+ kind: "test_links"
2299
+ });
2300
+ for (const [index, link] of testLinks.entries()) {
2147
2301
  insertTestLink.run(repoId, link.sourcePath, link.testPath, link.reason, link.strength);
2302
+ const current = index + 1;
2303
+ if (shouldEmitCodeWriteProgress(current, testLinks.length)) {
2304
+ options.onProgress?.({
2305
+ stage: "writing_test_awareness",
2306
+ repo,
2307
+ current,
2308
+ total: testLinks.length,
2309
+ kind: "test_links"
2310
+ });
2311
+ }
2148
2312
  }
2149
2313
  }
2150
2314
  function recordIndexRun(db, run) {
@@ -2471,6 +2635,10 @@ function chunkCodeFile(file, options = {}) {
2471
2635
  import crypto2 from "crypto";
2472
2636
  import path6 from "path";
2473
2637
  var KNOWN_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json"];
2638
+ var ARCHITECTURE_PROGRESS_INTERVAL = 250;
2639
+ function shouldEmitProgress(current, total) {
2640
+ return current === 0 || current === 1 || current === total || current % ARCHITECTURE_PROGRESS_INTERVAL === 0;
2641
+ }
2474
2642
  function classifyArchitectureArea(filePath, language, content = "") {
2475
2643
  const normalized = filePath.replace(/\\/g, "/").toLowerCase();
2476
2644
  const basename = path6.basename(normalized);
@@ -2618,7 +2786,7 @@ function createPattern(input) {
2618
2786
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2619
2787
  };
2620
2788
  }
2621
- function buildArchitectureIndex(repo, files, chunks) {
2789
+ function buildArchitectureIndex(repo, files, chunks, options = {}) {
2622
2790
  const allPaths = files.map((file) => file.path);
2623
2791
  const codePaths = new Set(allPaths);
2624
2792
  const symbolsByPath = /* @__PURE__ */ new Map();
@@ -2626,20 +2794,47 @@ function buildArchitectureIndex(repo, files, chunks) {
2626
2794
  const existing = symbolsByPath.get(chunk.filePath) ?? [];
2627
2795
  symbolsByPath.set(chunk.filePath, uniqueStrings([...existing, ...chunk.symbols]).slice(0, 40));
2628
2796
  }
2629
- const imports = files.flatMap(
2630
- (file) => extractCodeImports(file.path, file.content, codePaths, repo)
2631
- );
2797
+ const imports = [];
2798
+ options.onProgress?.({
2799
+ stage: "building_architecture_imports",
2800
+ repo,
2801
+ current: 0,
2802
+ total: files.length,
2803
+ imports: 0
2804
+ });
2805
+ for (const [index, file] of files.entries()) {
2806
+ imports.push(...extractCodeImports(file.path, file.content, codePaths, repo));
2807
+ const current = index + 1;
2808
+ if (shouldEmitProgress(current, files.length)) {
2809
+ options.onProgress?.({
2810
+ stage: "building_architecture_imports",
2811
+ repo,
2812
+ current,
2813
+ total: files.length,
2814
+ filePath: file.path,
2815
+ imports: imports.length
2816
+ });
2817
+ }
2818
+ }
2632
2819
  const importsByPath = /* @__PURE__ */ new Map();
2633
2820
  for (const item of imports) {
2634
2821
  const existing = importsByPath.get(item.sourcePath) ?? [];
2635
2822
  existing.push(item);
2636
2823
  importsByPath.set(item.sourcePath, existing);
2637
2824
  }
2638
- const components = files.map((file) => {
2825
+ const components = [];
2826
+ options.onProgress?.({
2827
+ stage: "building_architecture_components",
2828
+ repo,
2829
+ current: 0,
2830
+ total: files.length,
2831
+ components: 0
2832
+ });
2833
+ for (const [index, file] of files.entries()) {
2639
2834
  const area = classifyArchitectureArea(file.path, file.language, file.content);
2640
2835
  const fileImports = importsByPath.get(file.path) ?? [];
2641
2836
  const symbols = symbolsByPath.get(file.path) ?? [];
2642
- return {
2837
+ components.push({
2643
2838
  repo,
2644
2839
  path: file.path,
2645
2840
  area,
@@ -2652,16 +2847,49 @@ function buildArchitectureIndex(repo, files, chunks) {
2652
2847
  relatedTests: relatedTestsFor(file.path, allPaths),
2653
2848
  confidence: area === "unknown" ? 0.45 : 0.82,
2654
2849
  updatedAt: file.updatedAt
2655
- };
2656
- });
2850
+ });
2851
+ const current = index + 1;
2852
+ if (shouldEmitProgress(current, files.length)) {
2853
+ options.onProgress?.({
2854
+ stage: "building_architecture_components",
2855
+ repo,
2856
+ current,
2857
+ total: files.length,
2858
+ filePath: file.path,
2859
+ components: components.length
2860
+ });
2861
+ }
2862
+ }
2657
2863
  const componentByPath = new Map(components.map((component) => [component.path, component]));
2658
- const patterns = [];
2659
2864
  const componentsByArea = /* @__PURE__ */ new Map();
2660
2865
  for (const component of components) {
2661
2866
  const existing = componentsByArea.get(component.area) ?? [];
2662
2867
  existing.push(component);
2663
2868
  componentsByArea.set(component.area, existing);
2664
2869
  }
2870
+ const importDirectionCounts = /* @__PURE__ */ new Map();
2871
+ for (const item of imports) {
2872
+ if (!item.importedPath) continue;
2873
+ const source = componentByPath.get(item.sourcePath);
2874
+ const target = componentByPath.get(item.importedPath);
2875
+ if (!source || !target || source.area === target.area) continue;
2876
+ const key = `${source.area}->${target.area}`;
2877
+ const existing = importDirectionCounts.get(key) ?? { count: 0, files: [], symbols: [] };
2878
+ existing.count += 1;
2879
+ existing.files.push(source.path, target.path);
2880
+ existing.symbols.push(...item.importedSymbols);
2881
+ importDirectionCounts.set(key, existing);
2882
+ }
2883
+ const patterns = [];
2884
+ const patternTotal = componentsByArea.size + importDirectionCounts.size;
2885
+ let patternProgress = 0;
2886
+ options.onProgress?.({
2887
+ stage: "building_architecture_patterns",
2888
+ repo,
2889
+ current: 0,
2890
+ total: patternTotal,
2891
+ patterns: 0
2892
+ });
2665
2893
  for (const [area, areaComponents] of componentsByArea.entries()) {
2666
2894
  const filesForArea = areaComponents.map((component) => component.path);
2667
2895
  const directories = topDirectories(filesForArea);
@@ -2677,19 +2905,17 @@ function buildArchitectureIndex(repo, files, chunks) {
2677
2905
  confidence: 0.55 + Math.min(0.3, filesForArea.length * 0.04)
2678
2906
  })
2679
2907
  );
2680
- }
2681
- const importDirectionCounts = /* @__PURE__ */ new Map();
2682
- for (const item of imports) {
2683
- if (!item.importedPath) continue;
2684
- const source = componentByPath.get(item.sourcePath);
2685
- const target = componentByPath.get(item.importedPath);
2686
- if (!source || !target || source.area === target.area) continue;
2687
- const key = `${source.area}->${target.area}`;
2688
- const existing = importDirectionCounts.get(key) ?? { count: 0, files: [], symbols: [] };
2689
- existing.count += 1;
2690
- existing.files.push(source.path, target.path);
2691
- existing.symbols.push(...item.importedSymbols);
2692
- importDirectionCounts.set(key, existing);
2908
+ patternProgress += 1;
2909
+ if (shouldEmitProgress(patternProgress, patternTotal)) {
2910
+ options.onProgress?.({
2911
+ stage: "building_architecture_patterns",
2912
+ repo,
2913
+ current: patternProgress,
2914
+ total: patternTotal,
2915
+ area,
2916
+ patterns: patterns.length
2917
+ });
2918
+ }
2693
2919
  }
2694
2920
  for (const [key, value] of importDirectionCounts.entries()) {
2695
2921
  const [sourceArea, targetArea] = key.split("->");
@@ -2704,6 +2930,17 @@ function buildArchitectureIndex(repo, files, chunks) {
2704
2930
  confidence: 0.62 + Math.min(0.25, value.count * 0.05)
2705
2931
  })
2706
2932
  );
2933
+ patternProgress += 1;
2934
+ if (shouldEmitProgress(patternProgress, patternTotal)) {
2935
+ options.onProgress?.({
2936
+ stage: "building_architecture_patterns",
2937
+ repo,
2938
+ current: patternProgress,
2939
+ total: patternTotal,
2940
+ area: sourceArea,
2941
+ patterns: patterns.length
2942
+ });
2943
+ }
2707
2944
  }
2708
2945
  const testedComponents = components.filter((component) => component.relatedTests.length > 0);
2709
2946
  if (testedComponents.length > 0) {
@@ -3033,8 +3270,24 @@ function detectTestCommands(db, cwd, files = []) {
3033
3270
  return true;
3034
3271
  });
3035
3272
  }
3036
- function refreshTestCommands(db, cwd, repo, files = []) {
3273
+ function refreshTestCommands(db, cwd, repo, files = [], options = {}) {
3274
+ options.onProgress?.({
3275
+ stage: "refreshing_test_commands",
3276
+ repo,
3277
+ phase: "detecting",
3278
+ current: 0,
3279
+ total: files.length,
3280
+ commands: 0
3281
+ });
3037
3282
  const commands = detectTestCommands(db, cwd, files);
3283
+ options.onProgress?.({
3284
+ stage: "refreshing_test_commands",
3285
+ repo,
3286
+ phase: "writing",
3287
+ current: 0,
3288
+ total: commands.length,
3289
+ commands: commands.length
3290
+ });
3038
3291
  const now = (/* @__PURE__ */ new Date()).toISOString();
3039
3292
  const transaction = db.transaction(() => {
3040
3293
  db.prepare("DELETE FROM test_commands WHERE repo = ?").run(repo);
@@ -3042,7 +3295,7 @@ function refreshTestCommands(db, cwd, repo, files = []) {
3042
3295
  `INSERT INTO test_commands (id, repo, file_path, command, reason, confidence, created_at)
3043
3296
  VALUES (?, ?, ?, ?, ?, ?, ?)`
3044
3297
  );
3045
- for (const command of commands) {
3298
+ for (const [index, command] of commands.entries()) {
3046
3299
  insert.run(
3047
3300
  commandId(repo, command),
3048
3301
  repo,
@@ -3052,6 +3305,17 @@ function refreshTestCommands(db, cwd, repo, files = []) {
3052
3305
  command.confidence,
3053
3306
  now
3054
3307
  );
3308
+ const current = index + 1;
3309
+ if (current === 1 || current === commands.length || current % 250 === 0) {
3310
+ options.onProgress?.({
3311
+ stage: "refreshing_test_commands",
3312
+ repo,
3313
+ phase: "writing",
3314
+ current,
3315
+ total: commands.length,
3316
+ commands: commands.length
3317
+ });
3318
+ }
3055
3319
  }
3056
3320
  });
3057
3321
  transaction();
@@ -3090,7 +3354,9 @@ function indexCodebase(db, options) {
3090
3354
  chunks: fileChunks.length
3091
3355
  });
3092
3356
  }
3093
- const architecture = buildArchitectureIndex(options.repo, discovery.files, chunks);
3357
+ const architecture = buildArchitectureIndex(options.repo, discovery.files, chunks, {
3358
+ onProgress: options.onProgress
3359
+ });
3094
3360
  options.onProgress?.({
3095
3361
  stage: "indexed_architecture",
3096
3362
  repo: options.repo,
@@ -3105,9 +3371,10 @@ function indexCodebase(db, options) {
3105
3371
  chunks,
3106
3372
  discovery.skippedFiles,
3107
3373
  options.cwd,
3108
- architecture
3374
+ architecture,
3375
+ { onProgress: options.onProgress }
3109
3376
  );
3110
- refreshTestCommands(db, options.cwd, options.repo);
3377
+ refreshTestCommands(db, options.cwd, options.repo, [], { onProgress: options.onProgress });
3111
3378
  options.onProgress?.({
3112
3379
  stage: "completed_code_index",
3113
3380
  repo: options.repo,
@@ -7929,10 +8196,67 @@ function parseHeartbeat(value) {
7929
8196
  repoIndex: typeof candidate.repoIndex === "number" ? candidate.repoIndex : void 0,
7930
8197
  repoTotal: typeof candidate.repoTotal === "number" ? candidate.repoTotal : void 0,
7931
8198
  phase: candidate.phase,
8199
+ timeline: parseTimeline(candidate.timeline),
7932
8200
  startedAt: candidate.startedAt,
7933
8201
  updatedAt: candidate.updatedAt
7934
8202
  };
7935
8203
  }
8204
+ function parseTimelineStatus(value) {
8205
+ if (value === "active" || value === "done" || value === "skipped" || value === "warn" || value === "fail" || value === "wait") {
8206
+ return value;
8207
+ }
8208
+ return void 0;
8209
+ }
8210
+ function parseTimelineNumber(value) {
8211
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8212
+ }
8213
+ function parseTimelineStep(value) {
8214
+ if (!value || typeof value !== "object") return void 0;
8215
+ const candidate = value;
8216
+ const status = parseTimelineStatus(candidate.status);
8217
+ if (typeof candidate.id !== "string" || typeof candidate.label !== "string" || !status || typeof candidate.startedAt !== "string" || typeof candidate.updatedAt !== "string") {
8218
+ return void 0;
8219
+ }
8220
+ return {
8221
+ id: candidate.id,
8222
+ label: candidate.label,
8223
+ status,
8224
+ startedAt: candidate.startedAt,
8225
+ updatedAt: candidate.updatedAt,
8226
+ completedAt: typeof candidate.completedAt === "string" ? candidate.completedAt : void 0,
8227
+ durationMs: parseTimelineNumber(candidate.durationMs),
8228
+ current: parseTimelineNumber(candidate.current),
8229
+ total: parseTimelineNumber(candidate.total),
8230
+ detail: typeof candidate.detail === "string" ? candidate.detail : void 0
8231
+ };
8232
+ }
8233
+ function parseTimelineRepoSummary(value) {
8234
+ if (!value || typeof value !== "object") return void 0;
8235
+ const candidate = value;
8236
+ const status = parseTimelineStatus(candidate.status);
8237
+ if (typeof candidate.repo !== "string" || !status) return void 0;
8238
+ return {
8239
+ repo: candidate.repo,
8240
+ status,
8241
+ durationMs: parseTimelineNumber(candidate.durationMs) ?? 0,
8242
+ detail: typeof candidate.detail === "string" ? candidate.detail : void 0
8243
+ };
8244
+ }
8245
+ function parseTimeline(value) {
8246
+ if (!value || typeof value !== "object") return void 0;
8247
+ const candidate = value;
8248
+ if (!Array.isArray(candidate.steps) || !Array.isArray(candidate.recentRepos)) return void 0;
8249
+ const steps = candidate.steps.map(parseTimelineStep).filter((step) => step !== void 0);
8250
+ const recentRepos = candidate.recentRepos.map(parseTimelineRepoSummary).filter((repo) => repo !== void 0);
8251
+ return {
8252
+ repo: typeof candidate.repo === "string" ? candidate.repo : void 0,
8253
+ repoIndex: parseTimelineNumber(candidate.repoIndex),
8254
+ repoTotal: parseTimelineNumber(candidate.repoTotal),
8255
+ activeStepId: typeof candidate.activeStepId === "string" ? candidate.activeStepId : void 0,
8256
+ steps,
8257
+ recentRepos
8258
+ };
8259
+ }
7936
8260
  function writeOrgHeartbeat(heartbeat, baseDir) {
7937
8261
  atomicWriteJson2(orgHeartbeatPath(heartbeat.org, baseDir), heartbeat);
7938
8262
  }
@@ -8194,7 +8518,7 @@ function isApiConsumerText(text) {
8194
8518
  function evidenceJson(evidence) {
8195
8519
  return JSON.stringify(evidence);
8196
8520
  }
8197
- function shouldEmitProgress(current, total, interval = 100) {
8521
+ function shouldEmitProgress2(current, total, interval = 100) {
8198
8522
  return current === 1 || current === total || current % interval === 0;
8199
8523
  }
8200
8524
  function resolveOptions(baseDirOrOptions) {
@@ -8300,7 +8624,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
8300
8624
  });
8301
8625
  break;
8302
8626
  }
8303
- if (shouldEmitProgress(index + 1, imports.length)) {
8627
+ if (shouldEmitProgress2(index + 1, imports.length)) {
8304
8628
  options.onProgress?.({
8305
8629
  stage: "building_import_edges",
8306
8630
  org: config.org,
@@ -8341,7 +8665,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
8341
8665
  bucket.push(apiContract);
8342
8666
  contractsByToken.set(sanitizedContract, bucket);
8343
8667
  }
8344
- if (shouldEmitProgress(index + 1, providerChunks.length)) {
8668
+ if (shouldEmitProgress2(index + 1, providerChunks.length)) {
8345
8669
  options.onProgress?.({
8346
8670
  stage: "extracting_api_contracts",
8347
8671
  org: config.org,
@@ -8401,7 +8725,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
8401
8725
  });
8402
8726
  }
8403
8727
  }
8404
- if (shouldEmitProgress(index + 1, consumerChunks.length)) {
8728
+ if (shouldEmitProgress2(index + 1, consumerChunks.length)) {
8405
8729
  options.onProgress?.({
8406
8730
  stage: "matching_api_consumers",
8407
8731
  org: config.org,