scip-query 0.13.0 → 0.15.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +16 -2
  3. package/dist/augment-vue-worker.js +1 -1
  4. package/dist/{chunk-U3VMUDXJ.js → chunk-4SCMYKD6.js} +1 -1
  5. package/dist/{chunk-FERAXG6Y.js → chunk-X7CHWTZ2.js} +1 -1
  6. package/dist/chunk-YBGC7HTY.js +3 -0
  7. package/dist/cli.js +259 -251
  8. package/dist/{config-types-BrHl3Bge.d.ts → config-types-aCgyLrvE.d.ts} +2 -0
  9. package/dist/{db-_Bdx0E1W.d.ts → db-DwCdT3rv.d.ts} +1 -1
  10. package/dist/{health-BEZ1Rt0S.d.ts → health-nJUidasZ.d.ts} +1 -1
  11. package/dist/index.d.ts +3 -3
  12. package/dist/queries/affected.d.ts +2 -2
  13. package/dist/queries/bottlenecks.d.ts +2 -2
  14. package/dist/queries/by-kind.d.ts +2 -2
  15. package/dist/queries/call-graph.d.ts +2 -2
  16. package/dist/queries/change-surface.d.ts +2 -2
  17. package/dist/queries/cleanup-plan.d.ts +2 -2
  18. package/dist/queries/co-change.d.ts +2 -2
  19. package/dist/queries/code.d.ts +2 -2
  20. package/dist/queries/complexity-hotspots.d.ts +2 -2
  21. package/dist/queries/complexity.d.ts +2 -2
  22. package/dist/queries/convergence.d.ts +2 -2
  23. package/dist/queries/coupling.d.ts +2 -2
  24. package/dist/queries/cycles.d.ts +2 -2
  25. package/dist/queries/dataflow.d.ts +2 -2
  26. package/dist/queries/dead.d.ts +2 -2
  27. package/dist/queries/decorative-checkers.d.ts +2 -2
  28. package/dist/queries/deep-chains.d.ts +2 -2
  29. package/dist/queries/deps.d.ts +2 -2
  30. package/dist/queries/diff-gate.d.ts +2 -2
  31. package/dist/queries/diff-gate.js +1 -1
  32. package/dist/queries/diff-impact.d.ts +2 -2
  33. package/dist/queries/doc-drift.d.ts +2 -2
  34. package/dist/queries/drift.d.ts +2 -2
  35. package/dist/queries/duplicate-bodies.d.ts +2 -2
  36. package/dist/queries/extract-candidates.d.ts +2 -2
  37. package/dist/queries/fan.d.ts +2 -2
  38. package/dist/queries/files.d.ts +2 -2
  39. package/dist/queries/health.d.ts +3 -3
  40. package/dist/queries/health.js +1 -1
  41. package/dist/queries/hierarchy.d.ts +2 -2
  42. package/dist/queries/hotspots.d.ts +2 -2
  43. package/dist/queries/imports.d.ts +2 -2
  44. package/dist/queries/incomplete-migration.d.ts +2 -2
  45. package/dist/queries/index.d.ts +3 -3
  46. package/dist/queries/index.js +1 -1
  47. package/dist/queries/isolated.d.ts +2 -2
  48. package/dist/queries/locality-candidates.d.ts +2 -2
  49. package/dist/queries/members.d.ts +2 -2
  50. package/dist/queries/methods.d.ts +2 -2
  51. package/dist/queries/not-implemented.d.ts +2 -2
  52. package/dist/queries/outline.d.ts +2 -2
  53. package/dist/queries/passthrough-candidates.d.ts +2 -2
  54. package/dist/queries/plan-context.d.ts +2 -2
  55. package/dist/queries/react-component-duplicates.d.ts +2 -2
  56. package/dist/queries/react-hook-candidates.d.ts +2 -2
  57. package/dist/queries/react-large-component-pressure.d.ts +2 -2
  58. package/dist/queries/recent-duplicates.d.ts +2 -2
  59. package/dist/queries/redundant-reexports.d.ts +2 -2
  60. package/dist/queries/refs.d.ts +2 -2
  61. package/dist/queries/self-audit.d.ts +2 -2
  62. package/dist/queries/similar-chains.d.ts +2 -2
  63. package/dist/queries/similar-files.d.ts +2 -2
  64. package/dist/queries/similar-signatures.d.ts +2 -2
  65. package/dist/queries/similar.d.ts +2 -2
  66. package/dist/queries/slice.d.ts +2 -2
  67. package/dist/queries/stale-abstractions.d.ts +2 -2
  68. package/dist/queries/stats.d.ts +2 -2
  69. package/dist/queries/surface.d.ts +2 -2
  70. package/dist/queries/symbols.d.ts +2 -2
  71. package/dist/queries/system.d.ts +2 -2
  72. package/dist/queries/test-quality.d.ts +2 -2
  73. package/dist/queries/trace.d.ts +2 -2
  74. package/dist/queries/twin-ab.d.ts +2 -2
  75. package/dist/queries/twin-drift.d.ts +2 -2
  76. package/dist/queries/unused-imports.d.ts +2 -2
  77. package/dist/queries/unused-params.d.ts +2 -2
  78. package/dist/queries/vue-component-duplicates.d.ts +2 -2
  79. package/dist/queries/vue-composable-candidates.d.ts +2 -2
  80. package/dist/queries/vue-large-view-pressure.d.ts +2 -2
  81. package/dist/queries/wrapper-candidates.d.ts +2 -2
  82. package/dist/reindex-worker.js +12 -11
  83. package/dist/reindex.d.ts +3 -3
  84. package/dist/reindex.js +20 -19
  85. package/dist/runtime.d.ts +3 -3
  86. package/dist/runtime.js +2 -2
  87. package/dist/{scip-cli-C7cg4ZHR.d.ts → scip-cli-DeAbuJ2b.d.ts} +1 -1
  88. package/docs/COMMAND_REFERENCE.md +2 -1
  89. package/package.json +1 -1
  90. package/skills/_shared/SKILL.md +3 -1
  91. package/skills/scip-calibrate/SKILL.md +7 -3
  92. package/skills/scip-verify/SKILL.md +6 -1
  93. package/dist/chunk-XC2PJOVM.js +0 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to `scip-query` are documented here. This file starts at 0.11.0; everything below covers behavior changes made since the 0.10.12 release.
4
4
 
5
+ ## [0.15.0]
6
+
7
+ ### Breaking (behavior change for one command)
8
+
9
+ - **`suppress` writes one file per suppression instead of appending to `.scipquery.json`.** New suppressions land in `.scipquery/suppressions/<finding-id>.json` (check-level suppressions get a stable `CHECK-<hash>.json` name), stamped with `createdAt`. One-file-per-suppression makes concurrent branches merge without conflict and stops the config file from churning on every acceptance. Existing `suppressions[]` entries in `.scipquery.json` keep working (the gate reads both stores, identical matching semantics) — they are just no longer written. Commit the suppression files with your change.
10
+
11
+ ### Notable (new capabilities)
12
+
13
+ - **Committed outcome-event ledger.** Hook-mode `diff-gate` runs now mirror finding transitions (caught / resolved / suppressed / reopened) into `.scipquery/ledger/events.jsonl` — an append-only, one-event-per-line log stamped with the HEAD commit and, when known, the finding's SCIP symbol. A scoped `.gitattributes` (`merge=union`) is written alongside it, so concurrent appends from different branches merge without conflict; read-side dedupe by `(check, findingId, event, commit)` absorbs replays. Unlike the per-machine `evidence.db` ledger, this file is meant to be committed: outcome history survives re-clones and aggregates across every machine and agent working the repo. Ledger writes never block the hook — failures degrade to a stderr note.
14
+ - **New command: `effectiveness`.** Reads the committed event ledger and reports, per diff-gate check: findings caught, fixed by code changes (disappeared without a suppression), suppressed, still open, reopened, `moved` (rename noise — a resolved id whose symbol was re-caught under a new id at the same commit), precision (fixed ÷ concluded), and median days-to-fix. `--since 30d|12w|<ISO date>` windows by when the finding was caught, `--check <name>` filters, `--json` emits the standard envelope. This is the tool keeping score on itself: suppressions count against a detector's precision, fixes count for it.
15
+
16
+ ### Breaking (behavior change for one config key)
17
+
18
+ - **A non-empty `indexer.typescript.projects` list is now authoritative.** When set, exactly the listed projects are indexed: automatic tsconfig discovery does not run and the repo root is no longer re-added alongside the list (even when the root tsconfig covers subdirectories). Files covered only by an excluded root tsconfig (e.g. shared ambient `.d.ts` files) drop out of the index — pick the list deliberately. An empty or absent list falls back to full discovery, unchanged. Previously the configured list was merged additively with discovery, which made it impossible to exclude a whole-repo root shard.
19
+
20
+ ### Notable (new capabilities)
21
+
22
+ - **Per-project TypeScript shard caching in workspace mode.** With `indexer.typescript.projectMode: "workspace"`, each tsconfig project shard now has its own fingerprint and cached SCIP artifact (`language-indexes/typescript-projects/`); a reindex after an edit reruns only the changed projects and their dependents instead of every shard. Dependency edges come from workspace `package.json` dependencies and tsconfig `paths`/`references` targets (resolved through `extends` chains); unparseable manifests fail toward depend-on-everything, so the cache can over-invalidate but never serve a stale shard. Every per-project reuse decision appears in `reindex --json` shard diagnostics with an explicit miss reason (`typescript:<project>` entries). Metadata stays v3-additive: older binaries ignore the new field, and older metadata simply classifies every project as a miss. Measured on a 2,438-file four-package monorepo: full rebuild 57s (single mode) → 27s; reindex after an edit in a leaf package 35s → 9.5s.
23
+
5
24
  ## [0.11.0]
6
25
 
7
26
  ### Breaking (one-way doors — read before upgrading)
package/README.md CHANGED
@@ -224,7 +224,21 @@ Accepted findings can be recorded without weakening the rest of the gate:
224
224
  scip-query suppress SQABC123DEF456 --check echo --reason "intentional compatibility shim"
