trail-code 0.0.0-stage → 0.28.0-semantic.3
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/LICENSE +21 -0
- package/README.md +567 -2
- package/dist/ai/crux.d.ts +88 -0
- package/dist/ai/crux.d.ts.map +1 -0
- package/dist/ai/crux.js +219 -0
- package/dist/ai/crux.js.map +1 -0
- package/dist/ai/failure.d.ts +60 -0
- package/dist/ai/failure.d.ts.map +1 -0
- package/dist/ai/failure.js +86 -0
- package/dist/ai/failure.js.map +1 -0
- package/dist/ai/llm/anthropic.d.ts +37 -0
- package/dist/ai/llm/anthropic.d.ts.map +1 -0
- package/dist/ai/llm/anthropic.js +144 -0
- package/dist/ai/llm/anthropic.js.map +1 -0
- package/dist/ai/llm/factory.d.ts +28 -0
- package/dist/ai/llm/factory.d.ts.map +1 -0
- package/dist/ai/llm/factory.js +36 -0
- package/dist/ai/llm/factory.js.map +1 -0
- package/dist/ai/llm/litellm.d.ts +34 -0
- package/dist/ai/llm/litellm.d.ts.map +1 -0
- package/dist/ai/llm/litellm.js +45 -0
- package/dist/ai/llm/litellm.js.map +1 -0
- package/dist/ai/llm/openai.d.ts +145 -0
- package/dist/ai/llm/openai.d.ts.map +1 -0
- package/dist/ai/llm/openai.js +629 -0
- package/dist/ai/llm/openai.js.map +1 -0
- package/dist/ai/llm/orcarouter.d.ts +34 -0
- package/dist/ai/llm/orcarouter.d.ts.map +1 -0
- package/dist/ai/llm/orcarouter.js +45 -0
- package/dist/ai/llm/orcarouter.js.map +1 -0
- package/dist/ai/llm/recover-tool.d.ts +21 -0
- package/dist/ai/llm/recover-tool.d.ts.map +1 -0
- package/dist/ai/llm/recover-tool.js +109 -0
- package/dist/ai/llm/recover-tool.js.map +1 -0
- package/dist/ai/llm/types.d.ts +122 -0
- package/dist/ai/llm/types.d.ts.map +1 -0
- package/dist/ai/llm/types.js +33 -0
- package/dist/ai/llm/types.js.map +1 -0
- package/dist/ai/providers.d.ts +59 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +54 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/summarize.d.ts +25 -0
- package/dist/ai/summarize.d.ts.map +1 -0
- package/dist/ai/summarize.js +34 -0
- package/dist/ai/summarize.js.map +1 -0
- package/dist/ai/synthesize.d.ts +40 -0
- package/dist/ai/synthesize.d.ts.map +1 -0
- package/dist/ai/synthesize.js +230 -0
- package/dist/ai/synthesize.js.map +1 -0
- package/dist/ask/ask.d.ts +199 -0
- package/dist/ask/ask.d.ts.map +1 -0
- package/dist/ask/ask.js +1291 -0
- package/dist/ask/ask.js.map +1 -0
- package/dist/ask/file-rank.d.ts +72 -0
- package/dist/ask/file-rank.d.ts.map +1 -0
- package/dist/ask/file-rank.js +160 -0
- package/dist/ask/file-rank.js.map +1 -0
- package/dist/ask/file-selection.d.ts +22 -0
- package/dist/ask/file-selection.d.ts.map +1 -0
- package/dist/ask/file-selection.js +66 -0
- package/dist/ask/file-selection.js.map +1 -0
- package/dist/ask/fuse.d.ts +195 -0
- package/dist/ask/fuse.d.ts.map +1 -0
- package/dist/ask/fuse.js +344 -0
- package/dist/ask/fuse.js.map +1 -0
- package/dist/ask/graphrank.d.ts +61 -0
- package/dist/ask/graphrank.d.ts.map +1 -0
- package/dist/ask/graphrank.js +130 -0
- package/dist/ask/graphrank.js.map +1 -0
- package/dist/ask/index-file.d.ts +44 -0
- package/dist/ask/index-file.d.ts.map +1 -0
- package/dist/ask/index-file.js +128 -0
- package/dist/ask/index-file.js.map +1 -0
- package/dist/ask/semantic.d.ts +42 -0
- package/dist/ask/semantic.d.ts.map +1 -0
- package/dist/ask/semantic.js +293 -0
- package/dist/ask/semantic.js.map +1 -0
- package/dist/bin/graft.d.ts +3 -0
- package/dist/bin/graft.d.ts.map +1 -0
- package/dist/bin/graft.js +14 -0
- package/dist/bin/graft.js.map +1 -0
- package/dist/bin/trail.d.ts +3 -0
- package/dist/bin/trail.d.ts.map +1 -0
- package/dist/bin/trail.js +13 -0
- package/dist/bin/trail.js.map +1 -0
- package/dist/blast/blast-cli.d.ts +35 -0
- package/dist/blast/blast-cli.d.ts.map +1 -0
- package/dist/blast/blast-cli.js +155 -0
- package/dist/blast/blast-cli.js.map +1 -0
- package/dist/blast/blast.d.ts +128 -0
- package/dist/blast/blast.d.ts.map +1 -0
- package/dist/blast/blast.js +383 -0
- package/dist/blast/blast.js.map +1 -0
- package/dist/blast/diff.d.ts +53 -0
- package/dist/blast/diff.d.ts.map +1 -0
- package/dist/blast/diff.js +193 -0
- package/dist/blast/diff.js.map +1 -0
- package/dist/blast/evidence.d.ts +51 -0
- package/dist/blast/evidence.d.ts.map +1 -0
- package/dist/blast/evidence.js +162 -0
- package/dist/blast/evidence.js.map +1 -0
- package/dist/blast/modules.d.ts +33 -0
- package/dist/blast/modules.d.ts.map +1 -0
- package/dist/blast/modules.js +83 -0
- package/dist/blast/modules.js.map +1 -0
- package/dist/blast/name.d.ts +74 -0
- package/dist/blast/name.d.ts.map +1 -0
- package/dist/blast/name.js +269 -0
- package/dist/blast/name.js.map +1 -0
- package/dist/blast/owners.d.ts +85 -0
- package/dist/blast/owners.d.ts.map +1 -0
- package/dist/blast/owners.js +272 -0
- package/dist/blast/owners.js.map +1 -0
- package/dist/blast/render.d.ts +46 -0
- package/dist/blast/render.d.ts.map +1 -0
- package/dist/blast/render.js +422 -0
- package/dist/blast/render.js.map +1 -0
- package/dist/blast/viz.d.ts +37 -0
- package/dist/blast/viz.d.ts.map +1 -0
- package/dist/blast/viz.js +180 -0
- package/dist/blast/viz.js.map +1 -0
- package/dist/brand.d.ts +111 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +282 -0
- package/dist/brand.js.map +1 -0
- package/dist/claude/build-progress.d.ts +30 -0
- package/dist/claude/build-progress.d.ts.map +1 -0
- package/dist/claude/build-progress.js +81 -0
- package/dist/claude/build-progress.js.map +1 -0
- package/dist/claude/hermes-skill-template.d.ts +16 -0
- package/dist/claude/hermes-skill-template.d.ts.map +1 -0
- package/dist/claude/hermes-skill-template.js +154 -0
- package/dist/claude/hermes-skill-template.js.map +1 -0
- package/dist/claude/init.d.ts +65 -0
- package/dist/claude/init.d.ts.map +1 -0
- package/dist/claude/init.js +258 -0
- package/dist/claude/init.js.map +1 -0
- package/dist/claude/paths.d.ts +9 -0
- package/dist/claude/paths.d.ts.map +1 -0
- package/dist/claude/paths.js +22 -0
- package/dist/claude/paths.js.map +1 -0
- package/dist/claude/session-metrics.d.ts +73 -0
- package/dist/claude/session-metrics.d.ts.map +1 -0
- package/dist/claude/session-metrics.js +209 -0
- package/dist/claude/session-metrics.js.map +1 -0
- package/dist/claude/settings-merge.d.ts +76 -0
- package/dist/claude/settings-merge.d.ts.map +1 -0
- package/dist/claude/settings-merge.js +283 -0
- package/dist/claude/settings-merge.js.map +1 -0
- package/dist/claude/skill-template.d.ts +13 -0
- package/dist/claude/skill-template.d.ts.map +1 -0
- package/dist/claude/skill-template.js +290 -0
- package/dist/claude/skill-template.js.map +1 -0
- package/dist/claude/state.d.ts +85 -0
- package/dist/claude/state.d.ts.map +1 -0
- package/dist/claude/state.js +88 -0
- package/dist/claude/state.js.map +1 -0
- package/dist/claude/stats.d.ts +5 -0
- package/dist/claude/stats.d.ts.map +1 -0
- package/dist/claude/stats.js +24 -0
- package/dist/claude/stats.js.map +1 -0
- package/dist/cli-epilogue.d.ts +12 -0
- package/dist/cli-epilogue.d.ts.map +1 -0
- package/dist/cli-epilogue.js +72 -0
- package/dist/cli-epilogue.js.map +1 -0
- package/dist/cli-meta.d.ts +82 -0
- package/dist/cli-meta.d.ts.map +1 -0
- package/dist/cli-meta.js +238 -0
- package/dist/cli-meta.js.map +1 -0
- package/dist/cli-picker.d.ts +139 -0
- package/dist/cli-picker.d.ts.map +1 -0
- package/dist/cli-picker.js +439 -0
- package/dist/cli-picker.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1638 -0
- package/dist/cli.js.map +1 -0
- package/dist/context/batches.d.ts +29 -0
- package/dist/context/batches.d.ts.map +1 -0
- package/dist/context/batches.js +130 -0
- package/dist/context/batches.js.map +1 -0
- package/dist/context/build.d.ts +57 -0
- package/dist/context/build.d.ts.map +1 -0
- package/dist/context/build.js +396 -0
- package/dist/context/build.js.map +1 -0
- package/dist/context/check.d.ts +42 -0
- package/dist/context/check.d.ts.map +1 -0
- package/dist/context/check.js +162 -0
- package/dist/context/check.js.map +1 -0
- package/dist/context/map-dir.d.ts +13 -0
- package/dist/context/map-dir.d.ts.map +1 -0
- package/dist/context/map-dir.js +81 -0
- package/dist/context/map-dir.js.map +1 -0
- package/dist/context/node-file.d.ts +109 -0
- package/dist/context/node-file.d.ts.map +1 -0
- package/dist/context/node-file.js +390 -0
- package/dist/context/node-file.js.map +1 -0
- package/dist/context/price.d.ts +49 -0
- package/dist/context/price.d.ts.map +1 -0
- package/dist/context/price.js +83 -0
- package/dist/context/price.js.map +1 -0
- package/dist/context/savings.d.ts +67 -0
- package/dist/context/savings.d.ts.map +1 -0
- package/dist/context/savings.js +125 -0
- package/dist/context/savings.js.map +1 -0
- package/dist/engine.d.ts +85 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +122 -0
- package/dist/engine.js.map +1 -0
- package/dist/graph/bindings.d.ts +64 -0
- package/dist/graph/bindings.d.ts.map +1 -0
- package/dist/graph/bindings.js +713 -0
- package/dist/graph/bindings.js.map +1 -0
- package/dist/graph/build-run.d.ts +25 -0
- package/dist/graph/build-run.d.ts.map +1 -0
- package/dist/graph/build-run.js +121 -0
- package/dist/graph/build-run.js.map +1 -0
- package/dist/graph/build.d.ts +88 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +386 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/cards.d.ts +46 -0
- package/dist/graph/cards.d.ts.map +1 -0
- package/dist/graph/cards.js +280 -0
- package/dist/graph/cards.js.map +1 -0
- package/dist/graph/check.d.ts +25 -0
- package/dist/graph/check.d.ts.map +1 -0
- package/dist/graph/check.js +192 -0
- package/dist/graph/check.js.map +1 -0
- package/dist/graph/container.d.ts +63 -0
- package/dist/graph/container.d.ts.map +1 -0
- package/dist/graph/container.js +191 -0
- package/dist/graph/container.js.map +1 -0
- package/dist/graph/depth-parser.d.ts +61 -0
- package/dist/graph/depth-parser.d.ts.map +1 -0
- package/dist/graph/depth-parser.js +153 -0
- package/dist/graph/depth-parser.js.map +1 -0
- package/dist/graph/enrich.d.ts +62 -0
- package/dist/graph/enrich.d.ts.map +1 -0
- package/dist/graph/enrich.js +258 -0
- package/dist/graph/enrich.js.map +1 -0
- package/dist/graph/extract-cache.d.ts +101 -0
- package/dist/graph/extract-cache.d.ts.map +1 -0
- package/dist/graph/extract-cache.js +199 -0
- package/dist/graph/extract-cache.js.map +1 -0
- package/dist/graph/extract.d.ts +85 -0
- package/dist/graph/extract.d.ts.map +1 -0
- package/dist/graph/extract.js +2377 -0
- package/dist/graph/extract.js.map +1 -0
- package/dist/graph/fingerprint.d.ts +76 -0
- package/dist/graph/fingerprint.d.ts.map +1 -0
- package/dist/graph/fingerprint.js +160 -0
- package/dist/graph/fingerprint.js.map +1 -0
- package/dist/graph/generic.d.ts +74 -0
- package/dist/graph/generic.d.ts.map +1 -0
- package/dist/graph/generic.js +773 -0
- package/dist/graph/generic.js.map +1 -0
- package/dist/graph/invariants.d.ts +33 -0
- package/dist/graph/invariants.d.ts.map +1 -0
- package/dist/graph/invariants.js +52 -0
- package/dist/graph/invariants.js.map +1 -0
- package/dist/graph/load.d.ts +29 -0
- package/dist/graph/load.d.ts.map +1 -0
- package/dist/graph/load.js +97 -0
- package/dist/graph/load.js.map +1 -0
- package/dist/graph/lsp/client.d.ts +61 -0
- package/dist/graph/lsp/client.d.ts.map +1 -0
- package/dist/graph/lsp/client.js +217 -0
- package/dist/graph/lsp/client.js.map +1 -0
- package/dist/graph/lsp/enrich.d.ts +11 -0
- package/dist/graph/lsp/enrich.d.ts.map +1 -0
- package/dist/graph/lsp/enrich.js +163 -0
- package/dist/graph/lsp/enrich.js.map +1 -0
- package/dist/graph/lsp/registry.d.ts +22 -0
- package/dist/graph/lsp/registry.d.ts.map +1 -0
- package/dist/graph/lsp/registry.js +71 -0
- package/dist/graph/lsp/registry.js.map +1 -0
- package/dist/graph/map.d.ts +107 -0
- package/dist/graph/map.d.ts.map +1 -0
- package/dist/graph/map.js +239 -0
- package/dist/graph/map.js.map +1 -0
- package/dist/graph/markdown.d.ts +22 -0
- package/dist/graph/markdown.d.ts.map +1 -0
- package/dist/graph/markdown.js +143 -0
- package/dist/graph/markdown.js.map +1 -0
- package/dist/graph/native.d.ts +46 -0
- package/dist/graph/native.d.ts.map +1 -0
- package/dist/graph/native.js +169 -0
- package/dist/graph/native.js.map +1 -0
- package/dist/graph/queries/c.scm +13 -0
- package/dist/graph/queries/c_sharp.scm +19 -0
- package/dist/graph/queries/clojure.scm +58 -0
- package/dist/graph/queries/cpp.scm +23 -0
- package/dist/graph/queries/dart.scm +42 -0
- package/dist/graph/queries/elixir.scm +54 -0
- package/dist/graph/queries/gleam.scm +51 -0
- package/dist/graph/queries/java.scm +20 -0
- package/dist/graph/queries/kotlin.scm +43 -0
- package/dist/graph/queries/lua.scm +36 -0
- package/dist/graph/queries/nix.scm +32 -0
- package/dist/graph/queries/php.scm +40 -0
- package/dist/graph/queries/ruby.scm +65 -0
- package/dist/graph/queries/rust.scm +41 -0
- package/dist/graph/queries/scala.scm +66 -0
- package/dist/graph/queries/solidity.scm +43 -0
- package/dist/graph/queries/swift.scm +51 -0
- package/dist/graph/refresh.d.ts +106 -0
- package/dist/graph/refresh.d.ts.map +1 -0
- package/dist/graph/refresh.js +430 -0
- package/dist/graph/refresh.js.map +1 -0
- package/dist/graph/relations.d.ts +14 -0
- package/dist/graph/relations.d.ts.map +1 -0
- package/dist/graph/relations.js +8 -0
- package/dist/graph/relations.js.map +1 -0
- package/dist/graph/resolve.d.ts +17 -0
- package/dist/graph/resolve.d.ts.map +1 -0
- package/dist/graph/resolve.js +716 -0
- package/dist/graph/resolve.js.map +1 -0
- package/dist/graph/root.d.ts +11 -0
- package/dist/graph/root.d.ts.map +1 -0
- package/dist/graph/root.js +40 -0
- package/dist/graph/root.js.map +1 -0
- package/dist/graph/scopes.d.ts +40 -0
- package/dist/graph/scopes.d.ts.map +1 -0
- package/dist/graph/scopes.js +383 -0
- package/dist/graph/scopes.js.map +1 -0
- package/dist/graph/seed.d.ts +38 -0
- package/dist/graph/seed.d.ts.map +1 -0
- package/dist/graph/seed.js +229 -0
- package/dist/graph/seed.js.map +1 -0
- package/dist/graph/source-files.d.ts +46 -0
- package/dist/graph/source-files.d.ts.map +1 -0
- package/dist/graph/source-files.js +118 -0
- package/dist/graph/source-files.js.map +1 -0
- package/dist/graph/traverse-cli.d.ts +55 -0
- package/dist/graph/traverse-cli.d.ts.map +1 -0
- package/dist/graph/traverse-cli.js +189 -0
- package/dist/graph/traverse-cli.js.map +1 -0
- package/dist/graph/traverse.d.ts +112 -0
- package/dist/graph/traverse.d.ts.map +1 -0
- package/dist/graph/traverse.js +192 -0
- package/dist/graph/traverse.js.map +1 -0
- package/dist/graph/types.d.ts +78 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +14 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/graph/wasm-runtime.d.ts +19 -0
- package/dist/graph/wasm-runtime.d.ts.map +1 -0
- package/dist/graph/wasm-runtime.js +38 -0
- package/dist/graph/wasm-runtime.js.map +1 -0
- package/dist/graph/workspace-cli.d.ts +56 -0
- package/dist/graph/workspace-cli.d.ts.map +1 -0
- package/dist/graph/workspace-cli.js +111 -0
- package/dist/graph/workspace-cli.js.map +1 -0
- package/dist/graph/workspace.d.ts +172 -0
- package/dist/graph/workspace.d.ts.map +1 -0
- package/dist/graph/workspace.js +714 -0
- package/dist/graph/workspace.js.map +1 -0
- package/dist/graph/write.d.ts +13 -0
- package/dist/graph/write.d.ts.map +1 -0
- package/dist/graph/write.js +86 -0
- package/dist/graph/write.js.map +1 -0
- package/dist/hosts/antigravity.d.ts +14 -0
- package/dist/hosts/antigravity.d.ts.map +1 -0
- package/dist/hosts/antigravity.js +59 -0
- package/dist/hosts/antigravity.js.map +1 -0
- package/dist/hosts/auto.d.ts +37 -0
- package/dist/hosts/auto.d.ts.map +1 -0
- package/dist/hosts/auto.js +141 -0
- package/dist/hosts/auto.js.map +1 -0
- package/dist/hosts/claude-global.d.ts +72 -0
- package/dist/hosts/claude-global.d.ts.map +1 -0
- package/dist/hosts/claude-global.js +230 -0
- package/dist/hosts/claude-global.js.map +1 -0
- package/dist/hosts/config-write.d.ts +36 -0
- package/dist/hosts/config-write.d.ts.map +1 -0
- package/dist/hosts/config-write.js +64 -0
- package/dist/hosts/config-write.js.map +1 -0
- package/dist/hosts/init.d.ts +28 -0
- package/dist/hosts/init.d.ts.map +1 -0
- package/dist/hosts/init.js +89 -0
- package/dist/hosts/init.js.map +1 -0
- package/dist/hosts/instructions.d.ts +14 -0
- package/dist/hosts/instructions.d.ts.map +1 -0
- package/dist/hosts/instructions.js +95 -0
- package/dist/hosts/instructions.js.map +1 -0
- package/dist/hosts/mcp-config.d.ts +78 -0
- package/dist/hosts/mcp-config.d.ts.map +1 -0
- package/dist/hosts/mcp-config.js +251 -0
- package/dist/hosts/mcp-config.js.map +1 -0
- package/dist/hosts/plan.d.ts +32 -0
- package/dist/hosts/plan.d.ts.map +1 -0
- package/dist/hosts/plan.js +73 -0
- package/dist/hosts/plan.js.map +1 -0
- package/dist/hosts/registry.d.ts +41 -0
- package/dist/hosts/registry.d.ts.map +1 -0
- package/dist/hosts/registry.js +164 -0
- package/dist/hosts/registry.js.map +1 -0
- package/dist/hosts/retract.d.ts +47 -0
- package/dist/hosts/retract.d.ts.map +1 -0
- package/dist/hosts/retract.js +520 -0
- package/dist/hosts/retract.js.map +1 -0
- package/dist/hosts/sections.d.ts +47 -0
- package/dist/hosts/sections.d.ts.map +1 -0
- package/dist/hosts/sections.js +127 -0
- package/dist/hosts/sections.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/fs.d.ts +79 -0
- package/dist/ingest/fs.d.ts.map +1 -0
- package/dist/ingest/fs.js +306 -0
- package/dist/ingest/fs.js.map +1 -0
- package/dist/init-guard.d.ts +16 -0
- package/dist/init-guard.d.ts.map +1 -0
- package/dist/init-guard.js +27 -0
- package/dist/init-guard.js.map +1 -0
- package/dist/mcp/instructions.d.ts +5 -0
- package/dist/mcp/instructions.d.ts.map +1 -0
- package/dist/mcp/instructions.js +64 -0
- package/dist/mcp/instructions.js.map +1 -0
- package/dist/mcp/kept-listing.d.ts +27 -0
- package/dist/mcp/kept-listing.d.ts.map +1 -0
- package/dist/mcp/kept-listing.js +63 -0
- package/dist/mcp/kept-listing.js.map +1 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +147 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool-names.d.ts +27 -0
- package/dist/mcp/tool-names.d.ts.map +1 -0
- package/dist/mcp/tool-names.js +47 -0
- package/dist/mcp/tool-names.js.map +1 -0
- package/dist/mcp/tools.d.ts +22 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +440 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/node-version.d.ts +15 -0
- package/dist/node-version.d.ts.map +1 -0
- package/dist/node-version.js +23 -0
- package/dist/node-version.js.map +1 -0
- package/dist/search/grep-cli.d.ts +35 -0
- package/dist/search/grep-cli.d.ts.map +1 -0
- package/dist/search/grep-cli.js +115 -0
- package/dist/search/grep-cli.js.map +1 -0
- package/dist/search/grep.d.ts +60 -0
- package/dist/search/grep.d.ts.map +1 -0
- package/dist/search/grep.js +143 -0
- package/dist/search/grep.js.map +1 -0
- package/dist/search/ignored-code.d.ts +9 -0
- package/dist/search/ignored-code.d.ts.map +1 -0
- package/dist/search/ignored-code.js +73 -0
- package/dist/search/ignored-code.js.map +1 -0
- package/dist/search/user-regexp.d.ts +26 -0
- package/dist/search/user-regexp.d.ts.map +1 -0
- package/dist/search/user-regexp.js +150 -0
- package/dist/search/user-regexp.js.map +1 -0
- package/dist/telemetry/contract.d.ts +117 -0
- package/dist/telemetry/contract.d.ts.map +1 -0
- package/dist/telemetry/contract.js +341 -0
- package/dist/telemetry/contract.js.map +1 -0
- package/dist/telemetry/flush.d.ts +21 -0
- package/dist/telemetry/flush.d.ts.map +1 -0
- package/dist/telemetry/flush.js +85 -0
- package/dist/telemetry/flush.js.map +1 -0
- package/dist/telemetry/gate.d.ts +30 -0
- package/dist/telemetry/gate.d.ts.map +1 -0
- package/dist/telemetry/gate.js +81 -0
- package/dist/telemetry/gate.js.map +1 -0
- package/dist/telemetry/identity.d.ts +40 -0
- package/dist/telemetry/identity.d.ts.map +1 -0
- package/dist/telemetry/identity.js +80 -0
- package/dist/telemetry/identity.js.map +1 -0
- package/dist/telemetry/index.d.ts +18 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +16 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/key.d.ts +21 -0
- package/dist/telemetry/key.d.ts.map +1 -0
- package/dist/telemetry/key.js +49 -0
- package/dist/telemetry/key.js.map +1 -0
- package/dist/telemetry/notice.d.ts +19 -0
- package/dist/telemetry/notice.d.ts.map +1 -0
- package/dist/telemetry/notice.js +93 -0
- package/dist/telemetry/notice.js.map +1 -0
- package/dist/telemetry/queue.d.ts +35 -0
- package/dist/telemetry/queue.d.ts.map +1 -0
- package/dist/telemetry/queue.js +162 -0
- package/dist/telemetry/queue.js.map +1 -0
- package/dist/telemetry/send.d.ts +19 -0
- package/dist/telemetry/send.d.ts.map +1 -0
- package/dist/telemetry/send.js +84 -0
- package/dist/telemetry/send.js.map +1 -0
- package/dist/telemetry/sessions.d.ts +25 -0
- package/dist/telemetry/sessions.d.ts.map +1 -0
- package/dist/telemetry/sessions.js +104 -0
- package/dist/telemetry/sessions.js.map +1 -0
- package/dist/telemetry/track.d.ts +62 -0
- package/dist/telemetry/track.d.ts.map +1 -0
- package/dist/telemetry/track.js +143 -0
- package/dist/telemetry/track.js.map +1 -0
- package/dist/upkeep-run.d.ts +22 -0
- package/dist/upkeep-run.d.ts.map +1 -0
- package/dist/upkeep-run.js +85 -0
- package/dist/upkeep-run.js.map +1 -0
- package/dist/upkeep.d.ts +127 -0
- package/dist/upkeep.d.ts.map +1 -0
- package/dist/upkeep.js +252 -0
- package/dist/upkeep.js.map +1 -0
- package/dist/util/actions-folder.d.ts +2 -0
- package/dist/util/actions-folder.d.ts.map +1 -0
- package/dist/util/actions-folder.js +9 -0
- package/dist/util/actions-folder.js.map +1 -0
- package/dist/util/browser.d.ts +3 -0
- package/dist/util/browser.d.ts.map +1 -0
- package/dist/util/browser.js +14 -0
- package/dist/util/browser.js.map +1 -0
- package/dist/util/child-process.d.ts +15 -0
- package/dist/util/child-process.d.ts.map +1 -0
- package/dist/util/child-process.js +15 -0
- package/dist/util/child-process.js.map +1 -0
- package/dist/util/front-matter.d.ts +46 -0
- package/dist/util/front-matter.d.ts.map +1 -0
- package/dist/util/front-matter.js +113 -0
- package/dist/util/front-matter.js.map +1 -0
- package/dist/util/id.d.ts +7 -0
- package/dist/util/id.d.ts.map +1 -0
- package/dist/util/id.js +14 -0
- package/dist/util/id.js.map +1 -0
- package/dist/util/paths.d.ts +29 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +71 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/private-file.d.ts +14 -0
- package/dist/util/private-file.d.ts.map +1 -0
- package/dist/util/private-file.js +82 -0
- package/dist/util/private-file.js.map +1 -0
- package/dist/util/source.d.ts +4 -0
- package/dist/util/source.d.ts.map +1 -0
- package/dist/util/source.js +12 -0
- package/dist/util/source.js.map +1 -0
- package/dist/util/spinner.d.ts +15 -0
- package/dist/util/spinner.d.ts.map +1 -0
- package/dist/util/spinner.js +52 -0
- package/dist/util/spinner.js.map +1 -0
- package/dist/util/state.d.ts +120 -0
- package/dist/util/state.d.ts.map +1 -0
- package/dist/util/state.js +264 -0
- package/dist/util/state.js.map +1 -0
- package/dist/vendor/grammars/tree-sitter-go.LICENSE +21 -0
- package/dist/vendor/grammars/tree-sitter-go.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-java.LICENSE +21 -0
- package/dist/vendor/grammars/tree-sitter-java.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-kotlin.LICENSE +9 -0
- package/dist/vendor/grammars/tree-sitter-kotlin.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-php.LICENSE +22 -0
- package/dist/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-python.LICENSE +21 -0
- package/dist/vendor/grammars/tree-sitter-python.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-r.LICENSE +9 -0
- package/dist/vendor/grammars/tree-sitter-r.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-swift.LICENSE +21 -0
- package/dist/vendor/grammars/tree-sitter-swift.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/vendor/grammars/tree-sitter-typescript.LICENSE +21 -0
- package/dist/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/viewer/app.js +2 -0
- package/dist/viewer/index.html +49 -0
- package/dist/viewer/style.css +154 -0
- package/dist/viz/assemble.d.ts +83 -0
- package/dist/viz/assemble.d.ts.map +1 -0
- package/dist/viz/assemble.js +101 -0
- package/dist/viz/assemble.js.map +1 -0
- package/dist/viz/export.d.ts +39 -0
- package/dist/viz/export.d.ts.map +1 -0
- package/dist/viz/export.js +98 -0
- package/dist/viz/export.js.map +1 -0
- package/dist/viz/serve.d.ts +13 -0
- package/dist/viz/serve.d.ts.map +1 -0
- package/dist/viz/serve.js +130 -0
- package/dist/viz/serve.js.map +1 -0
- package/package.json +115 -4
- package/scripts/build-grammar-wasm.mjs +112 -0
- package/scripts/build-viewer.mjs +44 -0
- package/scripts/graph-quality.mjs +117 -0
- package/scripts/pack-trail.mjs +53 -0
- package/scripts/postinstall.mjs +65 -0
- package/scripts/run-tests.mjs +72 -0
- package/scripts/stamp-telemetry-key.mjs +56 -0
- package/scripts/tally-audit.mjs +140 -0
- package/scripts/vendor-grammars.mjs +53 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blast radius of a diff: what else in the repo depends on the lines this change
|
|
3
|
+
* touched.
|
|
4
|
+
*
|
|
5
|
+
* The whole command is a join between two things graft already has — git's changed
|
|
6
|
+
* line ranges (`diff.ts`) and the wiring graph's incoming edges (`traverse.ts`) —
|
|
7
|
+
* so there is no new graph logic here, only the seeding rule:
|
|
8
|
+
*
|
|
9
|
+
* changed line → the innermost symbol whose span contains it → seed
|
|
10
|
+
*
|
|
11
|
+
* Seeding by SYMBOL rather than by file is what makes the answer worth reading: a
|
|
12
|
+
* 400-line file with a one-line edit reports the dependents of that one function,
|
|
13
|
+
* not of everything the file happens to define. The file node is seeded too, so
|
|
14
|
+
* importers (whose edge targets the file, not the symbol) are still found.
|
|
15
|
+
*/
|
|
16
|
+
import { impactOfMany } from "../graph/traverse.js";
|
|
17
|
+
import { dirLabel, moduleIndex, parentDir, shortLabel } from "./modules.js";
|
|
18
|
+
const SPAN_RE = /^L(\d+)-L(\d+)$/;
|
|
19
|
+
function spanBounds(span) {
|
|
20
|
+
const m = SPAN_RE.exec(span);
|
|
21
|
+
return m ? { start: Number(m[1]), end: Number(m[2]) } : null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The innermost symbols whose spans overlap any changed range in `path`.
|
|
25
|
+
*
|
|
26
|
+
* "Innermost" matters for a method: a changed line inside `Cache.get` overlaps both
|
|
27
|
+
* the class span and the method span, and seeding the class would report everyone
|
|
28
|
+
* who touches any part of `Cache`. Nesting is resolved by keeping, for each range,
|
|
29
|
+
* only symbols that contain no other matched symbol.
|
|
30
|
+
*/
|
|
31
|
+
function seedsForFile(graph, path, ranges) {
|
|
32
|
+
const symbols = [];
|
|
33
|
+
for (const n of graph.nodes) {
|
|
34
|
+
if (n.kind === "file" || n.path !== path)
|
|
35
|
+
continue;
|
|
36
|
+
const b = spanBounds(n.span);
|
|
37
|
+
if (b)
|
|
38
|
+
symbols.push({ node: n, ...b });
|
|
39
|
+
}
|
|
40
|
+
if (symbols.length === 0)
|
|
41
|
+
return [];
|
|
42
|
+
const hit = new Map();
|
|
43
|
+
for (const r of ranges) {
|
|
44
|
+
const overlapping = symbols.filter((s) => s.start <= r.end && s.end >= r.start);
|
|
45
|
+
// Drop any symbol that strictly contains another overlapping one (the class
|
|
46
|
+
// around a changed method), keeping the tightest description of the edit.
|
|
47
|
+
for (const s of overlapping) {
|
|
48
|
+
const containsAnother = overlapping.some((o) => o !== s && o.start >= s.start && o.end <= s.end);
|
|
49
|
+
if (!containsAnother)
|
|
50
|
+
hit.set(s.node.id, s.node);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [...hit.values()];
|
|
54
|
+
}
|
|
55
|
+
/** Compute the blast radius of `changed` against `graph`. */
|
|
56
|
+
export function blastRadius(graph, changed, basis, opts) {
|
|
57
|
+
const fileNodes = new Map();
|
|
58
|
+
for (const n of graph.nodes)
|
|
59
|
+
if (n.kind === "file")
|
|
60
|
+
fileNodes.set(n.path, n);
|
|
61
|
+
const seeds = [];
|
|
62
|
+
const unindexed = [];
|
|
63
|
+
const deleted = [];
|
|
64
|
+
/** Seed nodes per changed file, kept for the area grouping and the test signal. */
|
|
65
|
+
const seedNodes = new Map();
|
|
66
|
+
/** Merged dependents, keyed by node id, at the shallowest depth any file reached
|
|
67
|
+
* them — plus which changed files did the reaching. */
|
|
68
|
+
const merged = new Map();
|
|
69
|
+
for (const file of changed) {
|
|
70
|
+
if (file.status === "deleted") {
|
|
71
|
+
deleted.push(file.path);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const fileNode = fileNodes.get(file.path);
|
|
75
|
+
if (!fileNode) {
|
|
76
|
+
unindexed.push(file.path);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// No hunk ranges (a rename with no content change, a mode change) or an added
|
|
80
|
+
// file: the unit of change is the file itself.
|
|
81
|
+
const symbolSeeds = file.ranges.length > 0 ? seedsForFile(graph, file.path, file.ranges) : [];
|
|
82
|
+
for (const node of symbolSeeds) {
|
|
83
|
+
seeds.push({ id: node.id, name: node.name, kind: node.kind, path: node.path, span: node.span, wholeFile: false });
|
|
84
|
+
}
|
|
85
|
+
if (symbolSeeds.length === 0) {
|
|
86
|
+
seeds.push({
|
|
87
|
+
id: fileNode.id, name: fileNode.name, kind: fileNode.kind,
|
|
88
|
+
path: fileNode.path, span: fileNode.span, wholeFile: true,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// One walk PER CHANGED FILE, not one walk over every seed at once. A combined
|
|
92
|
+
// walk records the depth a node was first reached at but not by which seed, so
|
|
93
|
+
// the diagram could only draw "every changed file reaches every module" — a
|
|
94
|
+
// cross-product that tells a reviewer nothing. Walking per file costs one
|
|
95
|
+
// adjacency build each (cheap: the graph is already in memory) and buys arrows
|
|
96
|
+
// that are true.
|
|
97
|
+
//
|
|
98
|
+
// The FILE node is a seed only when no symbol matched. `imports` edges target
|
|
99
|
+
// the file id, so seeding it alongside symbols would pull in every importer of
|
|
100
|
+
// the file — a one-line edit to one function would report every module that
|
|
101
|
+
// imports the module, which is the noise this command exists to avoid. With no
|
|
102
|
+
// symbol seeds (a change to a top-level constant, a new import, a file with no
|
|
103
|
+
// extracted symbols) the file IS the unit of change, and its importers are the
|
|
104
|
+
// only dependents there are.
|
|
105
|
+
const walkSeeds = symbolSeeds.length > 0 ? symbolSeeds : [fileNode];
|
|
106
|
+
seedNodes.set(file.path, walkSeeds);
|
|
107
|
+
const hits = impactOfMany(graph, walkSeeds, opts.depth, "in");
|
|
108
|
+
for (const h of hits) {
|
|
109
|
+
if (!hasNode(h))
|
|
110
|
+
continue;
|
|
111
|
+
const hit = toImpacted(h);
|
|
112
|
+
const prev = merged.get(hit.id);
|
|
113
|
+
if (!prev)
|
|
114
|
+
merged.set(hit.id, { hit, from: new Set([file.path]) });
|
|
115
|
+
else {
|
|
116
|
+
prev.from.add(file.path);
|
|
117
|
+
// Keep the shallowest reach: a symbol two hops from one changed file and one
|
|
118
|
+
// hop from another is one hop away from this PR.
|
|
119
|
+
if (hit.depth < prev.hit.depth)
|
|
120
|
+
prev.hit = hit;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const impacted = [...merged.values()].map((m) => m.hit);
|
|
125
|
+
const origins = new Map([...merged].map(([id, m]) => [id, m.from]));
|
|
126
|
+
const index = opts.modules ?? emptyIndex();
|
|
127
|
+
const grouped = groupByModule(impacted, changed, origins, index);
|
|
128
|
+
return {
|
|
129
|
+
basis,
|
|
130
|
+
depth: opts.depth,
|
|
131
|
+
changed,
|
|
132
|
+
unindexed,
|
|
133
|
+
deleted,
|
|
134
|
+
seeds,
|
|
135
|
+
impacted,
|
|
136
|
+
modules: grouped.modules,
|
|
137
|
+
testModules: grouped.testModules,
|
|
138
|
+
areas: changedAreas(graph, changed, seedNodes, index, grouped.modules),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Group the changed files into areas, and work out whether each one's tests moved.
|
|
143
|
+
*
|
|
144
|
+
* The diff's own side of the diagram used to be one box per changed file, which can
|
|
145
|
+
* never fit: a 24-file PR drew ten arbitrary paths. Grouped by the same concept
|
|
146
|
+
* labels the dependents use, the same PR is three or four circles — and once the
|
|
147
|
+
* files are grouped, "did a test that reaches this move too?" is one question per
|
|
148
|
+
* circle instead of per file.
|
|
149
|
+
*/
|
|
150
|
+
function changedAreas(graph, changed, seedNodes, index, modules) {
|
|
151
|
+
const changedTests = new Set(changed.filter((c) => TEST_PATH.test(c.path)).map((c) => c.path));
|
|
152
|
+
const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
153
|
+
/** How many edges point AT each node — how central it is in the graph. */
|
|
154
|
+
const inDegree = new Map();
|
|
155
|
+
for (const e of graph.edges)
|
|
156
|
+
inDegree.set(e.target, (inDegree.get(e.target) ?? 0) + 1);
|
|
157
|
+
/** Incoming edge sources per node id, resolved to the source's file path. */
|
|
158
|
+
const incoming = new Map();
|
|
159
|
+
for (const e of graph.edges) {
|
|
160
|
+
const path = nodeById.get(e.source)?.path;
|
|
161
|
+
if (!path || !TEST_PATH.test(path))
|
|
162
|
+
continue;
|
|
163
|
+
const at = incoming.get(e.target) ?? new Set();
|
|
164
|
+
at.add(path);
|
|
165
|
+
incoming.set(e.target, at);
|
|
166
|
+
}
|
|
167
|
+
// Group by DIRECTORY, not by concept. Concepts in a well-summarised repo are
|
|
168
|
+
// near-file-grained — graft's own PR produced nine of them, one file each — so
|
|
169
|
+
// grouping the diff by concept groups nothing: the left side was nine circles
|
|
170
|
+
// with near-identical truncated names. A directory is the coarser unit reviewers
|
|
171
|
+
// already use, and a concept name is only worth borrowing when one concept
|
|
172
|
+
// happens to claim the whole group.
|
|
173
|
+
const groups = new Map();
|
|
174
|
+
for (const path of seedNodes.keys()) {
|
|
175
|
+
// A changed test file is the signal, not the source of a blast radius. Drawing
|
|
176
|
+
// it as an area would put "your tests changed" on both sides of the arrow.
|
|
177
|
+
if (TEST_PATH.test(path))
|
|
178
|
+
continue;
|
|
179
|
+
const dir = dirLabel(path);
|
|
180
|
+
groups.set(dir, [...(groups.get(dir) ?? []), path]);
|
|
181
|
+
}
|
|
182
|
+
coarsen(groups, MAX_AREAS);
|
|
183
|
+
const byKey = new Map();
|
|
184
|
+
/** Hub candidates per area, ranked once every seed has been seen. */
|
|
185
|
+
const candidates = new Map();
|
|
186
|
+
for (const [dir, paths] of groups) {
|
|
187
|
+
const area = {
|
|
188
|
+
label: dir, labelSource: "symbol", key: dir,
|
|
189
|
+
files: [], seeds: 0, tests: "none", testFiles: [], changedTestFiles: [],
|
|
190
|
+
reached: 0, behavioural: 0, unreached: [], seedNames: [],
|
|
191
|
+
};
|
|
192
|
+
byKey.set(dir, area);
|
|
193
|
+
for (const path of paths) {
|
|
194
|
+
const nodes = seedNodes.get(path) ?? [];
|
|
195
|
+
area.files.push(path);
|
|
196
|
+
area.seeds += nodes.length;
|
|
197
|
+
for (const node of nodes) {
|
|
198
|
+
// Reach is measured over behaviour only. Counting interfaces and type aliases
|
|
199
|
+
// would sink every ratio: nothing "calls" a type, so a fully tested area of
|
|
200
|
+
// typed code would report a third of its symbols as unreached.
|
|
201
|
+
const behavioural = node.kind === "function" || node.kind === "method" || node.kind === "class";
|
|
202
|
+
const from = incoming.get(node.id) ?? new Set();
|
|
203
|
+
for (const t of from) {
|
|
204
|
+
if (!area.testFiles.includes(t))
|
|
205
|
+
area.testFiles.push(t);
|
|
206
|
+
if (changedTests.has(t) && !area.changedTestFiles.includes(t))
|
|
207
|
+
area.changedTestFiles.push(t);
|
|
208
|
+
}
|
|
209
|
+
candidates.set(area, [
|
|
210
|
+
...(candidates.get(area) ?? []),
|
|
211
|
+
{ name: node.name, behavioural, degree: inDegree.get(node.id) ?? 0 },
|
|
212
|
+
]);
|
|
213
|
+
if (!behavioural)
|
|
214
|
+
continue;
|
|
215
|
+
area.behavioural++;
|
|
216
|
+
if (from.size > 0)
|
|
217
|
+
area.reached++;
|
|
218
|
+
else
|
|
219
|
+
area.unreached.push(node.name);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
for (const area of byKey.values()) {
|
|
224
|
+
// The hub is the most-depended-on function in the area, not whichever symbol the
|
|
225
|
+
// file walk happened to end on: `LlmFailureGate` rather than `transportRetries`.
|
|
226
|
+
// Behaviour outranks types, since nothing calls an interface.
|
|
227
|
+
area.seedNames = (candidates.get(area) ?? [])
|
|
228
|
+
.sort((a, b) => Number(b.behavioural) - Number(a.behavioural) || b.degree - a.degree || a.name.localeCompare(b.name))
|
|
229
|
+
.map((c) => c.name);
|
|
230
|
+
const concept = sharedConcept(area.files, index);
|
|
231
|
+
area.label = concept ?? hubLabel(area.seedNames, area.key);
|
|
232
|
+
area.labelSource = concept ? "concept" : "symbol";
|
|
233
|
+
area.files.sort();
|
|
234
|
+
area.testFiles.sort();
|
|
235
|
+
area.changedTestFiles.sort();
|
|
236
|
+
area.tests =
|
|
237
|
+
area.behavioural === 0 ? "na"
|
|
238
|
+
: area.changedTestFiles.length > 0 ? "changed"
|
|
239
|
+
: area.testFiles.length > 0 ? "stale"
|
|
240
|
+
: "none";
|
|
241
|
+
}
|
|
242
|
+
// Ordered by how much each area actually reaches, because the diagram caps this
|
|
243
|
+
// list and folds the rest into one circle. Ordered by size instead, the fold ends
|
|
244
|
+
// up holding the best-connected areas and the tail circle collects every arrow —
|
|
245
|
+
// the same mistake, one level up, as capping changed FILES in diff order.
|
|
246
|
+
const reach = (a) => modules.filter((m) => m.from.some((f) => a.files.includes(f))).length;
|
|
247
|
+
return [...byKey.values()].sort((a, b) => reach(b) - reach(a) || b.files.length - a.files.length || a.label.localeCompare(b.label));
|
|
248
|
+
}
|
|
249
|
+
/** Convenience wrapper: build the module index from a context dir. */
|
|
250
|
+
export function blastRadiusIn(graph, contextDir, changed, basis, depth) {
|
|
251
|
+
return blastRadius(graph, changed, basis, { depth, modules: moduleIndex(contextDir) });
|
|
252
|
+
}
|
|
253
|
+
function hasNode(h) {
|
|
254
|
+
// Unresolved endpoints (an import module string no node exists for) carry no
|
|
255
|
+
// location, so they can neither be grouped nor opened — dropped rather than
|
|
256
|
+
// rendered as a box with no file behind it.
|
|
257
|
+
return h.node !== null;
|
|
258
|
+
}
|
|
259
|
+
function toImpacted(h) {
|
|
260
|
+
return {
|
|
261
|
+
id: h.node.id, name: h.node.name, kind: h.node.kind,
|
|
262
|
+
path: h.node.path, span: h.node.span, relation: h.relation, depth: h.depth,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function emptyIndex() {
|
|
266
|
+
return { hasConcepts: false, labelOf: (p) => p, conceptOf: () => null };
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The concept claiming EVERY file in a cluster, or null when they disagree.
|
|
270
|
+
*
|
|
271
|
+
* Unanimity is the point: a concept that covers three of five files describes
|
|
272
|
+
* something narrower than the cluster, and stamping its name on the whole thing is
|
|
273
|
+
* how a diagram starts lying about what it drew.
|
|
274
|
+
*/
|
|
275
|
+
function sharedConcept(paths, index) {
|
|
276
|
+
const concepts = new Set(paths.map((p) => index.conceptOf(p)));
|
|
277
|
+
const [only] = concepts;
|
|
278
|
+
return concepts.size === 1 && only ? shortLabel(only) : null;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* The deterministic backstop label: the cluster's most significant symbol.
|
|
282
|
+
*
|
|
283
|
+
* Reached with no concept, no cache and no API key, so this is what guarantees a
|
|
284
|
+
* circle never carries a bare directory. It is a fact rather than a guess — the
|
|
285
|
+
* reviewer can grep the name — which is why it beats borrowing a neighbouring
|
|
286
|
+
* concept: on graft's own graph that borrowing labelled the freshness gate
|
|
287
|
+
* "Graph Extraction and Loading", which misleads worse than any path.
|
|
288
|
+
*/
|
|
289
|
+
export function hubLabel(names, fallback) {
|
|
290
|
+
const [head] = names.filter(Boolean);
|
|
291
|
+
// The hub alone, with no "+8" tail: how many symbols came with it is already the
|
|
292
|
+
// circle's second line and the table's own column, and repeating it in the label
|
|
293
|
+
// turned "writeGraph" into "writeGraph +8", which reads as a version number.
|
|
294
|
+
return head ? shortLabel(head) : fallback;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Group dependents into modules. `origins` carries, per dependent, the changed
|
|
298
|
+
* files whose walk actually reached it — so a module's `from` is the real arrow
|
|
299
|
+
* set, not every changed file in the PR.
|
|
300
|
+
*/
|
|
301
|
+
function groupByModule(impacted, changed, origins, modules) {
|
|
302
|
+
const changedPaths = new Set(changed.map((c) => c.path));
|
|
303
|
+
const byKey = new Map();
|
|
304
|
+
for (const hit of impacted) {
|
|
305
|
+
// A dependent inside a file the PR already changes is not "reach" — it is the
|
|
306
|
+
// diff. Reviewers see it in the diff itself; repeating it as blast radius is
|
|
307
|
+
// what makes these comments feel like noise.
|
|
308
|
+
if (changedPaths.has(hit.path))
|
|
309
|
+
continue;
|
|
310
|
+
// Cluster by concept where one exists, else by directory. Clustering and
|
|
311
|
+
// LABELLING are separate steps now: the key only has to be stable, while the
|
|
312
|
+
// label goes through the ladder below and may end up naming a symbol.
|
|
313
|
+
const key = modules.conceptOf(hit.path) ?? dirLabel(hit.path);
|
|
314
|
+
let mod = byKey.get(key);
|
|
315
|
+
if (!mod) {
|
|
316
|
+
mod = { label: key, labelSource: "symbol", key, files: [], symbols: [], from: [] };
|
|
317
|
+
byKey.set(key, mod);
|
|
318
|
+
}
|
|
319
|
+
mod.symbols.push(hit);
|
|
320
|
+
if (!mod.files.includes(hit.path))
|
|
321
|
+
mod.files.push(hit.path);
|
|
322
|
+
}
|
|
323
|
+
for (const mod of byKey.values()) {
|
|
324
|
+
const from = new Set();
|
|
325
|
+
for (const s of mod.symbols)
|
|
326
|
+
for (const p of origins.get(s.id) ?? [])
|
|
327
|
+
from.add(p);
|
|
328
|
+
mod.from = [...from].sort();
|
|
329
|
+
mod.files.sort();
|
|
330
|
+
mod.symbols.sort((a, b) => a.depth - b.depth || a.path.localeCompare(b.path));
|
|
331
|
+
// Shallowest symbol first after that sort, so it is the cluster's hub: the
|
|
332
|
+
// thing one hop from the diff rather than an arbitrary member.
|
|
333
|
+
const concept = sharedConcept(mod.files, modules);
|
|
334
|
+
mod.label = concept ?? hubLabel(mod.symbols.map((s) => s.name), mod.key);
|
|
335
|
+
mod.labelSource = concept ? "concept" : "symbol";
|
|
336
|
+
}
|
|
337
|
+
// Test-only modules are separated, not just sorted last. On a repo with a test
|
|
338
|
+
// per module they are the majority of the graph's dependents — graft's own 24-file
|
|
339
|
+
// PR produced 31 modules, 24 of them a single test file — so leaving them in the
|
|
340
|
+
// same list means they crowd out every module a reviewer needs whatever the caps
|
|
341
|
+
// are. "Your tests reference the thing you changed" is one line, not 24 sections.
|
|
342
|
+
const bySize = (a, b) => b.symbols.length - a.symbols.length || a.label.localeCompare(b.label);
|
|
343
|
+
const all = [...byKey.values()];
|
|
344
|
+
return {
|
|
345
|
+
modules: all.filter((m) => !isTestOnly(m)).sort(bySize),
|
|
346
|
+
testModules: all.filter(isTestOnly).sort(bySize),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Directory groups the diff is folded down to before anything is drawn.
|
|
351
|
+
*
|
|
352
|
+
* Kept here rather than in the renderer because it is a statement about the diff,
|
|
353
|
+
* not about the picture: five areas is roughly what a reviewer holds in their head,
|
|
354
|
+
* and the renderer then draws all of them.
|
|
355
|
+
*/
|
|
356
|
+
const MAX_AREAS = 5;
|
|
357
|
+
/**
|
|
358
|
+
* Fold directory groups into their parents until there are at most `max`.
|
|
359
|
+
*
|
|
360
|
+
* The deepest group goes first, and only into a parent that already exists, so
|
|
361
|
+
* `src/ai/llm/` joins `src/ai/` rather than everything collapsing towards the repo
|
|
362
|
+
* root. When no group has an existing parent, one is created — but never at the top
|
|
363
|
+
* level, since a single `(root)` area is no grouping at all.
|
|
364
|
+
*/
|
|
365
|
+
function coarsen(groups, max) {
|
|
366
|
+
const depth = (dir) => dir.split("/").length;
|
|
367
|
+
while (groups.size > max) {
|
|
368
|
+
const keys = [...groups.keys()].sort((a, b) => depth(b) - depth(a) || (groups.get(a).length - groups.get(b).length));
|
|
369
|
+
const pick = keys.find((k) => groups.has(parentDir(k)) && parentDir(k) !== "") ??
|
|
370
|
+
keys.find((k) => parentDir(k) !== "");
|
|
371
|
+
if (!pick)
|
|
372
|
+
return;
|
|
373
|
+
const parent = parentDir(pick);
|
|
374
|
+
groups.set(parent, [...(groups.get(parent) ?? []), ...groups.get(pick)]);
|
|
375
|
+
groups.delete(pick);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const TEST_PATH = /(^|\/)(tests?|specs?|__tests__)\/|\.(test|spec)\.[cm]?[jt]sx?$|_test\.(go|py|rb)$/i;
|
|
379
|
+
/** True when every file in the module is a test file. */
|
|
380
|
+
function isTestOnly(mod) {
|
|
381
|
+
return mod.files.length > 0 && mod.files.every((f) => TEST_PATH.test(f));
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=blast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blast.js","sourceRoot":"","sources":["../../src/blast/blast.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type ChangeStatus = "added" | "modified" | "deleted" | "renamed";
|
|
2
|
+
/** A contiguous run of changed lines, in POST-image (new file) line numbers. */
|
|
3
|
+
export interface LineRange {
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* One line git reported inside a hunk.
|
|
9
|
+
*
|
|
10
|
+
* `n` is the POST-image number, so it is the number a reviewer sees in the file
|
|
11
|
+
* on disk — a deleted line has none.
|
|
12
|
+
*/
|
|
13
|
+
export interface DiffLine {
|
|
14
|
+
n: number | null;
|
|
15
|
+
sign: "+" | "-";
|
|
16
|
+
text: string;
|
|
17
|
+
}
|
|
18
|
+
/** A hunk's range and the lines in it. `ranges` on the file mirrors these. */
|
|
19
|
+
export interface Hunk extends LineRange {
|
|
20
|
+
lines: DiffLine[];
|
|
21
|
+
/** Lines dropped by the per-hunk cap — a generated file is not worth holding. */
|
|
22
|
+
dropped: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ChangedFile {
|
|
25
|
+
/** Repo-relative posix path, post-image (the new name for a rename). */
|
|
26
|
+
path: string;
|
|
27
|
+
status: ChangeStatus;
|
|
28
|
+
/** Pre-image path, renames only. */
|
|
29
|
+
oldPath?: string;
|
|
30
|
+
/** Post-image changed line ranges. Empty for a pure delete or a mode-only change. */
|
|
31
|
+
ranges: LineRange[];
|
|
32
|
+
/** The same hunks, carrying their text: what a panel shows so a reader sees the
|
|
33
|
+
* change rather than a line number they have to go look up. One per range. */
|
|
34
|
+
hunks: Hunk[];
|
|
35
|
+
}
|
|
36
|
+
export interface DiffResult {
|
|
37
|
+
/** Human label for what was compared, e.g. "origin/main...HEAD". */
|
|
38
|
+
basis: string;
|
|
39
|
+
files: ChangedFile[];
|
|
40
|
+
}
|
|
41
|
+
/** True when `ref` names something git can resolve — checked before diffing so
|
|
42
|
+
* an unknown `--base` fails with a caller-facing message, not a git stack. */
|
|
43
|
+
export declare function refExists(root: string, ref: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Changed files + post-image line ranges.
|
|
46
|
+
*
|
|
47
|
+
* With no `base` this reports the working tree against HEAD (the local
|
|
48
|
+
* "what am I about to push" case), falling back to the last commit when the
|
|
49
|
+
* tree is clean — so the command answers something useful when run by hand,
|
|
50
|
+
* and CI passes `--base` for the real PR range.
|
|
51
|
+
*/
|
|
52
|
+
export declare function changedFiles(root: string, base?: string): DiffResult | null;
|
|
53
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/blast/diff.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjB,IAAI,EAAE,GAAG,GAAG,GAAG,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8EAA8E;AAC9E,MAAM,WAAW,IAAK,SAAQ,SAAS;IACrC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB;kFAC8E;IAC9E,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAqBD;8EAC8E;AAC9E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE5D;AAcD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAe3E"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a PR touched, read from git: changed files plus the *line ranges* inside
|
|
3
|
+
* them, which is what turns a diff into graph seeds (an enclosing-symbol lookup
|
|
4
|
+
* per changed line, see `blast.ts`).
|
|
5
|
+
*
|
|
6
|
+
* Git is shelled out to rather than parsed from a library because the CI job
|
|
7
|
+
* this feeds already has git — and `--unified=0` gives exactly the post-image
|
|
8
|
+
* line ranges we need, with no context lines to subtract back out.
|
|
9
|
+
*/
|
|
10
|
+
import { WINHIDE } from "../util/child-process.js";
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { resolve } from "node:path";
|
|
13
|
+
const HUNK_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/;
|
|
14
|
+
/** Text kept per hunk, and per file. A regenerated lockfile is one hunk of 40,000
|
|
15
|
+
* lines: the ranges still matter (they seed the graph), the text does not, and it
|
|
16
|
+
* would otherwise ride along in `--format json` and in every exported page. */
|
|
17
|
+
const MAX_HUNK_LINES = 24;
|
|
18
|
+
const MAX_FILE_LINES = 200;
|
|
19
|
+
/** Run git in `root`, or return null when git fails (not a repo, unknown ref). */
|
|
20
|
+
function git(root, args) {
|
|
21
|
+
const res = spawnSync("git", ["-c", "core.quotePath=false", ...args], { ...WINHIDE, cwd: root,
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
24
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
25
|
+
});
|
|
26
|
+
if (res.error || res.status !== 0 || typeof res.stdout !== "string")
|
|
27
|
+
return null;
|
|
28
|
+
return res.stdout;
|
|
29
|
+
}
|
|
30
|
+
/** True when `ref` names something git can resolve — checked before diffing so
|
|
31
|
+
* an unknown `--base` fails with a caller-facing message, not a git stack. */
|
|
32
|
+
export function refExists(root, ref) {
|
|
33
|
+
return git(resolve(root), ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]) !== null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The diff arguments for a base ref, and the label to show for it.
|
|
37
|
+
*
|
|
38
|
+
* `<base>...HEAD` (three dots) diffs against the MERGE BASE, not the tip of the
|
|
39
|
+
* base branch — the difference matters on every PR whose base branch moved after
|
|
40
|
+
* it was cut: two-dot would attribute other people's merges to this PR and
|
|
41
|
+
* inflate the blast radius with files the author never touched.
|
|
42
|
+
*/
|
|
43
|
+
function rangeArgs(base) {
|
|
44
|
+
return { args: [`${base}...HEAD`], basis: `${base}...HEAD` };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Changed files + post-image line ranges.
|
|
48
|
+
*
|
|
49
|
+
* With no `base` this reports the working tree against HEAD (the local
|
|
50
|
+
* "what am I about to push" case), falling back to the last commit when the
|
|
51
|
+
* tree is clean — so the command answers something useful when run by hand,
|
|
52
|
+
* and CI passes `--base` for the real PR range.
|
|
53
|
+
*/
|
|
54
|
+
export function changedFiles(root, base) {
|
|
55
|
+
const dir = resolve(root);
|
|
56
|
+
if (base !== undefined) {
|
|
57
|
+
const { args, basis } = rangeArgs(base);
|
|
58
|
+
const files = diffFiles(dir, args);
|
|
59
|
+
return files === null ? null : { basis, files };
|
|
60
|
+
}
|
|
61
|
+
const working = diffFiles(dir, ["HEAD"]);
|
|
62
|
+
if (working === null)
|
|
63
|
+
return null;
|
|
64
|
+
if (working.length > 0)
|
|
65
|
+
return { basis: "working tree vs HEAD", files: working };
|
|
66
|
+
const last = diffFiles(dir, ["HEAD~1...HEAD"]);
|
|
67
|
+
if (last === null)
|
|
68
|
+
return { basis: "working tree vs HEAD", files: [] };
|
|
69
|
+
return { basis: "HEAD~1...HEAD", files: last };
|
|
70
|
+
}
|
|
71
|
+
/** Both git passes for one range: name-status (statuses, renames) then hunks. */
|
|
72
|
+
function diffFiles(dir, range) {
|
|
73
|
+
const status = git(dir, ["diff", "--name-status", "--find-renames", "-z", ...range, "--"]);
|
|
74
|
+
if (status === null)
|
|
75
|
+
return null;
|
|
76
|
+
const files = parseNameStatus(status);
|
|
77
|
+
if (files.length === 0)
|
|
78
|
+
return files;
|
|
79
|
+
const patch = git(dir, [
|
|
80
|
+
"diff",
|
|
81
|
+
"--unified=0",
|
|
82
|
+
"--no-color",
|
|
83
|
+
"--no-ext-diff",
|
|
84
|
+
"--find-renames",
|
|
85
|
+
...range,
|
|
86
|
+
"--",
|
|
87
|
+
]);
|
|
88
|
+
if (patch !== null)
|
|
89
|
+
applyHunks(files, patch);
|
|
90
|
+
return files;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `--name-status -z` output: NUL-separated fields, where a rename spends THREE
|
|
94
|
+
* fields (`R096`, old, new) and everything else spends two. Splitting on NUL and
|
|
95
|
+
* walking with a cursor is the only way to read it — a line-oriented parse
|
|
96
|
+
* silently pairs a rename's old path with the next file's status.
|
|
97
|
+
*/
|
|
98
|
+
function parseNameStatus(out) {
|
|
99
|
+
const fields = out.split("\0").filter((f) => f !== "");
|
|
100
|
+
const files = [];
|
|
101
|
+
for (let i = 0; i < fields.length;) {
|
|
102
|
+
const code = fields[i++];
|
|
103
|
+
if (code.startsWith("R") || code.startsWith("C")) {
|
|
104
|
+
const oldPath = fields[i++];
|
|
105
|
+
const path = fields[i++];
|
|
106
|
+
if (path === undefined)
|
|
107
|
+
break;
|
|
108
|
+
files.push({ path, status: "renamed", oldPath, ranges: [], hunks: [] });
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const path = fields[i++];
|
|
112
|
+
if (path === undefined)
|
|
113
|
+
break;
|
|
114
|
+
if (code.startsWith("A"))
|
|
115
|
+
files.push({ path, status: "added", ranges: [], hunks: [] });
|
|
116
|
+
else if (code.startsWith("D"))
|
|
117
|
+
files.push({ path, status: "deleted", ranges: [], hunks: [] });
|
|
118
|
+
else
|
|
119
|
+
files.push({ path, status: "modified", ranges: [], hunks: [] });
|
|
120
|
+
}
|
|
121
|
+
return files;
|
|
122
|
+
}
|
|
123
|
+
/** Attach each hunk's post-image range — and its text — to its file. */
|
|
124
|
+
function applyHunks(files, patch) {
|
|
125
|
+
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
126
|
+
let current;
|
|
127
|
+
let hunk;
|
|
128
|
+
/** Post-image number for the next `+` line of the open hunk. */
|
|
129
|
+
let next = 0;
|
|
130
|
+
let kept = 0;
|
|
131
|
+
for (const line of patch.split("\n")) {
|
|
132
|
+
// Every file starts with this, and a content line cannot: a deleted one begins
|
|
133
|
+
// with `-`. Closing the open hunk here is what stops the NEXT file's `--- a/x`
|
|
134
|
+
// header being absorbed as a deleted line of the previous hunk.
|
|
135
|
+
if (line.startsWith("diff --git ")) {
|
|
136
|
+
current = undefined;
|
|
137
|
+
hunk = undefined;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (line.startsWith("+++ ")) {
|
|
141
|
+
const raw = line.slice(4);
|
|
142
|
+
current = raw === "/dev/null" ? undefined : byPath.get(stripPrefix(raw));
|
|
143
|
+
hunk = undefined;
|
|
144
|
+
kept = 0;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (line.startsWith("@@")) {
|
|
148
|
+
hunk = undefined;
|
|
149
|
+
if (!current)
|
|
150
|
+
continue;
|
|
151
|
+
const m = HUNK_RE.exec(line);
|
|
152
|
+
if (!m)
|
|
153
|
+
continue;
|
|
154
|
+
const start = Number(m[1]);
|
|
155
|
+
// `+N,0` is a pure deletion: nothing survives in the post-image, and git
|
|
156
|
+
// reports the line BEFORE the gap. Recording that single line is what keeps
|
|
157
|
+
// "the body of foo() lost 10 lines" attributable to foo() at all.
|
|
158
|
+
const count = m[2] === undefined ? 1 : Number(m[2]);
|
|
159
|
+
const range = count === 0 ? { start, end: start } : { start, end: start + count - 1 };
|
|
160
|
+
hunk = { ...range, lines: [], dropped: 0 };
|
|
161
|
+
current.ranges.push(range);
|
|
162
|
+
current.hunks.push(hunk);
|
|
163
|
+
next = start;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (!hunk)
|
|
167
|
+
continue;
|
|
168
|
+
// With `--unified=0` every line inside a hunk is an edit, so there is no
|
|
169
|
+
// context to skip. `+++`/`---` headers are already consumed above; a bare
|
|
170
|
+
// `--- a/x` for the next file arrives before its `+++`, and the `!hunk`
|
|
171
|
+
// guard on a fresh file keeps it out.
|
|
172
|
+
if (line.startsWith("+")) {
|
|
173
|
+
pushLine(hunk, { n: next, sign: "+", text: line.slice(1) }, kept++ < MAX_FILE_LINES);
|
|
174
|
+
next += 1;
|
|
175
|
+
}
|
|
176
|
+
else if (line.startsWith("-")) {
|
|
177
|
+
pushLine(hunk, { n: null, sign: "-", text: line.slice(1) }, kept++ < MAX_FILE_LINES);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function pushLine(hunk, line, withinFile) {
|
|
182
|
+
if (!withinFile || hunk.lines.length >= MAX_HUNK_LINES) {
|
|
183
|
+
hunk.dropped += 1;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
hunk.lines.push(line);
|
|
187
|
+
}
|
|
188
|
+
/** `b/src/x.ts` → `src/x.ts`, minus any trailing tab-separated timestamp. */
|
|
189
|
+
function stripPrefix(raw) {
|
|
190
|
+
const untabbed = raw.split("\t")[0];
|
|
191
|
+
return untabbed.startsWith("b/") ? untabbed.slice(2) : untabbed;
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=diff.js.map
|