scip-query 0.1.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.
- package/IMPROVEMENTS.md +143 -0
- package/PLAN.md +320 -0
- package/README.md +1213 -0
- package/dist/chunk-2QZ23IBN.js +55 -0
- package/dist/chunk-2QZ23IBN.js.map +1 -0
- package/dist/chunk-36OMT7ZJ.js +144 -0
- package/dist/chunk-36OMT7ZJ.js.map +1 -0
- package/dist/chunk-3E2X7RIE.js +101 -0
- package/dist/chunk-3E2X7RIE.js.map +1 -0
- package/dist/chunk-3UOUTZQT.js +45 -0
- package/dist/chunk-3UOUTZQT.js.map +1 -0
- package/dist/chunk-3ZZJVBIO.js +88 -0
- package/dist/chunk-3ZZJVBIO.js.map +1 -0
- package/dist/chunk-4TYLS5XX.js +10 -0
- package/dist/chunk-4TYLS5XX.js.map +1 -0
- package/dist/chunk-5FGUEU7N.js +101 -0
- package/dist/chunk-5FGUEU7N.js.map +1 -0
- package/dist/chunk-5WTJAXY2.js +61 -0
- package/dist/chunk-5WTJAXY2.js.map +1 -0
- package/dist/chunk-6NBLIDF4.js +24 -0
- package/dist/chunk-6NBLIDF4.js.map +1 -0
- package/dist/chunk-6SXADWLW.js +43 -0
- package/dist/chunk-6SXADWLW.js.map +1 -0
- package/dist/chunk-6VJ6Q7IE.js +65 -0
- package/dist/chunk-6VJ6Q7IE.js.map +1 -0
- package/dist/chunk-7OZPA5OO.js +258 -0
- package/dist/chunk-7OZPA5OO.js.map +1 -0
- package/dist/chunk-BEPIEVLR.js +76 -0
- package/dist/chunk-BEPIEVLR.js.map +1 -0
- package/dist/chunk-BFSCMC22.js +42 -0
- package/dist/chunk-BFSCMC22.js.map +1 -0
- package/dist/chunk-BP2ATLK2.js +110 -0
- package/dist/chunk-BP2ATLK2.js.map +1 -0
- package/dist/chunk-CM454WL3.js +114 -0
- package/dist/chunk-CM454WL3.js.map +1 -0
- package/dist/chunk-DCKMSTJ4.js +74 -0
- package/dist/chunk-DCKMSTJ4.js.map +1 -0
- package/dist/chunk-DEZKCZXD.js +40 -0
- package/dist/chunk-DEZKCZXD.js.map +1 -0
- package/dist/chunk-DVWGWHFW.js +99 -0
- package/dist/chunk-DVWGWHFW.js.map +1 -0
- package/dist/chunk-EMDQWNYR.js +102 -0
- package/dist/chunk-EMDQWNYR.js.map +1 -0
- package/dist/chunk-FFSWWE5O.js +33 -0
- package/dist/chunk-FFSWWE5O.js.map +1 -0
- package/dist/chunk-FGXRVW7G.js +73 -0
- package/dist/chunk-FGXRVW7G.js.map +1 -0
- package/dist/chunk-FUHJCHS4.js +158 -0
- package/dist/chunk-FUHJCHS4.js.map +1 -0
- package/dist/chunk-GJFURBEW.js +64 -0
- package/dist/chunk-GJFURBEW.js.map +1 -0
- package/dist/chunk-GTILYBH6.js +102 -0
- package/dist/chunk-GTILYBH6.js.map +1 -0
- package/dist/chunk-JJP7KQND.js +1 -0
- package/dist/chunk-JJP7KQND.js.map +1 -0
- package/dist/chunk-JKP5GH6T.js +213 -0
- package/dist/chunk-JKP5GH6T.js.map +1 -0
- package/dist/chunk-KCBMVQL5.js +38 -0
- package/dist/chunk-KCBMVQL5.js.map +1 -0
- package/dist/chunk-KVSW5KYP.js +78 -0
- package/dist/chunk-KVSW5KYP.js.map +1 -0
- package/dist/chunk-LAWMH22O.js +172 -0
- package/dist/chunk-LAWMH22O.js.map +1 -0
- package/dist/chunk-LB7OS35Q.js +72 -0
- package/dist/chunk-LB7OS35Q.js.map +1 -0
- package/dist/chunk-LUSIFBXO.js +57 -0
- package/dist/chunk-LUSIFBXO.js.map +1 -0
- package/dist/chunk-MBVNHJVN.js +44 -0
- package/dist/chunk-MBVNHJVN.js.map +1 -0
- package/dist/chunk-MGNMHKX3.js +15 -0
- package/dist/chunk-MGNMHKX3.js.map +1 -0
- package/dist/chunk-N5KEREIA.js +41 -0
- package/dist/chunk-N5KEREIA.js.map +1 -0
- package/dist/chunk-NDSQYIWT.js +71 -0
- package/dist/chunk-NDSQYIWT.js.map +1 -0
- package/dist/chunk-NUZ4OMU3.js +28 -0
- package/dist/chunk-NUZ4OMU3.js.map +1 -0
- package/dist/chunk-QOV2R2WT.js +170 -0
- package/dist/chunk-QOV2R2WT.js.map +1 -0
- package/dist/chunk-SEFSL2GF.js +78 -0
- package/dist/chunk-SEFSL2GF.js.map +1 -0
- package/dist/chunk-T6ARFSBZ.js +103 -0
- package/dist/chunk-T6ARFSBZ.js.map +1 -0
- package/dist/chunk-TBP6BICL.js +46 -0
- package/dist/chunk-TBP6BICL.js.map +1 -0
- package/dist/chunk-TDNNOR6D.js +97 -0
- package/dist/chunk-TDNNOR6D.js.map +1 -0
- package/dist/chunk-TSPZOMHC.js +195 -0
- package/dist/chunk-TSPZOMHC.js.map +1 -0
- package/dist/chunk-UNTPVD36.js +55 -0
- package/dist/chunk-UNTPVD36.js.map +1 -0
- package/dist/chunk-VRUJH4BO.js +88 -0
- package/dist/chunk-VRUJH4BO.js.map +1 -0
- package/dist/chunk-VZ7AMAFL.js +76 -0
- package/dist/chunk-VZ7AMAFL.js.map +1 -0
- package/dist/chunk-XFXDXEUN.js +24 -0
- package/dist/chunk-XFXDXEUN.js.map +1 -0
- package/dist/chunk-YZAA4LYG.js +169 -0
- package/dist/chunk-YZAA4LYG.js.map +1 -0
- package/dist/chunk-Z73NYSBZ.js +92 -0
- package/dist/chunk-Z73NYSBZ.js.map +1 -0
- package/dist/chunk-ZJRYBOEE.js +125 -0
- package/dist/chunk-ZJRYBOEE.js.map +1 -0
- package/dist/cli.js +5798 -0
- package/dist/cli.js.map +1 -0
- package/dist/db-BxaevAyc.d.ts +683 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +1271 -0
- package/dist/index.js.map +1 -0
- package/dist/postinstall.js +167 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/queries/affected.d.ts +14 -0
- package/dist/queries/affected.js +9 -0
- package/dist/queries/affected.js.map +1 -0
- package/dist/queries/bottlenecks.d.ts +18 -0
- package/dist/queries/bottlenecks.js +8 -0
- package/dist/queries/bottlenecks.js.map +1 -0
- package/dist/queries/by-kind.d.ts +20 -0
- package/dist/queries/by-kind.js +10 -0
- package/dist/queries/by-kind.js.map +1 -0
- package/dist/queries/call-graph.d.ts +13 -0
- package/dist/queries/call-graph.js +9 -0
- package/dist/queries/call-graph.js.map +1 -0
- package/dist/queries/change-surface.d.ts +10 -0
- package/dist/queries/change-surface.js +9 -0
- package/dist/queries/change-surface.js.map +1 -0
- package/dist/queries/clean-signature.d.ts +9 -0
- package/dist/queries/clean-signature.js +7 -0
- package/dist/queries/clean-signature.js.map +1 -0
- package/dist/queries/code.d.ts +17 -0
- package/dist/queries/code.js +9 -0
- package/dist/queries/code.js.map +1 -0
- package/dist/queries/complexity-hotspots.d.ts +19 -0
- package/dist/queries/complexity-hotspots.js +9 -0
- package/dist/queries/complexity-hotspots.js.map +1 -0
- package/dist/queries/complexity.d.ts +13 -0
- package/dist/queries/complexity.js +9 -0
- package/dist/queries/complexity.js.map +1 -0
- package/dist/queries/convergence.d.ts +11 -0
- package/dist/queries/convergence.js +9 -0
- package/dist/queries/convergence.js.map +1 -0
- package/dist/queries/coupling.d.ts +17 -0
- package/dist/queries/coupling.js +9 -0
- package/dist/queries/coupling.js.map +1 -0
- package/dist/queries/cycles.d.ts +16 -0
- package/dist/queries/cycles.js +8 -0
- package/dist/queries/cycles.js.map +1 -0
- package/dist/queries/dataflow.d.ts +19 -0
- package/dist/queries/dataflow.js +9 -0
- package/dist/queries/dataflow.js.map +1 -0
- package/dist/queries/dead.d.ts +10 -0
- package/dist/queries/dead.js +9 -0
- package/dist/queries/dead.js.map +1 -0
- package/dist/queries/deep-chains.d.ts +16 -0
- package/dist/queries/deep-chains.js +8 -0
- package/dist/queries/deep-chains.js.map +1 -0
- package/dist/queries/deps.d.ts +9 -0
- package/dist/queries/deps.js +9 -0
- package/dist/queries/deps.js.map +1 -0
- package/dist/queries/diff-impact.d.ts +13 -0
- package/dist/queries/diff-impact.js +9 -0
- package/dist/queries/diff-impact.js.map +1 -0
- package/dist/queries/doc-coverage.d.ts +14 -0
- package/dist/queries/doc-coverage.js +8 -0
- package/dist/queries/doc-coverage.js.map +1 -0
- package/dist/queries/drift.d.ts +25 -0
- package/dist/queries/drift.js +8 -0
- package/dist/queries/drift.js.map +1 -0
- package/dist/queries/extract-candidates.d.ts +25 -0
- package/dist/queries/extract-candidates.js +9 -0
- package/dist/queries/extract-candidates.js.map +1 -0
- package/dist/queries/fan.d.ts +29 -0
- package/dist/queries/fan.js +14 -0
- package/dist/queries/fan.js.map +1 -0
- package/dist/queries/files.d.ts +6 -0
- package/dist/queries/files.js +7 -0
- package/dist/queries/files.js.map +1 -0
- package/dist/queries/health.d.ts +18 -0
- package/dist/queries/health.js +21 -0
- package/dist/queries/health.js.map +1 -0
- package/dist/queries/hierarchy.d.ts +13 -0
- package/dist/queries/hierarchy.js +8 -0
- package/dist/queries/hierarchy.js.map +1 -0
- package/dist/queries/hotspots.d.ts +13 -0
- package/dist/queries/hotspots.js +8 -0
- package/dist/queries/hotspots.js.map +1 -0
- package/dist/queries/imports.d.ts +19 -0
- package/dist/queries/imports.js +12 -0
- package/dist/queries/imports.js.map +1 -0
- package/dist/queries/index.d.ts +47 -0
- package/dist/queries/index.js +207 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/isolated.d.ts +14 -0
- package/dist/queries/isolated.js +9 -0
- package/dist/queries/isolated.js.map +1 -0
- package/dist/queries/members.d.ts +10 -0
- package/dist/queries/members.js +8 -0
- package/dist/queries/members.js.map +1 -0
- package/dist/queries/methods.d.ts +6 -0
- package/dist/queries/methods.js +8 -0
- package/dist/queries/methods.js.map +1 -0
- package/dist/queries/outline.d.ts +10 -0
- package/dist/queries/outline.js +8 -0
- package/dist/queries/outline.js.map +1 -0
- package/dist/queries/passthrough-candidates.d.ts +18 -0
- package/dist/queries/passthrough-candidates.js +9 -0
- package/dist/queries/passthrough-candidates.js.map +1 -0
- package/dist/queries/redundant-reexports.d.ts +22 -0
- package/dist/queries/redundant-reexports.js +8 -0
- package/dist/queries/redundant-reexports.js.map +1 -0
- package/dist/queries/refs.d.ts +6 -0
- package/dist/queries/refs.js +7 -0
- package/dist/queries/refs.js.map +1 -0
- package/dist/queries/similar-chains.d.ts +29 -0
- package/dist/queries/similar-chains.js +8 -0
- package/dist/queries/similar-chains.js.map +1 -0
- package/dist/queries/similar-files.d.ts +19 -0
- package/dist/queries/similar-files.js +8 -0
- package/dist/queries/similar-files.js.map +1 -0
- package/dist/queries/similar-signatures.d.ts +21 -0
- package/dist/queries/similar-signatures.js +8 -0
- package/dist/queries/similar-signatures.js.map +1 -0
- package/dist/queries/similar.d.ts +34 -0
- package/dist/queries/similar.js +11 -0
- package/dist/queries/similar.js.map +1 -0
- package/dist/queries/slice.d.ts +21 -0
- package/dist/queries/slice.js +9 -0
- package/dist/queries/slice.js.map +1 -0
- package/dist/queries/stale-abstractions.d.ts +18 -0
- package/dist/queries/stale-abstractions.js +9 -0
- package/dist/queries/stale-abstractions.js.map +1 -0
- package/dist/queries/stats.d.ts +6 -0
- package/dist/queries/stats.js +7 -0
- package/dist/queries/stats.js.map +1 -0
- package/dist/queries/surface.d.ts +7 -0
- package/dist/queries/surface.js +8 -0
- package/dist/queries/surface.js.map +1 -0
- package/dist/queries/symbols.d.ts +6 -0
- package/dist/queries/symbols.js +9 -0
- package/dist/queries/symbols.js.map +1 -0
- package/dist/queries/system.d.ts +7 -0
- package/dist/queries/system.js +9 -0
- package/dist/queries/system.js.map +1 -0
- package/dist/queries/test-coverage.d.ts +22 -0
- package/dist/queries/test-coverage.js +11 -0
- package/dist/queries/test-coverage.js.map +1 -0
- package/dist/queries/trace.d.ts +6 -0
- package/dist/queries/trace.js +8 -0
- package/dist/queries/trace.js.map +1 -0
- package/dist/queries/wrapper-candidates.d.ts +17 -0
- package/dist/queries/wrapper-candidates.js +9 -0
- package/dist/queries/wrapper-candidates.js.map +1 -0
- package/dist/reindex-worker.js +368 -0
- package/dist/reindex-worker.js.map +1 -0
- package/docs/AGENT_GUIDE.md +359 -0
- package/package.json +70 -0
- package/reports/debloat/2026-04-10-scip-query-self-audit.md +161 -0
- package/skills/concrete-plan/SKILL.md +318 -0
- package/skills/scip-debloat/SKILL.md +413 -0
- package/skills/scip-explore/SKILL.md +235 -0
- package/skills/scip-verify/SKILL.md +323 -0
- package/src/cli.ts +1480 -0
- package/src/config.ts +117 -0
- package/src/db.ts +127 -0
- package/src/gitignore-filter.ts +143 -0
- package/src/index.ts +11 -0
- package/src/postinstall.ts +8 -0
- package/src/queries/affected.ts +86 -0
- package/src/queries/bottlenecks.ts +67 -0
- package/src/queries/by-kind.ts +204 -0
- package/src/queries/call-graph.ts +66 -0
- package/src/queries/change-surface.ts +110 -0
- package/src/queries/clean-signature.ts +22 -0
- package/src/queries/code.ts +101 -0
- package/src/queries/complexity-hotspots.ts +119 -0
- package/src/queries/complexity.ts +152 -0
- package/src/queries/convergence.ts +82 -0
- package/src/queries/coupling.ts +99 -0
- package/src/queries/cycles.ts +78 -0
- package/src/queries/dataflow.ts +128 -0
- package/src/queries/dead.ts +122 -0
- package/src/queries/deep-chains.ts +59 -0
- package/src/queries/deps.ts +46 -0
- package/src/queries/diff-impact.ts +204 -0
- package/src/queries/doc-coverage.ts +86 -0
- package/src/queries/drift.ts +224 -0
- package/src/queries/extract-candidates.ts +167 -0
- package/src/queries/fan.ts +148 -0
- package/src/queries/files.ts +16 -0
- package/src/queries/health.ts +324 -0
- package/src/queries/hierarchy.ts +49 -0
- package/src/queries/hotspots.ts +53 -0
- package/src/queries/imports.ts +95 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/isolated.ts +67 -0
- package/src/queries/members.ts +54 -0
- package/src/queries/methods.ts +27 -0
- package/src/queries/outline.ts +52 -0
- package/src/queries/passthrough-candidates.ts +94 -0
- package/src/queries/redundant-reexports.ts +170 -0
- package/src/queries/refs.ts +27 -0
- package/src/queries/similar-chains.ts +314 -0
- package/src/queries/similar-files.ts +140 -0
- package/src/queries/similar-signatures.ts +151 -0
- package/src/queries/similar.ts +305 -0
- package/src/queries/slice.ts +154 -0
- package/src/queries/stale-abstractions.ts +82 -0
- package/src/queries/stats.ts +22 -0
- package/src/queries/surface.ts +34 -0
- package/src/queries/symbols.ts +39 -0
- package/src/queries/system.ts +86 -0
- package/src/queries/test-coverage.ts +106 -0
- package/src/queries/trace.ts +55 -0
- package/src/queries/wrapper-candidates.ts +112 -0
- package/src/query-support.ts +226 -0
- package/src/reindex/detect.ts +58 -0
- package/src/reindex/index.ts +153 -0
- package/src/reindex/indexers.ts +220 -0
- package/src/reindex/install.ts +125 -0
- package/src/reindex-worker.ts +35 -0
- package/src/setup.ts +202 -0
- package/src/symbol-parser.ts +278 -0
- package/src/types.ts +654 -0
- package/src/watch.ts +274 -0
- package/tests/gitignore-filter.test.ts +48 -0
- package/tests/queries.test.ts +300 -0
- package/tests/symbol-parser.test.ts +157 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +40 -0
- package/vitest.config.ts +7 -0
package/src/setup.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync, mkdirSync, symlinkSync, readlinkSync,
|
|
3
|
+
unlinkSync, chmodSync, createWriteStream,
|
|
4
|
+
} from 'node:fs';
|
|
5
|
+
import { join, dirname, resolve } from 'node:path';
|
|
6
|
+
import { homedir, platform, arch } from 'node:os';
|
|
7
|
+
import { execFileSync } from 'node:child_process';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { get as httpsGet } from 'node:https';
|
|
10
|
+
import { tryInstallScipCli } from './reindex/install.js';
|
|
11
|
+
|
|
12
|
+
const IS_WINDOWS = platform() === 'win32';
|
|
13
|
+
const SKILLS = ['concrete-plan', 'scip-explore', 'scip-debloat', 'scip-verify'];
|
|
14
|
+
const SCIP_VERSION = 'v0.7.0';
|
|
15
|
+
|
|
16
|
+
// ── Skills Installation ────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export interface InstallSkillsResult {
|
|
19
|
+
installed: string[];
|
|
20
|
+
skipped: string[];
|
|
21
|
+
alreadyLinked: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Install scip-query skills into both Claude Code (~/.claude/skills/)
|
|
26
|
+
* and Codex (~/.codex/skills/). Uses symlinks (junctions on Windows)
|
|
27
|
+
* so skills auto-update when the package updates.
|
|
28
|
+
*/
|
|
29
|
+
export function installSkills(
|
|
30
|
+
opts: { quiet?: boolean } = {},
|
|
31
|
+
): InstallSkillsResult {
|
|
32
|
+
const log = opts.quiet ? () => {} : console.log;
|
|
33
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
34
|
+
const skillsSource = resolve(dirname(thisFile), '..', 'skills');
|
|
35
|
+
|
|
36
|
+
const targets = [
|
|
37
|
+
join(homedir(), '.claude', 'skills'),
|
|
38
|
+
join(homedir(), '.codex', 'skills'),
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const result: InstallSkillsResult = {
|
|
42
|
+
installed: [],
|
|
43
|
+
skipped: [],
|
|
44
|
+
alreadyLinked: [],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
for (const targetDir of targets) {
|
|
48
|
+
// Only install if the parent directory exists (tool is installed)
|
|
49
|
+
const parentDir = dirname(targetDir);
|
|
50
|
+
if (!existsSync(parentDir)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
mkdirSync(targetDir, { recursive: true });
|
|
55
|
+
const toolName = targetDir.includes('.codex') ? 'Codex' : 'Claude';
|
|
56
|
+
|
|
57
|
+
for (const skill of SKILLS) {
|
|
58
|
+
const source = join(skillsSource, skill);
|
|
59
|
+
const target = join(targetDir, skill);
|
|
60
|
+
|
|
61
|
+
if (!existsSync(source)) {
|
|
62
|
+
result.skipped.push(`${toolName}/${skill}`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (existsSync(target)) {
|
|
67
|
+
try {
|
|
68
|
+
const existing = readlinkSync(target);
|
|
69
|
+
if (resolve(existing) === resolve(source)) {
|
|
70
|
+
result.alreadyLinked.push(`${toolName}/${skill}`);
|
|
71
|
+
log(` ok: ${skill} → ${toolName} (already linked)`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Not a symlink — don't overwrite user's custom skill
|
|
76
|
+
result.skipped.push(`${toolName}/${skill}`);
|
|
77
|
+
log(` skip: ${skill} → ${toolName} (exists, not a symlink)`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
unlinkSync(target);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Use 'junction' on Windows (doesn't need admin), 'dir' elsewhere
|
|
84
|
+
symlinkSync(source, target, IS_WINDOWS ? 'junction' : 'dir');
|
|
85
|
+
result.installed.push(`${toolName}/${skill}`);
|
|
86
|
+
log(` done: ${skill} → ${toolName}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── SCIP CLI Binary Detection & Installation ───────────────
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if the `scip` CLI binary is available on PATH.
|
|
97
|
+
*/
|
|
98
|
+
export function isScipInstalled(): boolean {
|
|
99
|
+
try {
|
|
100
|
+
const cmd = IS_WINDOWS ? 'where' : 'which';
|
|
101
|
+
execFileSync(cmd, ['scip'], { stdio: 'pipe' });
|
|
102
|
+
return true;
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get the scip CLI version if installed.
|
|
110
|
+
*/
|
|
111
|
+
export function getScipVersion(): string | null {
|
|
112
|
+
try {
|
|
113
|
+
const output = execFileSync('scip', ['--version'], { stdio: 'pipe' }).toString().trim();
|
|
114
|
+
return output;
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Resolve the download URL for the scip CLI binary for this platform.
|
|
122
|
+
*/
|
|
123
|
+
function getScipDownloadUrl(): { url: string; filename: string } | null {
|
|
124
|
+
const os = platform();
|
|
125
|
+
const cpu = arch();
|
|
126
|
+
|
|
127
|
+
let osName: string;
|
|
128
|
+
let archName: string;
|
|
129
|
+
let ext: string;
|
|
130
|
+
|
|
131
|
+
switch (os) {
|
|
132
|
+
case 'darwin': osName = 'darwin'; ext = 'tar.gz'; break;
|
|
133
|
+
case 'linux': osName = 'linux'; ext = 'tar.gz'; break;
|
|
134
|
+
case 'win32': osName = 'windows'; ext = 'zip'; break;
|
|
135
|
+
default: return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
switch (cpu) {
|
|
139
|
+
case 'arm64': archName = 'arm64'; break;
|
|
140
|
+
case 'x64': archName = 'amd64'; break;
|
|
141
|
+
default: return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const filename = `scip-${osName}-${archName}.${ext}`;
|
|
145
|
+
const url = `https://github.com/sourcegraph/scip/releases/download/${SCIP_VERSION}/${filename}`;
|
|
146
|
+
return { url, filename };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Print instructions for installing the scip CLI binary.
|
|
151
|
+
*/
|
|
152
|
+
export function printScipInstallInstructions(): void {
|
|
153
|
+
const download = getScipDownloadUrl();
|
|
154
|
+
|
|
155
|
+
console.log('\nThe `scip` CLI is required but not found on PATH.\n');
|
|
156
|
+
|
|
157
|
+
if (platform() === 'darwin') {
|
|
158
|
+
console.log('Install via Homebrew:');
|
|
159
|
+
console.log(' brew install sourcegraph/scip/scip\n');
|
|
160
|
+
console.log('Or download manually:');
|
|
161
|
+
} else {
|
|
162
|
+
console.log('Download from:');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (download) {
|
|
166
|
+
console.log(` ${download.url}\n`);
|
|
167
|
+
} else {
|
|
168
|
+
console.log(` https://github.com/sourcegraph/scip/releases/tag/${SCIP_VERSION}\n`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log('After installing, ensure `scip` is on your PATH and run `scip-query reindex`.');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── First-Run Setup ────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Run first-time setup: install skills and check for scip binary.
|
|
178
|
+
* Called from the postinstall script.
|
|
179
|
+
*/
|
|
180
|
+
export function postinstall(): void {
|
|
181
|
+
console.log('scip-query: installing skills...');
|
|
182
|
+
const result = installSkills({ quiet: false });
|
|
183
|
+
|
|
184
|
+
const total = result.installed.length + result.alreadyLinked.length;
|
|
185
|
+
if (total > 0) {
|
|
186
|
+
console.log(`\n${result.installed.length} skill(s) installed, ${result.alreadyLinked.length} already linked.`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Check for scip binary — auto-install if missing
|
|
190
|
+
if (!isScipInstalled()) {
|
|
191
|
+
console.log('\nscip CLI not found on PATH. Attempting auto-install...');
|
|
192
|
+
const installed = tryInstallScipCli(console.log);
|
|
193
|
+
if (!installed) {
|
|
194
|
+
printScipInstallInstructions();
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
const version = getScipVersion();
|
|
198
|
+
console.log(`\nscip CLI: ${version ?? 'installed'}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log('');
|
|
202
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import type { ScipSymbol, ScipDescriptor, ScipLocalSymbol, DescriptorSuffix } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCIP Symbol Grammar (from the SCIP spec):
|
|
5
|
+
*
|
|
6
|
+
* <symbol> ::= <scheme> ' ' <package> ' ' <descriptor>+ | 'local ' <local-id>
|
|
7
|
+
* <package> ::= <manager> ' ' <package-name> ' ' <version> ' '
|
|
8
|
+
* <descriptor> ::= <name> <suffix>
|
|
9
|
+
*
|
|
10
|
+
* Suffix characters:
|
|
11
|
+
* / namespace
|
|
12
|
+
* # type (class, interface, enum)
|
|
13
|
+
* . term (variable, field, property)
|
|
14
|
+
* (). method
|
|
15
|
+
* [ type parameter
|
|
16
|
+
* () parameter
|
|
17
|
+
* : meta
|
|
18
|
+
* ! macro
|
|
19
|
+
*
|
|
20
|
+
* Names may be backtick-escaped: `some.weird/name`
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const SUFFIX_MAP: Record<string, DescriptorSuffix> = {
|
|
24
|
+
'/': 'namespace',
|
|
25
|
+
'#': 'type',
|
|
26
|
+
'.': 'term',
|
|
27
|
+
'[': 'type-param',
|
|
28
|
+
':': 'meta',
|
|
29
|
+
'!': 'macro',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parse a SCIP symbol string into its structured components.
|
|
34
|
+
* Works for any SCIP-indexed language (TypeScript, Java, Rust, Python, etc.)
|
|
35
|
+
*/
|
|
36
|
+
export function parseSymbol(raw: string): ScipSymbol | ScipLocalSymbol {
|
|
37
|
+
if (raw.startsWith('local ')) {
|
|
38
|
+
return { kind: 'local', id: raw.slice(6), raw };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Split: <scheme> <manager> <package-name> <version> <descriptors...>
|
|
42
|
+
// The tricky part: package-name can contain spaces if backtick-escaped
|
|
43
|
+
const parts = raw.split(' ');
|
|
44
|
+
if (parts.length < 4) {
|
|
45
|
+
// Malformed — return a best-effort parse
|
|
46
|
+
return {
|
|
47
|
+
scheme: parts[0] ?? '',
|
|
48
|
+
manager: parts[1] ?? '',
|
|
49
|
+
packageName: parts[2] ?? '',
|
|
50
|
+
version: '',
|
|
51
|
+
descriptors: [],
|
|
52
|
+
raw,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const scheme = parts[0]!;
|
|
57
|
+
const manager = parts[1]!;
|
|
58
|
+
|
|
59
|
+
// Package name and version: package name might be backtick-escaped
|
|
60
|
+
// After scheme + manager, we need to find package + version + descriptor string
|
|
61
|
+
let restAfterManager = raw.slice(scheme.length + 1 + manager.length + 1);
|
|
62
|
+
|
|
63
|
+
// Parse package name (may be backtick-escaped)
|
|
64
|
+
let packageName: string;
|
|
65
|
+
if (restAfterManager.startsWith('`')) {
|
|
66
|
+
const closingTick = restAfterManager.indexOf('`', 1);
|
|
67
|
+
if (closingTick === -1) {
|
|
68
|
+
packageName = restAfterManager.slice(1);
|
|
69
|
+
restAfterManager = '';
|
|
70
|
+
} else {
|
|
71
|
+
packageName = restAfterManager.slice(1, closingTick);
|
|
72
|
+
restAfterManager = restAfterManager.slice(closingTick + 2); // skip ` and space
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
const spaceIdx = restAfterManager.indexOf(' ');
|
|
76
|
+
if (spaceIdx === -1) {
|
|
77
|
+
packageName = restAfterManager;
|
|
78
|
+
restAfterManager = '';
|
|
79
|
+
} else {
|
|
80
|
+
packageName = restAfterManager.slice(0, spaceIdx);
|
|
81
|
+
restAfterManager = restAfterManager.slice(spaceIdx + 1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Parse version
|
|
86
|
+
let version: string;
|
|
87
|
+
const versionSpaceIdx = restAfterManager.indexOf(' ');
|
|
88
|
+
if (versionSpaceIdx === -1) {
|
|
89
|
+
version = restAfterManager;
|
|
90
|
+
restAfterManager = '';
|
|
91
|
+
} else {
|
|
92
|
+
version = restAfterManager.slice(0, versionSpaceIdx);
|
|
93
|
+
restAfterManager = restAfterManager.slice(versionSpaceIdx + 1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse descriptors from the remaining string
|
|
97
|
+
const descriptors = parseDescriptors(restAfterManager);
|
|
98
|
+
|
|
99
|
+
return { scheme, manager, packageName, version, descriptors, raw };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse the descriptor chain from a SCIP symbol.
|
|
104
|
+
*
|
|
105
|
+
* SCIP descriptor grammar:
|
|
106
|
+
* namespace: name/
|
|
107
|
+
* type: name#
|
|
108
|
+
* term: name.
|
|
109
|
+
* method: name(disambiguator).
|
|
110
|
+
* type-param: [name] (bracket-wrapped, prefix syntax)
|
|
111
|
+
* parameter: (name) (paren-wrapped, prefix syntax)
|
|
112
|
+
* meta: name:
|
|
113
|
+
* macro: name!
|
|
114
|
+
*
|
|
115
|
+
* Names can be backtick-escaped: `some/name.with.dots`
|
|
116
|
+
*/
|
|
117
|
+
function parseDescriptors(input: string): ScipDescriptor[] {
|
|
118
|
+
const descriptors: ScipDescriptor[] = [];
|
|
119
|
+
let i = 0;
|
|
120
|
+
|
|
121
|
+
while (i < input.length) {
|
|
122
|
+
// Type parameter: [name]
|
|
123
|
+
if (input[i] === '[') {
|
|
124
|
+
const closeBracket = input.indexOf(']', i + 1);
|
|
125
|
+
if (closeBracket === -1) {
|
|
126
|
+
descriptors.push({ name: input.slice(i + 1), suffix: 'type-param' });
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
descriptors.push({ name: input.slice(i + 1, closeBracket), suffix: 'type-param' });
|
|
130
|
+
i = closeBracket + 1;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Parameter: (name) — only when ( appears at descriptor start with no preceding name
|
|
135
|
+
if (input[i] === '(' && (descriptors.length === 0 || i === 0 || isSuffixChar(input[i - 1]!))) {
|
|
136
|
+
const closeParen = input.indexOf(')', i + 1);
|
|
137
|
+
if (closeParen !== -1 && input[closeParen + 1] !== '.') {
|
|
138
|
+
// This is a parameter (name), not a method disambiguator
|
|
139
|
+
descriptors.push({ name: input.slice(i + 1, closeParen), suffix: 'parameter' });
|
|
140
|
+
i = closeParen + 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let name: string;
|
|
146
|
+
|
|
147
|
+
// Backtick-escaped name
|
|
148
|
+
if (input[i] === '`') {
|
|
149
|
+
const closingTick = input.indexOf('`', i + 1);
|
|
150
|
+
if (closingTick === -1) {
|
|
151
|
+
name = input.slice(i + 1);
|
|
152
|
+
i = input.length;
|
|
153
|
+
descriptors.push({ name, suffix: 'term' });
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
name = input.slice(i + 1, closingTick);
|
|
157
|
+
i = closingTick + 1;
|
|
158
|
+
} else {
|
|
159
|
+
// Read name until we hit a suffix character
|
|
160
|
+
const start = i;
|
|
161
|
+
while (i < input.length && !isSuffixChar(input[i]!)) {
|
|
162
|
+
i++;
|
|
163
|
+
}
|
|
164
|
+
name = input.slice(start, i);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Parse suffix after name
|
|
168
|
+
if (i >= input.length) {
|
|
169
|
+
if (name) descriptors.push({ name, suffix: 'term' });
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const char = input[i]!;
|
|
174
|
+
|
|
175
|
+
// Method: name(disambiguator).
|
|
176
|
+
if (char === '(') {
|
|
177
|
+
const closeParen = input.indexOf(')', i + 1);
|
|
178
|
+
if (closeParen !== -1 && input[closeParen + 1] === '.') {
|
|
179
|
+
descriptors.push({ name, suffix: 'method' });
|
|
180
|
+
i = closeParen + 2; // skip past ).
|
|
181
|
+
} else if (closeParen !== -1) {
|
|
182
|
+
// Bare (disambiguator) without . — treat as method anyway (common in practice)
|
|
183
|
+
descriptors.push({ name, suffix: 'method' });
|
|
184
|
+
i = closeParen + 1;
|
|
185
|
+
} else {
|
|
186
|
+
descriptors.push({ name, suffix: 'term' });
|
|
187
|
+
i++;
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
const suffix = SUFFIX_MAP[char];
|
|
191
|
+
if (suffix) {
|
|
192
|
+
descriptors.push({ name, suffix });
|
|
193
|
+
i += 1;
|
|
194
|
+
} else {
|
|
195
|
+
i += 1; // Unknown suffix — skip
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return descriptors;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isSuffixChar(c: string): boolean {
|
|
204
|
+
return c === '/' || c === '#' || c === '.' || c === '(' || c === '[' || c === ':' || c === '!';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Convert a parsed SCIP symbol to a short, human-readable name.
|
|
209
|
+
* Language-agnostic: works for any SCIP-indexed language.
|
|
210
|
+
*
|
|
211
|
+
* Examples:
|
|
212
|
+
* "scip-typescript npm @vega/api 0.1.3 src/modules/auth/auth.service.ts/AuthService#login()."
|
|
213
|
+
* → "auth.service:AuthService:login()"
|
|
214
|
+
*
|
|
215
|
+
* "scip-java maven com.example/mylib 1.0.0 com/example/MyClass#doStuff()."
|
|
216
|
+
* → "MyClass:doStuff()"
|
|
217
|
+
*
|
|
218
|
+
* "rust-analyzer cargo my-crate 0.1.0 src/lib.rs/MyStruct#new()."
|
|
219
|
+
* → "lib:MyStruct:new()"
|
|
220
|
+
*/
|
|
221
|
+
export function shortenSymbol(raw: string): string {
|
|
222
|
+
const parsed = parseSymbol(raw);
|
|
223
|
+
if ('kind' in parsed && parsed.kind === 'local') {
|
|
224
|
+
return `local:${parsed.id}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const sym = parsed as ScipSymbol;
|
|
228
|
+
if (sym.descriptors.length === 0) return sym.raw;
|
|
229
|
+
|
|
230
|
+
const parts: string[] = [];
|
|
231
|
+
for (const desc of sym.descriptors) {
|
|
232
|
+
// Strip file extensions from namespace descriptors (the file path parts)
|
|
233
|
+
let name = desc.name;
|
|
234
|
+
if (desc.suffix === 'namespace') {
|
|
235
|
+
// Remove common file extensions
|
|
236
|
+
name = name
|
|
237
|
+
.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, '')
|
|
238
|
+
.replace(/\.(py|pyi)$/, '')
|
|
239
|
+
.replace(/\.(rs)$/, '')
|
|
240
|
+
.replace(/\.(java|scala|kt|kts)$/, '')
|
|
241
|
+
.replace(/\.(rb)$/, '')
|
|
242
|
+
.replace(/\.(go)$/, '')
|
|
243
|
+
.replace(/\.(cs|vb)$/, '')
|
|
244
|
+
.replace(/\.(dart)$/, '')
|
|
245
|
+
.replace(/\.(php)$/, '')
|
|
246
|
+
.replace(/\.(c|cc|cpp|cxx|h|hpp)$/, '');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Skip empty names (can happen with trailing suffixes)
|
|
250
|
+
if (!name) continue;
|
|
251
|
+
|
|
252
|
+
// For methods, append () for clarity
|
|
253
|
+
if (desc.suffix === 'method') {
|
|
254
|
+
parts.push(`${name}()`);
|
|
255
|
+
} else {
|
|
256
|
+
parts.push(name);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return parts.join(':');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Extract just the leaf name from a SCIP symbol.
|
|
265
|
+
* Useful when you only need the function/class/variable name without path context.
|
|
266
|
+
*/
|
|
267
|
+
export function leafName(raw: string): string {
|
|
268
|
+
const parsed = parseSymbol(raw);
|
|
269
|
+
if ('kind' in parsed && parsed.kind === 'local') {
|
|
270
|
+
return parsed.id;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const sym = parsed as ScipSymbol;
|
|
274
|
+
if (sym.descriptors.length === 0) return '';
|
|
275
|
+
|
|
276
|
+
const last = sym.descriptors[sym.descriptors.length - 1]!;
|
|
277
|
+
return last.name;
|
|
278
|
+
}
|