225
225
  ```
226
226
 
227
- This appends a reasoned entry to `.scipquery.json`; `config-validate` requires every suppression to include a reason plus either a stable finding id or both `check` and `file`. Check+file suppressions are allowed but warn because they waive every matching finding in that file. `diff-gate --json` reports both active and suppressed findings.
227
+ This writes one file per suppression under `.scipquery/suppressions/` — commit it with your change. One-file-per-suppression means two branches suppressing different findings merge without conflict; the legacy `suppressions[]` array in `.scipquery.json` is still honored (read-only). Every suppression requires a reason plus either a stable finding id or a `check` (optionally narrowed by `file`). Check+file suppressions are allowed but warn because they waive every matching finding in that file. `diff-gate --json` reports both active and suppressed findings.
228
+
229
+ **12. Measure whether the gate is earning its keep.** Every hook-mode `diff-gate` run appends finding transitions to a committed, merge-conflict-free ledger (`.scipquery/ledger/events.jsonl`, one JSON event per line under a scoped `merge=union` gitattribute). A finding that disappears without a suppression was fixed; a suppressed finding was noise or an accepted trade-off — the tool keeps score on itself:
230
+
231
+ ```bash
232
+ scip-query effectiveness --since 30d
233
+ ```
234
+
235
+ ```
236
+ check caught fixed suppressed open moved precision median-days-to-fix
237
+ echo 14 11 2 1 0 85% 0.8
238
+ new-dead 6 5 0 1 0 100% 0.3
239
+ ```
240
+
241
+ `precision` is fixed ÷ (fixed + suppressed) — how often acting on the check was the right call. `moved` separates rename churn from real fixes. Filter with `--check <name>`, window with `--since 30d|12w|<ISO date>`, and get machine-readable output with `--json`. Because the ledger is committed, the numbers survive re-clones and aggregate across every machine and agent working the repo.
228
242
 
229
243
  Before any edit, `plan-context <target>` bundles the structural picture — definitions, references, call graph, blast radius — plus a HISTORY section: churn, fix-commit density, and the files that usually change together with the target ("editing this usually means editing these").
230
244
 
@@ -380,7 +394,7 @@ Vue single-file components are handled through the JavaScript/TypeScript indexer
380
394
 
381
395
  By default, indexes live in `~/.cache/scip-query/projects/<hash>/`, keeping project directories clean. Override paths with `.scipquery.json` or `SCIP_QUERY_*` environment variables. Reindexing writes per-language SCIP shards next to the SQLite index, so a mixed-language repo can reuse unchanged language outputs and rerun only the languages whose source/config inputs changed.
382
396
 
383
- TypeScript monorepos can opt into project sharding with `indexer.typescript.projectMode: "workspace"`. In that mode, `scip-query` discovers repo-local TypeScript project roots, runs one `scip-typescript` process per project with bounded concurrency, merges the shard protobufs, and still publishes one TypeScript language index. Set `indexer.typescript.projects` to an explicit list of project directories or tsconfig paths when automatic discovery is too broad. Set `indexerConcurrency` when a repo needs a persistent worker cap; CLI `--indexer-concurrency` and `SCIP_QUERY_INDEXER_CONCURRENCY` still override ad hoc runs.
397
+ TypeScript monorepos can opt into project sharding with `indexer.typescript.projectMode: "workspace"`. In that mode, `scip-query` discovers repo-local TypeScript project roots, runs one `scip-typescript` process per project with bounded concurrency, merges the shard protobufs, and still publishes one TypeScript language index. Set `indexer.typescript.projects` to an explicit list of project directories or tsconfig paths when automatic discovery is too broad. When `projects` is set to a non-empty list, it is authoritative: only the listed projects are indexed, automatic discovery does not run, and the repo root is not re-added alongside them (even if the root tsconfig covers subdirectories) — files that are only covered by an excluded root tsconfig (e.g. shared ambient `.d.ts` files) drop out of the index, so pick the list deliberately. An empty or absent `projects` falls back to full discovery, unchanged. Workspace mode also caches each project shard: reindexing after an edit reruns only the changed projects and their dependents (workspace `package.json` dependencies and tsconfig `paths`/`references` targets count as dependencies), serves untouched projects from `language-indexes/typescript-projects/`, and reports every reuse decision in `reindex --json` shard diagnostics. Set `indexerConcurrency` when a repo needs a persistent worker cap; CLI `--indexer-concurrency` and `SCIP_QUERY_INDEXER_CONCURRENCY` still override ad hoc runs.
384
398
  Use `indexer.typescript.pnpmWorkspaces` only with the default single-project mode; workspace mode passes explicit projects instead.
385
399
 
386
400
  Clojure projects can pass a project-local `scip-clojure` config file through `.scipquery.json`:
@@ -1,2 +1,2 @@
1
- import{e as t}from"./chunk-FERAXG6Y.js";import{writeFileSync as o}from"fs";import{workerData as s}from"worker_threads";var e=s;try{let r=t({projectRoot:e.projectRoot,dbPath:e.dbPath,tsconfig:e.tsconfig,vueFiles:e.vueFiles,tasks:e.tasks});o(e.resultPath,JSON.stringify({ok:!0,result:r}))}catch(r){o(e.resultPath,JSON.stringify({ok:!1,error:r instanceof Error?r.message:String(r)}))}finally{let r=new Int32Array(e.sharedBuffer);Atomics.add(r,0,1),Atomics.notify(r,0)}
1
+ import{e as t}from"./chunk-X7CHWTZ2.js";import{writeFileSync as o}from"fs";import{workerData as s}from"worker_threads";var e=s;try{let r=t({projectRoot:e.projectRoot,dbPath:e.dbPath,tsconfig:e.tsconfig,vueFiles:e.vueFiles,tasks:e.tasks});o(e.resultPath,JSON.stringify({ok:!0,result:r}))}catch(r){o(e.resultPath,JSON.stringify({ok:!1,error:r instanceof Error?r.message:String(r)}))}finally{let r=new Int32Array(e.sharedBuffer);Atomics.add(r,0,1),Atomics.notify(r,0)}
2
2
  //# sourceMappingURL=augment-vue-worker.js.map
@@ -1,2 +1,2 @@
1
1
  import{a as ae}from"./chunk-7JZRFDCU.js";import{a as ue}from"./chunk-R5336VHZ.js";import{a as ne}from"./chunk-ZMGBWSFZ.js";import{a as se}from"./chunk-URSSPS5H.js";import{a as ce}from"./chunk-TG7QSYCJ.js";import{a as ie}from"./chunk-5AAAEZ2Z.js";import{a as oe}from"./chunk-J5WVNZ6O.js";import{b as G,e as m}from"./chunk-ZEO4CXQR.js";import{a as te}from"./chunk-27XEGMCT.js";import{a as Z}from"./chunk-6JUAGXR4.js";import{a as ee}from"./chunk-C2QSK7E7.js";import{a as Q}from"./chunk-6KMTPNZL.js";import{a as X}from"./chunk-JJUX2OAP.js";import{a as Y}from"./chunk-X3DTIEMU.js";import{a as K}from"./chunk-2XRXEIRK.js";import{b as W}from"./chunk-HZZQKNVE.js";import{a as U}from"./chunk-3AMWF6V5.js";import{a as A,b as q}from"./chunk-QVGFVRKD.js";import{b as N}from"./chunk-IUSSGHJO.js";import{a as z}from"./chunk-7COVQPQQ.js";import{a as re}from"./chunk-XDESMVOL.js";import{f as J}from"./chunk-KSJ544IV.js";import{a as j}from"./chunk-LY7NMOBW.js";import{f as k,g as M}from"./chunk-BZXE3HO4.js";import{h as _}from"./chunk-WS3Z6W3M.js";function de(e,r){let t=new Map;for(let o of e){let n=t.get(o.check)??[];n.push(o),t.set(o.check,n)}let i=[];for(let[o,n]of t){let c=n.filter(d=>d.outcome==="resolved").length,s=n.filter(d=>d.outcome==="suppressed").length,l=n.filter(d=>d.outcome==="still-open").length,h=n.length,C=n.filter(d=>d.outcome==="still-open").sort((d,b)=>d.firstSeen-b.firstSeen)[0];i.push({check:o,seen:h,resolved:c,suppressed:s,stillOpen:l,resolutionRate:h>0?c/h:0,suppressionRate:h>0?s/h:0,oldestOpenAgeDays:C?Math.floor((r-C.firstSeen)/Ie):null})}return i.sort((o,n)=>o.check.localeCompare(n.check))}var Ie=864e5;function le(e){return _(e).map(r=>({check:r.check,findingId:r.findingId,firstSeen:r.firstSeen,lastSeen:r.lastSeen,timesShown:r.timesShown,outcome:r.outcome}))}function f(e){return e.scoreCount??e.count}function B(e){let r=ze(e),{breakdown:t,pressure:i}=Ne(e),o=me(t,"risk"),n=me(t,"hygiene");return{score:Math.min(o,n),riskScore:o,hygieneScore:n,scoreBreakdown:t,overview:{documents:e.statsResult.documents,symbols:e.statsResult.symbols,indexSizeBytes:e.statsResult.indexSizeBytes},findings:{deadSymbols:e.dead.count,deadLoc:e.dead.loc,isolatedSymbols:e.isolated.count,isolatedLoc:e.isolated.loc,cycles:e.realCycleCount,similarPairs:e.similarCount,duplicateBodyGroups:e.duplicateBodies.count,duplicateBodyLoc:e.duplicateBodies.loc,twinDriftGroups:e.twinDrift.count,twinDriftLoc:e.twinDrift.loc,reactComponentDuplicatePairs:e.reactComponentDuplicates.count,reactHookCandidatePairs:e.reactHookCandidates.count,reactHookCandidateScoreCount:f(e.reactHookCandidates),reactLargeComponentPressureFiles:e.reactLargeComponentPressure.count,vueComponentDuplicatePairs:e.vueComponentDuplicates.count,vueComposableCandidatePairs:e.vueComposableCandidates.count,vueComposableCandidateScoreCount:f(e.vueComposableCandidates),vueLargeViewPressureFiles:e.vueLargeViewPressure.count,extractionCandidates:e.extractCount,wrappers:e.wrappers.count,wrapperScoreCount:f(e.wrappers),passthroughs:e.passthroughs.count,staleTypes:e.stale.count,driftedFiles:e.drift.count,complexityHotspotCount:e.complexity.extremeCount,hiddenCouplingPairs:e.gitEvidence?.hiddenCoupling.pairCount??null,hiddenCouplingScoreCount:e.gitEvidence?.hiddenCoupling.scoreCount??null,coverageContractViolations:e.coverageContracts.count},axes:Oe(e),validation:_e(e),suppressions:e.suppressions,actions:r,pressure:i,topComplexity:e.complexity.top,warnings:e.warnings.length>0?e.warnings:void 0,detectorPrecision:[]}}function Oe(e){return{deletable:{loc:e.dead.loc+e.isolated.loc,symbols:e.dead.count+e.isolated.count},cycles:{count:e.realCycleCount},changeAmplification:e.gitEvidence?.amplification??null,hiddenCoupling:e.gitEvidence?.hiddenCoupling??null,churnWeightedComplexity:Ve(e),evidenceQuality:{graphFindings:e.dead.count+e.isolated.count+e.realCycleCount,heuristicFindings:e.similarCount+e.duplicateBodies.count+e.twinDrift.count+e.reactComponentDuplicates.count+e.reactHookCandidates.count+e.reactLargeComponentPressure.count+e.vueComponentDuplicates.count+e.vueComposableCandidates.count+e.vueLargeViewPressure.count+e.extractCount+e.wrappers.count+e.passthroughs.count+e.stale.count+e.drift.count,userSuppressed:e.suppressions?.total??0}}}function Ve(e){let r=e.gitEvidence?.fileStats;return r?e.complexity.top.map(t=>{let i=t.file?r[t.file]?.changes??0:0;return{...t,changes:i,weighted:g(t.score*Math.log2(1+i))}}).sort((t,i)=>i.weighted-t.weighted):null}function _e(e){let r=e.gitEvidence?.fileStats;if(!r)return null;let t={dead:e.dead.files??[],isolated:e.isolated.files??[],twinDrift:e.twinDrift.files??[],wrappers:e.wrappers.files??[],passthroughs:e.passthroughs.files??[],stale:e.stale.files??[],reactComponents:e.reactComponentDuplicates.files??[],reactHooks:e.reactHookCandidates.files??[],reactLargeComponents:e.reactLargeComponentPressure.files??[],vueComponents:e.vueComponentDuplicates.files??[],vueComposables:e.vueComposableCandidates.files??[],vueLargeViews:e.vueLargeViewPressure.files??[]},i=new Set(Object.values(t).flat()),o=0,n=0,c=0;for(let[C,d]of Object.entries(r))i.has(C)?o+=d.fixChanges:(n+=d.fixChanges,c+=1);let s=i.size>0?g(o/i.size):0,l=c>0?g(n/c):0,h={};for(let[C,d]of Object.entries(t)){let b=new Set(d);if(b.size===0)continue;let S=0;for(let R of b)S+=r[R]?.fixChanges??0;let x=g(S/b.size);h[C]={flaggedFiles:b.size,fixDensity:x,lift:l>0?g(x/l):null}}return{flaggedFiles:i.size,flaggedFixDensity:s,baselineFixDensity:l,ratio:i.size>0&&l>0?g(s/l):null,byCategory:h,validationBasis:{method:"subject-regex",commitsScanned:e.gitEvidence?.commitsScanned??0}}}function g(e){return Math.round(e*100)/100}function he(e){return Number.isInteger(e)?String(e):e.toFixed(2).replace(/0+$/,"").replace(/\.$/,"")}function y(e,r="existing shared infrastructure discount"){let t=f(e);return Math.abs(t-e.count)<.01?"":` (${he(t)} score-weighted after ${r})`}function w(e,r){let t=f(e),i=`${e.count} ${r}`;return Math.abs(t-e.count)<.01?i:`${i} (${he(t)} score-weighted)`}function ze(e){let r=[];if(e.dead.count>0&&r.push({category:"Dead code",evidence:"graph-fact",description:`${e.dead.count} symbols with zero references anywhere -- deletion candidates; confirm with cleanup-plan --verify before deleting`,effort:"low",impact:"high",count:e.dead.count,locRecoverable:e.dead.loc}),e.isolated.count>0&&r.push({category:"Isolated symbols",evidence:"graph-fact",description:`${e.isolated.count} symbols completely disconnected from the codebase graph`,effort:"low",impact:"medium",count:e.isolated.count,locRecoverable:e.isolated.loc}),e.realCycleCount>0&&r.push({category:"Circular dependencies",evidence:"graph-fact",description:`${e.realCycleCount} cycle(s) \u2014 break with dependency inversion or module restructuring`,effort:"medium",impact:"high",count:e.realCycleCount,locRecoverable:0}),e.similarCount>0&&r.push({category:"Similar functions",evidence:"heuristic",description:`${e.similarCount} pairs with real logic overlap (beyond shared imports) \u2014 consolidation candidates`,effort:"medium",impact:"medium",count:e.similarCount,locRecoverable:0}),e.duplicateBodies.count>0&&r.push({category:"Duplicate function bodies",evidence:"heuristic",description:`${e.duplicateBodies.count} exact small-body group(s) across files \u2014 consolidate only when the domain concept matches`,effort:"low",impact:"medium",count:e.duplicateBodies.count,locRecoverable:e.duplicateBodies.loc}),e.twinDrift.count>0&&r.push({category:"Drifted twin implementations",evidence:"heuristic",description:`${e.twinDrift.count} same-name (or near-name) function group(s) across files with diverged or identical bodies \u2014 the same concept implemented twice; consolidate or verify the drift is intentional`,effort:"medium",impact:"medium",count:e.twinDrift.count,locRecoverable:0}),e.reactComponentDuplicates.count>0&&r.push({category:"Duplicated React components",evidence:"heuristic",description:`${e.reactComponentDuplicates.count} React component pair(s) share JSX structure \u2014 extract or reuse the shared UI concept`,effort:"medium",impact:"medium",count:e.reactComponentDuplicates.count,locRecoverable:e.reactComponentDuplicates.loc}),e.reactHookCandidates.count>0&&r.push({category:"Duplicated React hook behavior",evidence:"heuristic",description:`${e.reactHookCandidates.count} React component pair(s) share state/effect/request behavior${y(e.reactHookCandidates)} \u2014 extract or reuse a hook`,effort:"medium",impact:"medium",count:e.reactHookCandidates.count,locRecoverable:e.reactHookCandidates.loc}),e.reactLargeComponentPressure.count>0&&r.push({category:"Large React components",evidence:"heuristic",description:`${e.reactLargeComponentPressure.count} React component(s) concentrate JSX/behavior pressure \u2014 split by reason to change`,effort:"medium",impact:"medium",count:e.reactLargeComponentPressure.count,locRecoverable:0}),e.vueComponentDuplicates.count>0&&r.push({category:"Duplicated Vue components",evidence:"heuristic",description:`${e.vueComponentDuplicates.count} Vue component pair(s) share template structure \u2014 extract or reuse the shared UI concept`,effort:"medium",impact:"medium",count:e.vueComponentDuplicates.count,locRecoverable:e.vueComponentDuplicates.loc}),e.vueComposableCandidates.count>0&&r.push({category:"Duplicated Vue behavior",evidence:"heuristic",description:`${e.vueComposableCandidates.count} Vue component pair(s) share state/effect/request behavior${y(e.vueComposableCandidates)} \u2014 extract or reuse a composable`,effort:"medium",impact:"medium",count:e.vueComposableCandidates.count,locRecoverable:e.vueComposableCandidates.loc}),e.vueLargeViewPressure.count>0&&r.push({category:"Large Vue views",evidence:"heuristic",description:`${e.vueLargeViewPressure.count} Vue file(s) concentrate template/script/style pressure \u2014 split by reason to change`,effort:"medium",impact:"medium",count:e.vueLargeViewPressure.count,locRecoverable:0}),e.extractCount>0&&r.push({category:"Extraction candidates",evidence:"heuristic",description:`${e.extractCount} large functions with isolated callee clusters \u2014 review same-file or feature-local extraction seams`,effort:"medium",impact:"medium",count:e.extractCount,locRecoverable:0}),e.wrappers.count>0&&r.push({category:"Wrapper functions",evidence:"heuristic",description:`${e.wrappers.count} single-consumer symbols${y(e.wrappers,"boundary-evidence discount")} \u2014 inline direct wrappers or review boundary signals`,effort:"low",impact:"low",count:e.wrappers.count,locRecoverable:e.wrappers.loc}),e.passthroughs.count>0&&r.push({category:"Passthrough functions",evidence:"heuristic",description:`${e.passthroughs.count} functions that just forward to one callee \u2014 unnecessary indirection`,effort:"low",impact:"low",count:e.passthroughs.count,locRecoverable:e.passthroughs.loc}),e.stale.count>0){let o=[];e.stale.unused>0&&o.push(`${e.stale.unused} unused`),e.stale.singleUse>0&&o.push(`${e.stale.singleUse} single-consumer (not in types file)`),r.push({category:"Stale abstractions",evidence:"heuristic",description:`${o.join(", ")} \u2014 remove unused abstractions; review single-consumer ownership before moving or inlining`,effort:"low",impact:"medium",count:e.stale.count,locRecoverable:e.stale.loc})}if(e.drift.count>0){let o=[];e.drift.unusedImports>0&&o.push(`${e.drift.unusedImports} unused imports`),e.drift.layerViolations>0&&o.push(`${e.drift.layerViolations} layer violations`),r.push({category:"Structural drift",evidence:"heuristic",description:`${o.join(", ")} \u2014 remove direct drift; review signal drift against layer ownership`,effort:e.drift.layerViolations>0?"medium":"low",impact:e.drift.layerViolations>0?"medium":"low",count:e.drift.count,locRecoverable:0})}if(e.coverageContracts.count>0&&r.push({category:"Coverage contract drift",evidence:"graph-fact",description:`${e.coverageContracts.count} configured coverage contract(s) drifted from their ground-truth source (enumeration rot) \u2014 run \`scip-query diff-gate\` or \`scip-query health --json\` for the missing/extra keys`,effort:"low",impact:"medium",count:e.coverageContracts.count,locRecoverable:0}),e.gitEvidence&&e.gitEvidence.hiddenCoupling.pairCount>0){let o=e.gitEvidence.hiddenCoupling.top[0];r.push({category:"Hidden coupling",evidence:"change-graph",description:`${e.gitEvidence.hiddenCoupling.pairCount} file pair(s) co-change without a structural link`+y({count:e.gitEvidence.hiddenCoupling.pairCount,scoreCount:e.gitEvidence.hiddenCoupling.scoreCount},"broad/stale-history discount")+(o?` (e.g. ${o.fileA} \u2194 ${o.fileB})`:"")+" \u2014 name the shared concept or enforce the sync",effort:"medium",impact:"high",count:e.gitEvidence.hiddenCoupling.pairCount,locRecoverable:0})}let t={high:3,medium:2,low:1},i={low:3,medium:2,high:1};return r.sort((o,n)=>{let c=t[o.impact]*i[o.effort];return t[n.impact]*i[n.effort]-c}),r}var pe={"dead-code":"risk",isolated:"risk",cycles:"risk",complexity:"risk","hidden-coupling":"risk","cycles-pressure":"risk","complexity-pressure":"risk","hidden-coupling-pressure":"risk",similar:"hygiene","duplicate-bodies":"hygiene","twin-drift":"hygiene","react-component-duplicates":"hygiene","react-hook-candidates":"hygiene","react-large-component-pressure":"hygiene","vue-component-duplicates":"hygiene","vue-composable-candidates":"hygiene","vue-large-view-pressure":"hygiene",extract:"hygiene",wrappers:"hygiene",passthroughs:"hygiene","stale-abstractions":"hygiene",drift:"hygiene","similar-pressure":"hygiene","duplicate-bodies-pressure":"hygiene","twin-drift-pressure":"hygiene","react-component-duplicates-pressure":"hygiene","react-hook-candidates-pressure":"hygiene","react-large-component-pressure-pressure":"hygiene","vue-component-duplicates-pressure":"hygiene","vue-composable-candidates-pressure":"hygiene","vue-large-view-pressure-pressure":"hygiene","extract-pressure":"hygiene","wrappers-pressure":"hygiene","passthroughs-pressure":"hygiene","stale-abstractions-pressure":"hygiene","drift-pressure":"hygiene"};function me(e,r){let t=e.filter(i=>i.kind===r).reduce((i,o)=>i+o.points,0);return Math.max(0,Math.min(100,100-t))}function Ne(e){let r=Math.max(e.statsResult.documents,1),t=Math.max(e.statsResult.symbols,1),i=[],o=[],n=(p,H,v)=>{H>0&&i.push({axis:p,points:H,detail:v,kind:pe[p]??"hygiene"})},c=(p,H,v,Ae,Be,Te,Fe)=>{let L=Math.max(Ae,1);if(v<=L)return;let P=v/L,E=Math.min(Be,Math.ceil(Math.log2(P)*Te));if(E<=0)return;let $e=pe[p]??"hygiene";o.push({axis:p,category:H,kind:$e,count:v,threshold:g(L),ratio:g(P),extraPenalty:E}),n(p,E,`${v} ${Fe} exceed pressure threshold ${g(L)} (${g(P)}x)`)},s=e.dead.count/t;n("dead-code",Math.min(20,Math.round(s*200)),`${e.dead.count} dead symbols (${e.dead.loc} LOC deletable)`);let l=e.isolated.count/t;n("isolated",Math.min(10,Math.round(l*200)),`${e.isolated.count} isolated symbols (${e.isolated.loc} LOC deletable)`),n("cycles",Math.min(15,e.realCycleCount*5),`${e.realCycleCount} real dependency cycle(s)`);let h=e.similarCount/t*1e3;n("similar",Math.min(10,Math.round(h)),`${e.similarCount} similar function pair(s)`);let C=e.duplicateBodies.count/t*1e3;n("duplicate-bodies",Math.min(10,Math.round(C*2)),`${e.duplicateBodies.count} exact duplicate small-body group(s) (${e.duplicateBodies.loc} duplicate LOC)`);let d=e.twinDrift.count/t*1e3;n("twin-drift",Math.min(10,Math.round(d*2)),`${e.twinDrift.count} drifted twin implementation group(s) (same or near-same name, diverged or identical bodies)`);let b=e.reactComponentDuplicates.count/r*1e3;n("react-component-duplicates",Math.min(10,Math.round(b)),`${e.reactComponentDuplicates.count} duplicated React component pair(s)`);let S=f(e.reactHookCandidates),x=S/r*1e3;n("react-hook-candidates",Math.min(10,Math.round(x)),w(e.reactHookCandidates,"duplicated React behavior pair(s)"));let R=e.reactLargeComponentPressure.count/r*1e3;n("react-large-component-pressure",Math.min(5,Math.round(R/2)),`${e.reactLargeComponentPressure.count} large React component pressure file(s)`);let Pe=e.vueComponentDuplicates.count/r*1e3;n("vue-component-duplicates",Math.min(10,Math.round(Pe)),`${e.vueComponentDuplicates.count} duplicated Vue component pair(s)`);let I=f(e.vueComposableCandidates),Ee=I/r*1e3;n("vue-composable-candidates",Math.min(10,Math.round(Ee)),w(e.vueComposableCandidates,"duplicated Vue behavior pair(s)"));let ke=e.vueLargeViewPressure.count/r*1e3;n("vue-large-view-pressure",Math.min(5,Math.round(ke/2)),`${e.vueLargeViewPressure.count} large Vue view pressure file(s)`);let O=f(e.wrappers);n("wrappers",Math.min(3,O),w(e.wrappers,"wrapper candidate(s)"));let V=f(e.passthroughs);n("passthroughs",Math.min(3,V),w(e.passthroughs,"passthrough candidate(s)")),n("stale-abstractions",Math.min(8,e.stale.unused),`${e.stale.unused} unused stale abstraction(s); ${e.stale.singleUse} single-consumer signal(s)`);let Me=e.drift.direct/r;if(n("drift",Math.min(5,Math.round(Me*50)),`${e.drift.direct} direct drift finding(s); ${e.drift.signal} signal drift finding(s)`),n("complexity",Math.min(5,e.complexity.extremeCount*2),`${e.complexity.extremeCount} extreme complexity hotspot(s)`),e.gitEvidence){let p=e.gitEvidence.hiddenCoupling.scoreCount;n("hidden-coupling",Math.min(5,Math.ceil(p/10)),w({count:e.gitEvidence.hiddenCoupling.pairCount,scoreCount:p},"co-changing pair(s) without a structural link"))}if(c("cycles-pressure","Circular dependencies",e.realCycleCount,3,10,3,"cycle(s)"),c("complexity-pressure","Extreme complexity",e.complexity.extremeCount,3,5,2,"extreme complexity hotspot(s)"),e.gitEvidence){let p=e.gitEvidence.hiddenCoupling.scoreCount;c("hidden-coupling-pressure","Hidden coupling",p,50,10,4,"score-weighted co-changing pair(s)")}return c("similar-pressure","Similar functions",e.similarCount,Math.max(50,t*.01),8,4,"similar function pair(s)"),c("duplicate-bodies-pressure","Duplicate function bodies",e.duplicateBodies.count,Math.max(20,t*.005),8,4,"exact duplicate small-body group(s)"),c("twin-drift-pressure","Drifted twin implementations",e.twinDrift.count,Math.max(20,t*.005),8,4,"drifted twin implementation group(s)"),c("react-component-duplicates-pressure","Duplicated React components",e.reactComponentDuplicates.count,Math.max(10,r*.01),8,4,"duplicated React component pair(s)"),c("react-hook-candidates-pressure","Duplicated React hook behavior",S,Math.max(10,r*.01),8,4,"duplicated React behavior pair(s)"),c("react-large-component-pressure-pressure","Large React components",e.reactLargeComponentPressure.count,Math.max(10,r*.01),4,2,"large React component pressure file(s)"),c("vue-component-duplicates-pressure","Duplicated Vue components",e.vueComponentDuplicates.count,Math.max(10,r*.01),8,4,"duplicated Vue component pair(s)"),c("vue-composable-candidates-pressure","Duplicated Vue behavior",I,Math.max(10,r*.01),8,4,"duplicated Vue behavior pair(s)"),c("vue-large-view-pressure-pressure","Large Vue views",e.vueLargeViewPressure.count,Math.max(10,r*.01),4,2,"large Vue view pressure file(s)"),c("extract-pressure","Extraction candidates",e.extractCount,Math.max(50,t*.01),4,2,"extraction candidate(s)"),c("wrappers-pressure","Wrapper functions",O,50,4,2,"wrapper candidate(s)"),c("passthroughs-pressure","Passthrough functions",V,50,4,2,"passthrough candidate(s)"),c("stale-abstractions-pressure","Single-consumer stale abstraction signals",e.stale.singleUse,Math.max(50,t*.02),4,2,"single-consumer stale abstraction signal(s)"),c("drift-pressure","Structural drift signals",e.drift.signal,Math.max(10,r*.1),4,2,"signal drift finding(s)"),{breakdown:i,pressure:o}}import{getHeapStatistics as Ue}from"v8";var We=64*1024*1024;function T(){let e=globalThis.gc;if(!e)return;let r=Ue();r.heap_size_limit-r.used_heap_size<We||e()}var Ge=50,je=25e3,qe=2500,ge=2500,fe=50,Ye=10,Ke=["overview","dead","isolated","cycles","similar","duplicate-bodies","twin-drift","react-component-duplicates","react-hook-candidates","react-large-component-pressure","vue-component-duplicates","vue-composable-candidates","vue-large-view-pressure","extract-candidates","wrapper-candidates","passthrough-candidates","stale-abstractions","drift","complexity-hotspots","git-evidence","suppressions","coverage-contracts"],ve={overview:(e,r,t,i)=>({phase:"overview",statsResult:i,warnings:t.warnings}),dead:(e,r,t)=>({phase:"dead",dead:Je(e,r,t)}),isolated:(e,r,t)=>({phase:"isolated",isolated:Ze(e,r,t)}),cycles:(e,r,t)=>({phase:"cycles",realCycleCount:et(e,r,t)}),similar:(e,r,t)=>({phase:"similar",similarCount:tt(e,r,t)}),"duplicate-bodies":(e,r,t)=>({phase:"duplicate-bodies",duplicateBodies:it(e,r,t)}),"twin-drift":(e,r,t)=>({phase:"twin-drift",twinDrift:ot(e,r,t)}),"react-component-duplicates":(e,r,t)=>({phase:"react-component-duplicates",reactComponentDuplicates:nt(e,r,t)}),"react-hook-candidates":(e,r,t)=>({phase:"react-hook-candidates",reactHookCandidates:ct(e,r,t)}),"react-large-component-pressure":(e,r,t)=>({phase:"react-large-component-pressure",reactLargeComponentPressure:st(e,r,t)}),"vue-component-duplicates":(e,r,t)=>({phase:"vue-component-duplicates",vueComponentDuplicates:at(e,r,t)}),"vue-composable-candidates":(e,r,t)=>({phase:"vue-composable-candidates",vueComposableCandidates:ut(e,r,t)}),"vue-large-view-pressure":(e,r,t)=>({phase:"vue-large-view-pressure",vueLargeViewPressure:dt(e,r,t)}),"extract-candidates":(e,r,t)=>({phase:"extract-candidates",extractCount:rt(e,r,t)}),"wrapper-candidates":(e,r,t)=>({phase:"wrapper-candidates",wrappers:lt(e,r,t)}),"passthrough-candidates":(e,r,t)=>({phase:"passthrough-candidates",passthroughs:pt(e,r,t)}),"stale-abstractions":(e,r,t)=>({phase:"stale-abstractions",stale:mt(e,r,t)}),drift:(e,r,t)=>({phase:"drift",drift:Ct(e,r,t)}),"complexity-hotspots":(e,r,t)=>({phase:"complexity-hotspots",complexity:bt(e,r,t)}),"git-evidence":(e,r,t)=>({phase:"git-evidence",gitEvidence:ht(e,t)}),suppressions:(e,r,t)=>({phase:"suppressions",suppressions:gt(e,t)}),"coverage-contracts":(e,r,t)=>({phase:"coverage-contracts",coverageContracts:ft(e,t)})};function dr(e,r={}){return Se(e,r.full!==!1,(t,i)=>{let o=Qe(e,r.scope,t,i),n=B(o);return n.detectorPrecision=de(le(e),Date.now()),n})}function lr(e,r,t={}){return Xe(e,[r],t)[0]}function Xe(e,r,t={}){return Se(e,t.full!==!1,(i,o)=>{let n={...o,releaseCachesBetweenPhases:!1};return r.map(c=>ve[c](e,t.scope,n,i))})}function Se(e,r,t){let i=ee(e),o=vt(i,r);try{return t(i,o)}finally{A(e,{semanticProvider:!0}),T()}}function pr(e){return B(we(e))}function we(e){let r=u(e,"overview");return{statsResult:r.statsResult,warnings:r.warnings,dead:u(e,"dead").dead,isolated:u(e,"isolated").isolated,realCycleCount:u(e,"cycles").realCycleCount,similarCount:u(e,"similar").similarCount,duplicateBodies:u(e,"duplicate-bodies").duplicateBodies,twinDrift:u(e,"twin-drift").twinDrift,reactComponentDuplicates:u(e,"react-component-duplicates").reactComponentDuplicates,reactHookCandidates:u(e,"react-hook-candidates").reactHookCandidates,reactLargeComponentPressure:u(e,"react-large-component-pressure").reactLargeComponentPressure,vueComponentDuplicates:u(e,"vue-component-duplicates").vueComponentDuplicates,vueComposableCandidates:u(e,"vue-composable-candidates").vueComposableCandidates,vueLargeViewPressure:u(e,"vue-large-view-pressure").vueLargeViewPressure,extractCount:u(e,"extract-candidates").extractCount,wrappers:u(e,"wrapper-candidates").wrappers,passthroughs:u(e,"passthrough-candidates").passthroughs,stale:u(e,"stale-abstractions").stale,drift:u(e,"drift").drift,complexity:u(e,"complexity-hotspots").complexity,gitEvidence:Ce(e,"git-evidence")?.gitEvidence??null,suppressions:Ce(e,"suppressions")?.suppressions??null,coverageContracts:u(e,"coverage-contracts").coverageContracts}}function Ce(e,r){return e.find(t=>t.phase===r)}function u(e,r){let t=e.find(i=>i.phase===r);if(!t)throw new Error(`Missing health phase result: ${r}`);return t}function Qe(e,r,t,i){return we(Ke.map(o=>ve[o](e,r,i,t)))}function Je(e,r,t){return a(e,t,"dead",()=>{let i=q(e,{scope:r,...m.dead,scanLimit:t.candidateScanLimit});return D(xt(e,i.symbols))})}function Ze(e,r,t){return a(e,t,"isolated",()=>{let i=X(e,{scope:r,...m.isolated,scanLimit:t.candidateScanLimit});return D(Ht(e,i))})}function et(e,r,t){return a(e,t,"cycles",()=>j(e,{scope:r}).filter(o=>o.kind==="real").length)}function tt(e,r,t){let i=a(e,t,"similar",()=>J(e,{scope:r,...m.similar,scanLimit:t.candidateScanLimit}));return Math.min(i,t.candidateResultLimit)}function rt(e,r,t){return a(e,t,"extract-candidates",()=>K(e,{scope:r,...m.extract,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit}).length)}function it(e,r,t){return a(e,t,"duplicate-bodies",()=>{let i=U(e,{scope:r,...m.duplicateBodies,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit}),o=new Set,n=0;for(let c of i)for(let s of c.functions.slice(1))o.add(s.file),n+=s.loc;return{count:i.length,loc:n,files:[...o]}})}function ot(e,r,t){return a(e,t,"twin-drift",()=>{let i=W(e,{scope:r,...m.twinDrift,scanLimit:t.candidateScanLimit}).filter(c=>c.relationship==="divergent"||c.relationship==="identical"),o=new Set,n=0;for(let c of i)for(let s of c.members)o.add(s.file),n+=s.loc;return{count:i.length,loc:n,files:[...o].sort()}})}function nt(e,r,t){return a(e,t,"react-component-duplicates",()=>{let i=ie(e,{scope:r,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return F(i)})}function ct(e,r,t){return a(e,t,"react-hook-candidates",()=>{let i=oe(e,{scope:r,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return He(i,yt)})}function st(e,r,t){return a(e,t,"react-large-component-pressure",()=>{let i=ne(e,{scope:r,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return Lt(i)})}function at(e,r,t){return a(e,t,"vue-component-duplicates",()=>{let i=ce(e,{scope:r,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return F(i)})}function ut(e,r,t){return a(e,t,"vue-composable-candidates",()=>{let i=se(e,{scope:r,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return He(i,Dt)})}function dt(e,r,t){return a(e,t,"vue-large-view-pressure",()=>{let i=ae(e,{scope:r,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return D(i)})}function lt(e,r,t){return a(e,t,"wrapper-candidates",()=>{let i=te(e,{scope:r,...m.wrappers,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return xe(i,Rt)})}function pt(e,r,t){return a(e,t,"passthrough-candidates",()=>{let i=Q(e,{scope:r,...m.passthroughs,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit});return xe(i,Pt)})}function mt(e,r,t){return a(e,t,"stale-abstractions",()=>{let i=Z(e,{scope:r,...m.stale,limit:t.candidateResultLimit,scanLimit:t.candidateScanLimit}),o=i.filter(n=>n.consumers===0).length;return{count:i.length,loc:i.reduce((n,c)=>n+c.loc,0),files:[...new Set(i.map(n=>n.file))],unused:o,singleUse:i.length-o}})}function ht(e,r){return a(e,r,"git-evidence",()=>{let t=z(e),i=t.fileChurn();if(!i)return null;let o=t.commitHistory(),n=N(e,void 0,{limit:r.candidateResultLimit}),c={};for(let[s,l]of i)c[s]={changes:l.changes,fixChanges:l.fixChanges};return{amplification:t.changeAmplification(),hiddenCoupling:{pairCount:n.findings.length,scoreCount:$(n.findings.reduce((s,l)=>s+be(l),0)),top:n.findings.slice(0,5).map(s=>({fileA:s.fileA,fileB:s.fileB,together:s.together,confidence:s.confidence,focusedTogether:s.focusedTogether,broadTogether:s.broadTogether,broadCommitRatio:s.broadCommitRatio,lastTogetherAt:s.lastTogetherAt,recentTogether:s.recentTogether,commitScope:s.commitScope,recency:s.recency,scoreWeight:be(s),subjectContext:s.subjectContext}))},fileStats:c,commitsScanned:o?.commits.length??0}})}function gt(e,r){return a(e,r,"suppressions",()=>{let t=ue(e);return{total:t.total,byCategory:{...t.byCategory}}})}function ft(e,r){return a(e,r,"coverage-contracts",()=>{let t=e.config.coverageContracts??[],o=G(e,t).filter(n=>n.status!=="ok");return{count:o.length,loc:0,files:[...new Set(o.map(n=>n.file))].sort()}})}function Ct(e,r,t){return a(e,t,"drift",()=>{let i=Y(e,{scope:r,...m.drift,includePatternDeviations:!1,limit:Number.POSITIVE_INFINITY}),o=i.results.filter(s=>s.kind!=="pattern-deviation"),n=o.filter(s=>s.actionTier==="direct").length,c=o.length-n;return{count:o.length,unusedImports:i.unusedImports,layerViolations:i.layerViolations,direct:n,signal:c}})}function bt(e,r,t){return a(e,t,"complexity-hotspots",()=>{let i=re(e,{scope:r,minLoc:10,limit:t.complexityResultLimit,scanLimit:t.candidateScanLimit,semantic:!1});return{top:i.slice(0,5).map(o=>({symbol:o.shortName,score:o.score,file:o.file})),extremeCount:i.filter(o=>o.score>Ge).length}})}function vt(e,r){let t=e.symbols>=je||e.documents>=qe,i=r?Number.POSITIVE_INFINITY:fe,o=r?Number.POSITIVE_INFINITY:Ye;return!t||r?{candidateScanLimit:void 0,candidateResultLimit:i,complexityResultLimit:o,releaseCachesBetweenPhases:!0,warnings:r&&t?["Large index detected; running health without candidate scan or result caps because full mode is enabled."]:[]}:{candidateScanLimit:ge,candidateResultLimit:i,complexityResultLimit:o,releaseCachesBetweenPhases:!0,warnings:[`Large index detected; candidate-style health checks scanned their highest-priority ${ge} symbols and reported their top ${fe} findings. Enable full mode for unbounded candidate counts.`]}}function St(e,r){r.releaseCachesBetweenPhases&&(A(e),T())}function a(e,r,t,i){wt(t);try{return i()}finally{St(e,r)}}function wt(e){process.env.SCIP_QUERY_HEALTH_TRACE==="1"&&console.error(`[health] ${e}`)}function xt(e,r){return r.filter(t=>!k(e,t.relativePath)&&!M(e,t.symbol,t.relativePath)&&t.kind==="dead-code")}function Ht(e,r){return r.filter(t=>!k(e,t.relativePath)&&!M(e,t.symbol,t.relativePath))}function D(e){let r=new Set;for(let t of e){let i=t.relativePath??t.file;i&&r.add(i)}return{count:e.length,loc:e.reduce((t,i)=>t+i.loc,0),files:[...r]}}function xe(e,r){return{...D(e),scoreCount:$(e.reduce((i,o)=>i+r(o),0))}}function Lt(e){let r=new Map;for(let t of e)r.set(t.file,Math.max(r.get(t.file)??0,t.loc));return{count:r.size,loc:[...r.values()].reduce((t,i)=>t+i,0),files:[...r.keys()]}}function F(e){let r=new Map;for(let t of e)r.set(t.fileA,Math.max(r.get(t.fileA)??0,t.locA)),r.set(t.fileB,Math.max(r.get(t.fileB)??0,t.locB));return{count:e.length,loc:[...r.values()].reduce((t,i)=>t+i,0),files:[...r.keys()]}}function He(e,r){return{...F(e),scoreCount:$(e.reduce((i,o)=>i+r(o),0))}}function yt(e){let r=e.sharedHooks.length>0,t=Le({namedState:e.sharedState,requests:e.sharedRequests,lifecycle:e.sharedEffects,functions:e.sharedHandlers});return r&&t<=2?0:r?.5:ye(e.sharedState,e.sharedHandlers,e.sharedHandlerVerbs)?.25:t>=2?1:.5}function Dt(e){let r=e.sharedComposables.length+e.sharedStores.length>0,t=Le({namedState:e.sharedBindings,requests:e.sharedRequests,lifecycle:e.sharedLifecycle,functions:e.sharedFunctions});return r&&t<=2?0:r?.5:ye(e.sharedBindings,e.sharedFunctions,e.sharedFunctionVerbs)?.25:t>=2?1:.5}function Le(e){return e.requests.length*2+e.lifecycle.length+e.namedState.filter(r=>!Re.has(De(r))).length+Math.min(e.functions.length,3)}function ye(e,r,t){let i=[...e,...r,...t].map(De);return i.length>0&&i.every(o=>Re.has(o))}function De(e){return e.replace(/^handle/,"").replace(/^is/,"").replace(/^has/,"").toLowerCase()}function Rt(e){return e.actionTier==="signal"?.25:1}function Pt(e){return e.actionTier==="signal"?.25:1}function be(e){return e.commitScope==="broad-sweep"?e.recency==="recent"?.25:0:e.commitScope==="mixed"?e.recency==="recent"?.5:.25:e.recency==="recent"?1:.5}function $(e){return Math.round(e*100)/100}var Re=new Set(["add","apply","cancel","change","clear","close","create","delete","draft","edit","error","filter","form","load","loading","name","open","refresh","remove","reset","save","saving","search","select","selected","submit","toggle","update","value"]);export{Ke as a,dr as b,lr as c,Xe as d,pr as e};
2
- //# sourceMappingURL=chunk-U3VMUDXJ.js.map
2
+ //# sourceMappingURL=chunk-4SCMYKD6.js.map
@@ -69,4 +69,4 @@ Thumbs.db
69
69
  JOIN documents d ON d.id = der.document_id
70
70
  ORDER BY d.relative_path, (der.end_line - der.start_line) DESC
71
71
  `).all(),r=new Map;for(let n of t){let i=r.get(n.relativePath);i||(i={containingByLine:new Map,starts:[]},r.set(n.relativePath,i)),i.starts.push({line:n.startLine,symbolId:n.symbolId});for(let o=n.startLine;o<=n.endLine;o++)i.containingByLine.set(o,n.symbolId)}for(let n of r.values())n.starts.sort((i,o)=>i.line-o.line);return r}function _e(e,t,r){let n=0,i=e.length-1;for(;n<=i;){let s=Math.floor((n+i)/2);e[s].line<t?n=s+1:i=s-1}let o=null;for(let s of[i,n]){let a=e[s];if(!a)continue;let c=Math.abs(a.line-t);c>r||(!o||c<o.distance)&&(o={distance:c,symbolId:a.symbolId})}return o?.symbolId??null}function $(e,t){let r=We(t),n=e.prepare("SELECT id FROM global_symbols WHERE symbol = ?"),i=new Map;return{get(o){if(i.has(o))return i.get(o)??null;let s=Ue(n,r,t,o);return i.set(o,s),s}}}function G(e,t,r,n,i){if(e.fileName.endsWith(".vue")){let o=n.language.scripts.get(e.fileName),s=o?.generated?.languagePlugin.typescript?.getServiceScript(o.generated.root)?.code;if(o&&s){let a=n.language.maps.get(s,o);if(x(a,e.textSpan.start)!==null)return r.get(e.fileName)}return e.fileName.startsWith(i)?r.get(e.fileName):null}return t(e)}function*W(e){let t=/\b[A-Za-z_$][A-Za-z0-9_$]*\b/g,r=new Set(["script","setup","template","style","lang","scoped","true","false","null","undefined","const","let","var","import","from","export","return","if","else","for","while","function","class","type","interface","as","await","async"]),n;for(;n=t.exec(e);){let i=n[0];r.has(i)||(yield{text:i,start:n.index,end:n.index+i.length})}}function U(e,t){for(let[r]of e.toGeneratedLocation(t,n=>!!n.navigation))return r;return null}function x(e,t){let r=e;for(let[n]of r.toSourceLocation(t,i=>!!i.navigation))return n;return null}function Me(e,t){let r=0,n=e.lineStarts.length-1;for(;r<=n;){let o=Math.floor((r+n)/2);e.lineStarts[o]<=t?r=o+1:n=o-1}let i=Math.max(0,n);return{line:i,character:t-e.lineStarts[i]}}function H(e,t){return p(e,t).startsWith("node_modules/")}function p(e,t){return De(e,t).replaceAll("\\","/")}function $e(e){switch(Le(e)){case".vue":return"vue";case".tsx":return"typescriptreact";case".ts":case".mts":case".cts":return"typescript";case".jsx":return"javascriptreact";case".js":case".mjs":case".cjs":return"javascript";case".json":return"json";default:return"typescript"}}function K(){let e=new Map;return{get(t){if(e.has(t))return e.get(t)??null;try{let r=R(t,"utf-8"),n={text:r,lineStarts:Ge(r)};return e.set(t,n),n}catch{return e.set(t,null),null}},positionAt:Me}}function Ge(e){let t=[0];for(let r=0;r<e.length;r++)e.charCodeAt(r)===10&&t.push(r+1);return t}function We(e){try{let t=JSON.parse(R(N(e,"package.json"),"utf-8"));return{name:t.name??"workspace",version:t.version??"0.0.0"}}catch{return{name:"workspace",version:"0.0.0"}}}function Ue(e,t,r,n){let i=p(r,n),o=He(t.name,t.version,i);return e.get(o)?.id??null}function He(e,t,r){let n=r.split("/").map(i=>`\`${i.replaceAll("`","")}\``).join("/");return`scip-vue npm ${e} ${t} ${n}/default.`}function sn(e){let t=Xe(e.projectRoot,e.tsconfig),r=new Ke(e.dbPath,{readonly:!0});try{let n=Be({db:r,projectRoot:e.projectRoot,configPath:t,vueSymbolLookup:$(r,e.projectRoot)});return ze({vueFiles:e.vueFiles??[],tasks:e.tasks,...n})}finally{r.close()}}function Be(e){let t=K();return{projectRoot:e.projectRoot,context:_(e.projectRoot,e.configPath),symbolLookup:M(e.db,e.projectRoot,t),vueSymbolLookup:e.vueSymbolLookup,sourceReader:t}}function ze(e){let t=[],r=0,n=e.tasks??e.vueFiles.map(i=>({fileName:i,startOffset:0,endOffset:Number.POSITIVE_INFINITY,countFileSkip:!0}));for(let i of n){let o=Je(e,i);t.push(...o.occurrences),r+=o.skippedReferences}return{occurrences:t,skippedReferences:r}}function Je(e,t){let r=e.context.language.scripts.get(t.fileName),n=r?.generated?.languagePlugin.typescript?.getServiceScript(r.generated.root)?.code;if(!r||!n)return{occurrences:[],skippedReferences:t.countFileSkip?1:0};let i=e.sourceReader.get(t.fileName);if(!i)return{occurrences:[],skippedReferences:t.countFileSkip?1:0};let o=e.context.language.maps.get(n,r),s=p(e.projectRoot,t.fileName),a=[...W(i.text)],c={tokens:a.filter(u=>u.start>=t.startOffset&&u.start<t.endOffset),tokenByStart:new Map(a.map(u=>[u.start,u])),tokenTextCounts:Qe(a),processedStarts:new Set};return qe({...e,fileName:t.fileName,sourceInfo:i,sourceFile:s,map:o,tokenContext:c})}function qe(e){let t=[],r=0;for(let n of e.tokenContext.tokens){if(e.tokenContext.processedStarts.has(n.start))continue;let i=U(e.map,n.start);if(i===null)continue;let s=(e.context.languageService.getDefinitionAtPosition(e.fileName,i+1)??[]).find(c=>!H(e.projectRoot,c.fileName));if(!s){r++;continue}let a=G(s,e.symbolLookup,e.vueSymbolLookup,e.context,e.projectRoot);if(a===null){r++;continue}X(t,e.sourceReader,e.sourceInfo,e.sourceFile,n,a),e.tokenContext.processedStarts.add(n.start),Ye(t,e,n,i,a)}return{occurrences:t,skippedReferences:r}}function Ye(e,t,r,n,i){if(!((t.tokenContext.tokenTextCounts.get(r.text)??0)<=1))for(let o of Ze(t.context.languageService,t.fileName,n+1,t.map,r,t.tokenContext.tokenByStart)){if(t.tokenContext.processedStarts.has(o))continue;let s=t.tokenContext.tokenByStart.get(o);s&&(X(e,t.sourceReader,t.sourceInfo,t.sourceFile,s,i),t.tokenContext.processedStarts.add(o))}}function X(e,t,r,n,i,o){let s=t.positionAt(r,i.start);e.push({sourceFile:n,sourceLine:s.line,sourceStartChar:s.character,sourceEndChar:s.character+i.text.length,symbolId:o})}function Qe(e){let t=new Map;for(let r of e)t.set(r.text,(t.get(r.text)??0)+1);return t}function Ze(e,t,r,n,i,o){let s=e.getDocumentHighlights?.(t,r,[t])??[],a=[];for(let c of s)if(c.fileName===t)for(let u of c.highlightSpans){let l=et(n,u.textSpan.start,o,i.text);l!==null&&a.push(l)}return a}function et(e,t,r,n){let i=r.get(t);if(i?.text===n)return i.start;let o=x(e,t);if(o===null)return null;let s=r.get(o);return s?.text===n?s.start:null}export{Ft as a,Ot as b,Mt as c,$t as d,sn as e};
72
- //# sourceMappingURL=chunk-FERAXG6Y.js.map
72
+ //# sourceMappingURL=chunk-X7CHWTZ2.js.map
@@ -0,0 +1,3 @@
1
+ import{a as Se}from"./chunk-M7EEHYSU.js";import{a as Ce}from"./chunk-N2OTJWC2.js";import{a as re,c as se,d as ae,e as w}from"./chunk-ZEO4CXQR.js";import{a as be}from"./chunk-27XEGMCT.js";import{a as pe}from"./chunk-6JUAGXR4.js";import{a as ye}from"./chunk-C2QSK7E7.js";import{a as he}from"./chunk-6KMTPNZL.js";import{a as ue}from"./chunk-JJUX2OAP.js";import{a as de}from"./chunk-X3DTIEMU.js";import{a as fe}from"./chunk-2XRXEIRK.js";import{b as oe}from"./chunk-HZZQKNVE.js";import{a as te,b as ne}from"./chunk-3AMWF6V5.js";import{a as Z,b as Q,c as X,g as B,h as M}from"./chunk-KYB7FPXT.js";import{a as ee,b as v,e as ie}from"./chunk-Q7PWDXL5.js";import{b as le}from"./chunk-QVGFVRKD.js";import{a as N,c as V,d as J,e as Y}from"./chunk-IUSSGHJO.js";import{a as W}from"./chunk-7COVQPQQ.js";import{a as ge,e as me}from"./chunk-KSJ544IV.js";import{a as ce}from"./chunk-LY7NMOBW.js";import{a as q}from"./chunk-IMQFJ53K.js";import{f as R,g as T}from"./chunk-BZXE3HO4.js";import{i as U}from"./chunk-GULVO6NF.js";import{e as F,f as O,g as I}from"./chunk-X5C43YVI.js";import{a as z}from"./chunk-JZBGTSAB.js";import{a as _}from"./chunk-ISLWJ4PY.js";import{l as H}from"./chunk-WS3Z6W3M.js";import{createHash as Xe}from"crypto";import{existsSync as P}from"fs";function $e(e){let[i,t]=G(e,":"),o=i||"unknown",n=(a={})=>{let s=a.actionTier??Be(o);return{sourceAnalyzer:o,actionTier:s,rootCauseKey:a.rootCauseKey??(t||e),label:a.label??`${o} finding`,relatedFiles:a.relatedFiles??[],why:a.why??[],remediation:a.remediation??Me(s,o),file:a.file}};if(!t)return n({rootCauseKey:e,why:["Baseline identity did not include analyzer payload."]});if(["dead","isolated","extract","wrapper","passthrough","stale"].includes(o)){let[a,s]=G(t,":");return n({file:a,relatedFiles:a?[a]:[],rootCauseKey:s?`${a}:${s}`:t,label:`${o} finding`,why:s?[`Baseline subject: ${s}.`]:[]})}if(o==="cycle"){let a=t.split(">").filter(Boolean);return n({relatedFiles:a,rootCauseKey:t,label:"cycle finding",why:a.length>0?[`Cycle path: ${a.join(" > ")}.`]:[]})}if(o==="similar"){let a=t.split("|").filter(Boolean);return n({relatedFiles:[],rootCauseKey:t,label:"similarity finding",why:a.length>0?[`Similar symbols: ${a.join(" | ")}.`]:[]})}if(o==="drift"){let[a,s]=G(t,":"),[l,c]=G(s,":");return n({actionTier:a==="unused-import"?"direct":"signal",file:l,relatedFiles:[...new Set([l,c].filter(Boolean))],rootCauseKey:t,label:`${a||"drift"} finding`,why:[...a?[`Drift kind: ${a}.`]:[],...c?[`Related dependency: ${c}.`]:[]]})}return n({why:["Baseline analyzer prefix is not recognized by this diff-gate version."]})}function Be(e){switch(e){case"dead":case"isolated":case"cycle":case"passthrough":return"direct";case"similar":case"extract":case"wrapper":case"stale":return"signal";default:return"signal"}}function Me(e,i){return e==="direct"?`Fix the new ${i} baseline finding, or knowingly accept it via health --write-baseline.`:`Review the new ${i} baseline signal; fix it if it reflects real debt, or knowingly accept it via health --write-baseline.`}function G(e,i){let t=e.indexOf(i);return t<0?[e,""]:[e.slice(0,t),e.slice(t+i.length)]}import{existsSync as Ke,readFileSync as Le,writeFileSync as Pe}from"fs";import{isAbsolute as _e,join as He}from"path";var ze=25e3,Oe=2500,Ue=2500;function qe(e){let i=ye(e);return i.symbols>=ze||i.documents>=Oe?Ue:void 0}var We=".scipquery-baseline.json";function E(e,i){return i&&_e(i)?i:He(e.config.projectRoot,i??We)}function we(e,i={}){let{scope:t}=i,o=qe(e),n=[],a=le(e,{scope:t,...w.dead,scanLimit:o});for(let s of a.symbols)R(e,s.relativePath)||T(e,s.symbol,s.relativePath)||s.kind==="dead-code"&&n.push(`dead:${s.relativePath}:${s.shortName}`);for(let s of ue(e,{scope:t,...w.isolated,scanLimit:o}))R(e,s.relativePath)||T(e,s.symbol,s.relativePath)||n.push(`isolated:${s.relativePath}:${s.shortName}`);for(let s of ce(e,{scope:t}))s.kind==="real"&&n.push(`cycle:${Ve(s.path)}`);for(let s of me(e,{scope:t,...w.similar,scanLimit:o}))n.push(`similar:${[s.symbolA,s.symbolB].sort().join("|")}`);for(let s of te(e,{scope:t,...w.duplicateBodies,scanLimit:o}))n.push(`duplicate-bodies:${s.hash}:${s.functions.map(l=>l.symbol).sort().join("|")}`);for(let s of fe(e,{scope:t,...w.extract,scanLimit:o}))n.push(`extract:${s.relativePath}:${s.shortName}`);for(let s of be(e,{scope:t,...w.wrappers,scanLimit:o}))n.push(`wrapper:${s.file}:${s.shortName}`);for(let s of he(e,{scope:t,...w.passthroughs,scanLimit:o}))n.push(`passthrough:${s.file}:${s.shortName}`);for(let s of pe(e,{scope:t,...w.stale,scanLimit:o}))n.push(`stale:${s.file}:${s.shortName}`);for(let s of de(e,{scope:t,...w.drift,includePatternDeviations:!1,limit:Number.POSITIVE_INFINITY}).results)n.push(`drift:${s.kind}:${s.file}:${s.dep}`);return[...new Set(n)].sort()}function Ve(e){if(e.length===0)return"";let i=0;for(let t=1;t<e.length;t++)e[t]<e[i]&&(i=t);return[...e.slice(i),...e.slice(0,i)].join(">")}function Ui(e,i={}){let t=we(e,{scope:i.scope}),o=E(e,i.path);return Pe(o,JSON.stringify({version:1,findings:t},null,2)+`
2
+ `),{path:o,findingCount:t.length}}function De(e,i={}){let t=E(e,i.path);if(!Ke(t))throw new Error(`No baseline found at ${t}. Create one with: scip-query health --write-baseline`);let o=JSON.parse(Le(t,"utf-8")),n=new Set(o.findings??[]),a=we(e,{scope:i.scope}),s=new Set(a);return{baselinePath:t,baselineCount:n.size,current:a,newFindings:a.filter(l=>!n.has(l)),fixedFindings:[...n].filter(l=>!s.has(l))}}import{existsSync as Je,mkdirSync as Vi,readdirSync as Ye,readFileSync as Ze,writeFileSync as Ji}from"fs";import{createHash as Zi}from"crypto";import{join as K}from"path";var A=K(".scipquery","suppressions");function Qe(e){return K(e,A)}function ke(e){let i=Qe(e);if(!Je(i))return{suppressions:[],warnings:[]};let t=[],o=[];for(let n of Ye(i).sort()){if(!n.endsWith(".json"))continue;let a=K(i,n),s;try{s=JSON.parse(Ze(a,"utf-8"))}catch{o.push(`${A}/${n}: malformed JSON \u2014 ignored`);continue}if(typeof s!="object"||s===null||Array.isArray(s)){o.push(`${A}/${n}: not a suppression object \u2014 ignored`);continue}let l=s;if(typeof l.reason!="string"||l.reason.trim()===""){o.push(`${A}/${n}: missing reason \u2014 ignored`);continue}if(!l.id&&!l.check){o.push(`${A}/${n}: needs an id or a check \u2014 ignored`);continue}t.push(l)}return{suppressions:t,warnings:o}}var Dt=["echo","incomplete-migration","co-change-partner","twin-partner","coverage-contract","doc-reference","unused-params","new-dead","baseline"];function kt(e,i={}){let t=i.base??"HEAD",{minTogether:o=6,minConfidence:n=.6,maxEchoChecks:a=Number.POSITIVE_INFINITY,maxHelpers:s=Number.POSITIVE_INFINITY,minSimilarity:l=.8,includeBaseline:c=!1,scanLimit:r}=i,f=i.semantic!==!1,h=new Set(i.skip??[]),u=X(e,{base:t}),p=Q(e,{base:t,plan:u}),m=p.changedFiles,b=new Set(m),$=new Set(u.changedFileLines),d=new Set([...b,...$]),C=B(e.config.projectRoot,t,M(u,m)),S=Fi(e.config.projectRoot,t,u,C),g={base:t,changedFiles:m,changedSymbols:p.changedSymbols.length,checksRun:[],skipped:[],suppressed:[],findings:[],attributionNotes:p.attributionNotes,rootCauseGroups:[],note:p.summary.note};if(m.length===0)return g;let D=(x,Ee)=>{if(h.has(x)){g.skipped.push({check:x,reason:"skipped via --skip"});return}let je=g.findings.length,Ie=g.skipped.length,Ne=g.checksRun.length;H(`diff-gate.check.${x}`,Ee,()=>({check:x,changedFiles:m.length,changedSymbols:p.changedSymbols.length,findingsAdded:g.findings.length-je,skippedAdded:g.skipped.length-Ie,checksRunAdded:g.checksRun.length-Ne}))};return D("echo",()=>ni(e,p.changedSymbols,b,S,a,l,r,f,g)),D("incomplete-migration",()=>li(e,t,u,s,r,f,C,g)),D("co-change-partner",()=>di(e,d,o,n,g)),D("twin-partner",()=>fi(e,p.changedSymbols,b,r,g)),D("coverage-contract",()=>ui(e,d,g)),D("doc-reference",()=>yi(e,b,$,u.changedRanges,u.renamedFiles,g)),D("unused-params",()=>$i(e,m,g)),D("new-dead",()=>wi(e,p.changedSymbols,S,g)),c&&D("baseline",()=>Ti(e,g)),ii(g,[...e.config.suppressions??[],...ke(e.config.projectRoot).suppressions]),g.rootCauseGroups=ei(g.findings),g}function vt(e){return e.checksRun.length===0&&e.note===Z}function Ft(e){return e.filter(i=>!i.advisory)}function ei(e){let i=new Map;for(let t of e){let o=t.groupKey??`${t.check}:${t.id}`,n=i.get(o);n||(n={groupKey:o,check:t.check,severity:t.severity,actionTier:t.actionTier,sourceAnalyzer:t.sourceAnalyzer,rootCauseKey:t.rootCauseKey,count:0,findingIds:[],files:new Set,relatedFiles:new Set,message:t.message,remediation:t.remediation},i.set(o,n)),n.count+=1,n.findingIds.push(t.id),n.severity=xe(n.severity,t.severity),n.actionTier=Ge(n.actionTier,t.actionTier),t.file&&n.files.add(t.file);for(let a of t.relatedFiles??[])n.relatedFiles.add(a)}return[...i.values()].map(t=>({...t,files:[...t.files].sort(),relatedFiles:[...t.relatedFiles].sort()})).sort((t,o)=>o.count-t.count||j(o.severity)-j(t.severity)||t.groupKey.localeCompare(o.groupKey))}function xe(e,i){return j(i)>j(e)?i:e}function j(e){return e==="error"?2:e==="warning"?1:0}function Ge(e,i){return e?i&&ve(i)>ve(e)?i:e:i}function ve(e){return e==="direct"?2:e==="signal"?1:0}function ii(e,i){if(i.length===0||e.findings.length===0)return;let t=[];for(let o of e.findings){let n=i.find(a=>ti(a,o));n?e.suppressed.push({finding:o,suppression:n}):t.push(o)}e.findings=t}function ti(e,i){return!e.reason||e.reason.trim()===""||e.expiresAt&&Date.parse(e.expiresAt)<=Date.now()||e.id&&e.id!==i.id&&!i.legacySuppressionIds?.includes(e.id)||e.check&&e.check!==i.check||e.file&&e.file!==i.file?!1:!!(e.id||e.check)}function k(e,i){e.findings.push({...i,suppressionHint:`scip-query: ignore ${i.check} ${i.id} -- <reason>`})}function ni(e,i,t,o,n,a,s,l,c){c.checksRun.push("echo");for(let r of i.slice(0,n)){if(!I(r.symbol)||o(r))continue;let f=ne(e,r.symbol,{maxLoc:15,scanLimit:s}).filter(d=>!t.has(d.file)).slice(0,5).map(d=>({otherFile:d.file,otherShort:d.shortName,otherSymbol:d.symbol,similarity:1,similarityBasis:"exact-body",sharedEvidence:[`exact normalized body (${d.loc} LOC)`]}));if(f.length>0){let d=y("echo",r.symbol,r.file),C=y("echo",r.symbol,r.file,...f.map(g=>`${g.otherSymbol}|${g.otherFile}`).sort()),S=f[0];k(c,{id:d,legacySuppressionIds:C===d?void 0:[C],groupKey:d,actionTier:"direct",check:"echo",severity:"warning",evidence:"heuristic",confidence:1,file:r.file,symbol:r.symbol,relatedFiles:[...new Set(f.map(g=>g.otherFile))].sort(),sourceAnalyzer:"duplicate-bodies",rootCauseKey:`${r.symbol}:${S.otherSymbol}`,message:Re(r,f),why:Te(r,f),remediation:Ae("direct",r.shortName,S.otherShort)});continue}let h=ge(e,r.symbol,{minSimilarity:a,limit:5,scanLimit:s,semantic:l,sourceCandidateMode:"target-pruned"}),u=[];for(let d of h){let C=d.fileA===r.file?d.fileB:d.fileA,S=d.fileA===r.file?d.shortNameB:d.shortNameA,g=d.fileA===r.file?d.symbolB:d.symbolA;t.has(C)||u.push({otherFile:C,otherShort:S,otherSymbol:g,similarity:d.similarity,similarityBasis:d.similarityBasis??"callees",sharedEvidence:d.sharedCallees})}if(u.length===0)continue;let p=u[0],m=y("echo",r.symbol,r.file,...u.map(d=>`${d.otherSymbol}|${d.otherFile}`).sort()),b=y("echo",r.symbol,r.file),$=oi(r,u);k(c,{id:b,legacySuppressionIds:m===b?void 0:[m],groupKey:b,actionTier:$,check:"echo",severity:"warning",evidence:"heuristic",confidence:p.similarity,file:r.file,symbol:r.symbol,relatedFiles:[...new Set(u.map(d=>d.otherFile))].sort(),message:Re(r,u),why:Te(r,u),remediation:Ae($,r.shortName,p.otherShort)})}i.length>n&&c.skipped.push({check:"echo",reason:`echo check capped at ${n} of ${i.length} changed symbols`})}function oi(e,i){return i.some(o=>ri(e.symbol,o))?"direct":"signal"}function ri(e,i){if(i.similarityBasis==="exact-body")return!0;if(i.similarityBasis!=="source-tokens"||i.similarity<.98||i.sharedEvidence.length>8)return!1;let t=F(e),o=F(i.otherSymbol);return!t||!o?!1:t===o?!0:ci(i.sharedEvidence)?!1:si(t,o)}function si(e,i){let t=Fe(e),o=Fe(i);if(t.length===0||o.length===0)return!1;let n=new Set(o),a=t.filter(s=>n.has(s)).length;return a>=2&&a/Math.min(t.length,o.length)>=.5}function Fe(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1 $2").split(/[^A-Za-z0-9]+/).map(i=>i.toLowerCase()).filter(i=>i.length>=2)}var ai=new Set(["bytes","crypto","hex","num","random","secret","to","token"]);function ci(e){return e.length>=3&&e.every(i=>ai.has(i.toLowerCase()))}function Re(e,i){let t=i[0],o=Math.round(t.similarity*100);return i.length===1?`${e.shortName} (${e.file}) is ${o}% similar to established ${t.otherShort} (${t.otherFile})`:`${e.shortName} (${e.file}) resembles ${i.length} established symbol(s); strongest is ${o}% similar to ${t.otherShort} (${t.otherFile})`}function Te(e,i){let t=[`${e.shortName} was changed in this diff.`];for(let o of i.slice(0,3))t.push(`${o.otherShort} is outside this diff and scored ${Math.round(o.similarity*100)}% similar by ${o.similarityBasis} evidence.`),t.push(`Shared evidence: ${o.sharedEvidence.slice(0,5).join(", ")||"(none listed)"}.`);return i.length>3&&t.push(`${i.length-3} additional similar match(es) grouped into this finding.`),t}function Ae(e,i,t){return e==="direct"?`Extract or reuse the shared behavior with ${t}, or keep the duplicate with a suppression reason.`:`Review whether ${i} is intentionally parallel to the established implementation(s); extract shared behavior only if the product semantics match.`}function li(e,i,t,o,n,a,s,l){let c=Ce(e,{base:i,maxHelpers:o,diffPlan:t,scanLimit:n,semantic:a,baseContentAt:s});if(!c.available){l.skipped.push({check:"incomplete-migration",reason:"no git history"});return}l.checksRun.push("incomplete-migration"),c.note&&l.skipped.push({check:"incomplete-migration",reason:c.note});for(let r of c.findings){let f=r.leftovers.map(d=>`${d.shortName} (${d.file}, ${Math.round(d.containment*100)}% helper / ${Math.round(d.siteCoverage*100)}% site, ${d.migrationScope})`).join(", "),h=r.leftovers.map(d=>`${d.shortName}: ${d.migrationScope} (${d.migrationScopeReasons[0]})`).join("; "),u=[...r.migratedFiles,...r.leftovers.map(d=>d.file)],p=r.leftovers.length===0?void 0:Math.max(...r.leftovers.map(d=>Math.min(d.containment,d.siteCoverage))),m=r.leftovers.some(d=>d.migrationScope==="possible-subtype"),b=y("incomplete-migration",r.helperSymbol,r.helperFile,u.join("|")),$=y("incomplete-migration",r.helperSymbol,r.helperFile);k(l,{id:$,legacySuppressionIds:b===$?void 0:[b],check:"incomplete-migration",severity:"warning",evidence:"heuristic",confidence:p,file:r.helperFile,symbol:r.helperSymbol,relatedFiles:u,message:`new helper ${r.helperShortName} (${r.helperFile}) is wired into ${r.migratedFiles.length} file(s), but ${r.leftovers.length} similar un-migrated site(s) remain: ${f}`,why:[`${r.helperShortName} is new in this diff and already referenced by ${r.migratedFiles.join(", ")}.`,`Helper shape: ${r.helperShape}; ${r.specificHelperCalleeCount}/${r.helperCalleeCount} helper callees are specific enough to score.`,`Unchanged site(s) still contain the helper's callee pattern: ${f}.`,`Migration scope hints: ${h}.`],remediation:m?`Migrate same-scope sites to ${r.helperShortName}; review possible subtype/variant sites before applying the helper.`:`Migrate the remaining sites to ${r.helperShortName}, or confirm they are intentionally different.`})}}function di(e,i,t,o,n){let a=W(e).directionalCoChangePairsForFiles(i,{minTogether:t,minConfidence:0,maxFilesPerCommit:20});if(!a){n.skipped.push({check:"co-change-partner",reason:"no git history"});return}n.checksRun.push("co-change-partner");let s=new Set,l;for(let c of a){let r=i.has(c.fileA),f=i.has(c.fileB);if(r===f)continue;let h=r?c.fileA:c.fileB,u=r?c.fileB:c.fileA,p=r?c.changesA:c.changesB,m=p>0?c.together/p:0;if(m<o||N(u)||N(h)||!P(`${e.config.projectRoot}/${u}`))continue;let b=`${h}|${u}`;if(s.has(b))continue;s.add(b);let $=y("co-change-partner",h,u,String(c.together)),d=y("co-change-partner",h,u),C=V(h,u),S=J({fileA:h,fileB:u,together:c.together,confidence:m,commitScope:c.commitScope,recency:c.recency,subjectContext:c.subjectContext},C,(l??=Y(e))(h,u)),g=[h,u].sort().join("|");n.findings.push({id:d,legacySuppressionIds:$===d?void 0:[$],check:"co-change-partner",severity:"warning",evidence:"change-graph",actionTier:"signal",groupKey:`co-change-partner:${g}`,confidence:m,file:h,relatedFiles:[u],sourceAnalyzer:"co-change",rootCauseKey:g,partnerClass:C.partnerClass,partnerClassReasons:C.reasons,commitScope:c.commitScope,recency:c.recency,focusedTogether:c.focusedTogether,broadTogether:c.broadTogether,broadCommitRatio:c.broadCommitRatio,lastTogetherAt:c.lastTogetherAt,recentTogether:c.recentTogether,subjectContext:c.subjectContext,...S?{declaredCouplingSuggestion:S}:{},message:`${h} changed, but ${u} did not \u2014 they change together ${c.together}x (${Math.round(m*100)}% of the time)`,why:[`${h} is in this diff and ${u} is not.`,`Git history shows ${c.together} co-change(s), which is ${Math.round(m*100)}% of changes to the edited side.`,`History context: ${hi(c)}.`,`Subject context: ${gi(c.subjectContext)}.`,...mi(c),`Partner class: ${C.partnerClass} (${C.reasons.join("; ")}).`],remediation:S?`Update ${u} alongside this change, declare the coupling "${S.name}", or confirm the coupling no longer holds.`:`Update ${u} alongside this change, or confirm the coupling no longer holds.`})}}function fi(e,i,t,o,n){n.checksRun.push("twin-partner");let a=oe(e,{scanLimit:o}).filter(c=>c.relationship==="divergent"||c.relationship==="identical");if(a.length===0)return;let s=new Map;for(let c of a)for(let r of c.members)s.set(r.symbol,c);let l=new Set;for(let c of i){if(!I(c.symbol))continue;let r=s.get(c.symbol);if(!r)continue;let f=r.members.filter(m=>m.symbol!==c.symbol&&!t.has(m.file));if(f.length===0)continue;let h=[c.symbol,...f.map(m=>m.symbol)].sort().join("|");if(l.has(h))continue;l.add(h);let u=f[0],p=y("twin-partner",c.symbol,c.file,u.symbol);k(n,{id:p,groupKey:`twin-partner:${h}`,check:"twin-partner",severity:"warning",evidence:"heuristic",actionTier:"signal",advisory:!0,confidence:r.relationship==="identical"?1:Math.max(0,1-r.maxDivergence),file:c.file,symbol:c.symbol,relatedFiles:f.map(m=>m.file).sort(),sourceAnalyzer:"twin-drift",rootCauseKey:h,message:`${c.shortName} (${c.file}) changed but its same-name twin ${u.shortName} (${u.file}) did not \u2014 verify the change shouldn't apply to both, or consolidate them.`,why:[`${c.symbol} is in this diff.`,`${u.symbol} shares its (near-)name and is classified '${r.relationship}' with it, but ${u.file} is not in this diff.`,r.relationship==="divergent"?`The two bodies already diverge (~${Math.round(r.maxDivergence*100)}% token difference) \u2014 this diff may widen or narrow that drift.`:"The two bodies were byte-identical before this diff.","Advisory: twin-partner never fails the gate by itself \u2014 see the twin-partner entry in the diff-gate checks table."],remediation:`Update ${u.file} alongside this change, consolidate ${c.shortName}/${u.shortName} into one helper, or confirm the twin should diverge on purpose.`})}}function ui(e,i,t){t.checksRun.push("coverage-contract");let o=e.config.coverageContracts??[];for(let n of o){if(!se(n,i))continue;let a=re(e,n);if(a.status==="ok")continue;let s=y("coverage-contract",n.name,n.file);k(t,{id:s,groupKey:`coverage-contract:${n.name}`,check:"coverage-contract",severity:a.status==="unavailable"?"warning":"error",evidence:"graph-fact",actionTier:"direct",file:n.file,sourceAnalyzer:"coverage-contracts",rootCauseKey:n.name,message:ae(a),why:[`Contract "${n.name}" declares that ${n.file} (${n.keys.type}) must track ${n.mustEqual.type}.`,a.status==="unavailable"?`The declared side could not be evaluated: ${a.unavailableReason}.`:`Missing: ${a.missing.join(", ")||"none"}. Extra: ${a.extra.join(", ")||"none"}.`,"Either side of this contract changed in this diff."],remediation:a.status==="unavailable"?"Investigate why the declared key set could not be parsed (see the reason above) \u2014 do not treat this as a pass.":`Update ${n.file} so its ${n.keys.type==="object-literal-keys"||n.keys.type==="string-array"?n.keys.identifier:n.keys.marker} matches ${n.mustEqual.type}, or fix the ground-truth source if the declared set is right.`})}}function hi(e){return`${e.commitScope}/${e.recency}; ${e.focusedTogether} focused, ${e.broadTogether} broad-sweep (${Math.round(e.broadCommitRatio*100)}% broad), ${e.recentTogether} recent, last ${pi(e.lastTogetherAt)}`}function gi(e){let i=e.subjectLabels.length>0?e.subjectLabels.slice(0,5).join(", "):"none inferred",t=e.issueRefs.length>0?e.issueRefs.slice(0,5).join(", "):"none",o=e.sampleSubjects.length>0?e.sampleSubjects.slice(0,3).map(n=>`"${n}"`).join("; "):"none";return`labels ${i}; refs ${t}; samples ${o}; external issue/PR labels ${e.externalIssueLabelStatus}`}function mi(e){let i=[];return e.commitScope==="broad-sweep"&&i.push("Broad-sweep history is weaker than focused co-edits; confirm this is a real coordination contract."),e.recency==="stale"&&i.push("The pair has no co-change in the recent history window; confirm the coupling still holds."),i}function pi(e){return e<=0?"unknown":new Date(e*1e3).toISOString().slice(0,10)}var L=3;function yi(e,i,t,o,n,a){a.checksRun.push("doc-reference");let s=v.referenceTargets(e,i,o);if(s.size===0)return;let l=new Set(n.map(r=>r.from)),c=[];for(let r of ie(e,s))t.has(r.doc)||ee(e,r.doc)||c.push(bi(e,r,l));for(let r of Ci(c))k(a,r)}function bi(e,i,t){let o=i.citedClaims.length>0?i.citedClaims:v.citationContexts(e,i.doc,i.cited),n=v.classifyCitation(o),a=v.hasLineAnchoredCitation(i.citations),s=i.cited.some(h=>t.has(h)||!P(`${e.config.projectRoot}/${h}`)),l=!(a||s),c=i.cited[0]??"",r=y("doc-reference",i.doc,i.cited.join("|")),f=y("doc-reference",i.doc,c);return{id:f,legacySuppressionIds:r===f?void 0:[r],check:"doc-reference",severity:"warning",evidence:"change-graph",actionTier:n.actionTier,groupKey:`doc-reference:${c}`,rootCauseKey:c,confidence:1,file:i.doc,relatedFiles:i.cited,citationKind:n.citationKind,citationKindReasons:n.reasons,citedClaims:o,...l?{advisory:!0}:{},message:`${i.doc} cites ${i.cited.join(", ")} as ${v.citationKindLabel(n.citationKind)} \u2014 changed in this diff, doc untouched`,why:[`${i.cited.join(", ")} changed in this diff.`,`${i.doc} cites the changed file(s) but was not updated in the same diff.`,...n.reasons.map(h=>`Citation kind evidence: ${h}`),l?"Advisory: bare file-mention citation (no line anchor, cited file still present under the same path) \u2014 21.2 calibration found this citation shape low-precision; verify at your leisure.":a?"Blocking: citation includes a line anchor that may now point at the wrong lines.":"Blocking: the cited file was deleted or renamed in this diff \u2014 the citation almost certainly needs an update."],remediation:v.citationRemediation(n.citationKind,i.doc)}}function Ci(e){let i=new Map;for(let o of e){let n=o.rootCauseKey??"",a=i.get(n);a?a.push(o):i.set(n,[o])}let t=[];for(let[o,n]of i)n.length<=L?t.push(...n):t.push(Si(o,n));return t}function Si(e,i){let t=[...i].sort((f,h)=>(f.file??"").localeCompare(h.file??"")),o=t.slice(0,L).map(f=>({doc:f.file??"",citedClaims:f.citedClaims??[]})),n=t.length-o.length,a=t.every(f=>f.advisory===!0),s=t.reduce((f,h)=>xe(f,h.severity),"info"),l=t.reduce((f,h)=>Ge(f,h.actionTier),void 0),c=t.map(f=>f.file??"").filter(f=>f.length>0),r=[...new Set(t.flatMap(f=>[f.id,...f.legacySuppressionIds??[]]))];return{id:y("doc-reference","hub-cluster",e),memberFindingIds:r,check:"doc-reference",severity:s,evidence:"change-graph",actionTier:l,groupKey:`doc-reference:${e}`,rootCauseKey:e,confidence:1,file:o[0]?.doc,relatedFiles:[e],citationCount:t.length,citationExemplars:o,suppressedCount:n,...a?{advisory:!0}:{},message:`${e} is cited by ${t.length} doc(s) changed in this diff, doc(s) untouched \u2014 showing ${o.length} example(s), ${n} more damped`,why:[`${e} changed in this diff.`,`${t.length} doc(s) cite it but were not updated in the same diff: ${c.join(", ")}.`,...o.map(f=>`Example \u2014 ${f.doc}: ${f.citedClaims[0]??"(no captured citation text)"}`),`${n} additional doc(s) also cite this file, past the per-hub-file cascade cap (${L}) \u2014 not silently dropped: counted in citationCount/suppressedCount and individually addressable via memberFindingIds.`,a?"Advisory: every citation in this cluster is a bare file-mention (no line anchor, cited file still present).":"Blocking: at least one citation in this cluster is line-anchored or cites a deleted/renamed file."],remediation:`Review the ${o.length} example doc(s) above; ${t.length} total doc(s) cite ${e} the same way \u2014 see citationExemplars and memberFindingIds in --json for the complete list.`}}function $i(e,i,t){t.checksRun.push("unused-params");for(let o of Se(e,{files:i,limit:50})){let n=y("unused-params",o.symbol,o.file,o.unusedTrailing.join("|")),a=y("unused-params",o.symbol,o.file);k(t,{id:a,legacySuppressionIds:n===a?void 0:[n],check:"unused-params",severity:"warning",evidence:"heuristic",confidence:.85,file:o.file,startLine:o.startLine,endLine:o.endLine,symbol:o.symbol,message:`${o.shortName} (${o.file}) has trailing unused parameter(s): ${o.unusedTrailing.join(", ")}`,why:[`${o.shortName} is in a changed file.`,`The trailing parameter(s) ${o.unusedTrailing.join(", ")} are not referenced in the function body.`],remediation:"Drop the unused trailing parameters and their call-site arguments."})}}function wi(e,i,t,o){o.checksRun.push("new-dead");let n=new q(e),a=_(e.config.projectRoot),s=a.length>1;for(let l of i){if(t(l)||l.fanIn>0||n.fileKind(l.file)==="test"||R(e,l.file)||T(e,l.symbol,l.file)||vi(l.symbol))continue;let c=y("new-dead",l.symbol,l.file),r=s&&Di(a,l.file)&&ki(e,l.symbol);k(o,{id:c,check:"new-dead",severity:"warning",evidence:r?"heuristic":"graph-fact",confidence:r?.4:.9,file:l.file,symbol:l.symbol,message:r?`${l.shortName} (${l.file}) unconfirmed (cross-package ambiguous-name resolution gap): zero indexed consumers found, but this name is ambiguous project-wide and lives in a workspace package \u2014 verify manually before treating as dead`:`${l.shortName} (${l.file}) was changed but has zero indexed consumers`,why:r?[`${l.shortName} is a changed production symbol in a workspace package.`,"The index, semantic, and source-fallback layers all report zero consumers for this symbol.","Its leaf name is ambiguous project-wide (a same-named definition exists elsewhere), and cross-package consumers reached only through a re-exporting barrel are a known unresolvable shape for this tool \u2014 see docs/plans/2026-07-02-followups.md item 2."]:[`${l.shortName} is a changed production symbol.`,"The index reports zero consumers for this symbol.","The symbol is not in a detected entry surface or configured live root."],remediation:r?"Verify with a direct code search before deleting; this tool cannot confidently resolve this symbol.":"Wire it up, or remove it before it becomes permanent dead code."})}}function Di(e,i){return e.some(t=>i===t.relativeDir||i.startsWith(`${t.relativeDir}/`))}function ki(e,i){let t=F(i);return t?(U(e).get(t)?.length??0)>1:!1}function vi(e){if(O(e)!=="type")return!1;let i=F(e);return i.startsWith("_Assert")||i.startsWith("Assert")}function Fi(e,i,t,o){let n=new Map(t.renamedFiles.map(s=>[s.to,s.from])),a=o??B(e,i,M(t));return s=>{let l=n.get(s.file)??s.file,c=F(s.symbol);if(!c)return!1;let r=a(l);return r!==null&&Ri(r,c)}}function Ri(e,i){return new RegExp(`\\b${z(i)}\\b`).test(e)}function Ti(e,i){if(!P(E(e))){i.skipped.push({check:"baseline",reason:"no .scipquery-baseline.json \u2014 run health --write-baseline to enable"});return}i.checksRun.push("baseline");let t=De(e);for(let o of t.newFindings){let n=$e(o),a=y("baseline",o);k(i,{id:a,groupKey:`baseline:${n.sourceAnalyzer}:${n.rootCauseKey}`,check:"baseline",severity:"error",evidence:"baseline",actionTier:n.actionTier,confidence:1,file:n.file,relatedFiles:n.relatedFiles.length>0?n.relatedFiles:void 0,sourceAnalyzer:n.sourceAnalyzer,rootCauseKey:n.rootCauseKey,message:`new ${n.label} vs committed baseline: ${o}`,why:["A committed health baseline exists.","The current health result contains a finding not present in that baseline.",`Underlying analyzer: ${n.sourceAnalyzer}.`,`Inherited action tier: ${n.actionTier}.`,`Root cause key: ${n.rootCauseKey}.`,...n.why],remediation:n.remediation})}}function y(e,...i){return`SQ${Xe("sha256").update([e,...i].join("\0")).digest("hex").slice(0,12).toUpperCase()}`}export{we as a,Ui as b,De as c,Dt as d,kt as e,vt as f,Ft as g,Fi as h};
3
+ //# sourceMappingURL=chunk-YBGC7HTY.js.map