ghostimport 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/README.md ADDED
@@ -0,0 +1,240 @@
1
+ ![CI](https://github.com/FGuerreir0/ghostimport/actions/workflows/ci.yml/badge.svg)
2
+
3
+ # ghostimport
4
+
5
+ **Detects ghost imports — npm packages that don't exist, hallucinated by AI coding tools like Cursor, Copilot, and Claude**
6
+
7
+ AI coding tools sometimes generate `import` statements for packages that don't exist on npm. `ghostimport` scans your codebase and flags them before they cause a build failure — or worse, before an attacker registers the name with a malicious payload.
8
+
9
+ ```
10
+ $ npx ghostimport
11
+
12
+ ghostimport v0.1.0
13
+ Scanning /my-project
14
+
15
+ Scanned 142 files · 38 unique packages checked
16
+
17
+ ✗ 2 hallucinated packages (do not exist on npm):
18
+
19
+ ● @openai/functions-runtime
20
+ ↳ src/agents/runner.ts
21
+ ↳ src/agents/tools.ts
22
+
23
+ ● react-server-fetch
24
+ ↳ src/data/loader.ts
25
+
26
+ Found 2 issues.
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Why this exists
32
+
33
+ When an LLM generates code, it predicts the most likely next token — not the most accurate one. It will confidently write `import { createAgent } from '@langchain/agent-runtime'` even if that exact package doesn't exist.
34
+
35
+ The result: your build fails, or your CI breaks at 2am, or — in the worst case — an attacker who monitors public GitHub repos for unregistered package names [registers it with a `postinstall` script that exfiltrates your `.env`](https://vibedoctor.io/blog/hallucinated-imports-ai-packages-dont-exist).
36
+
37
+ `ghostimport` catches this in seconds.
38
+
39
+ ---
40
+
41
+ ## Install
42
+
43
+ ```bash
44
+ # Run once (no install needed)
45
+ npx ghostimport
46
+
47
+ # Or install globally
48
+ npm install -g ghostimport
49
+
50
+ # Or as a dev dependency
51
+ npm install --save-dev ghostimport
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Usage
57
+
58
+ ```bash
59
+ # Scan current directory
60
+ ghostimport
61
+
62
+ # Scan a specific folder
63
+ ghostimport ./src
64
+
65
+ # Only show problems (great for CI)
66
+ ghostimport --quiet
67
+
68
+ # JSON output (pipe to other tools)
69
+ ghostimport --json
70
+
71
+ # Skip "missing from package.json" warnings
72
+ ghostimport --no-undeclared
73
+
74
+ # Help
75
+ ghostimport --help
76
+ ```
77
+
78
+ ### Add to CI (GitHub Actions)
79
+
80
+ ```yaml
81
+ - name: Check for hallucinated packages
82
+ run: npx ghostimport --quiet
83
+ ```
84
+
85
+ This step will fail (exit code 1) if any hallucinated packages are found.
86
+
87
+ ---
88
+
89
+ ## Programmatic API
90
+
91
+ ```ts
92
+ import { scan, extractImports, checkNpm } from 'ghostimport'
93
+
94
+ // Scan a directory
95
+ const results = await scan('./src')
96
+
97
+ console.log(results.hallucinated)
98
+ // [{ pkg: '@openai/functions-runtime', files: ['src/agents/runner.ts'] }]
99
+
100
+ console.log(results.notInPackageJson)
101
+ // [{ pkg: 'zod', files: ['src/validate.ts'] }]
102
+
103
+ // Check a single package
104
+ const { exists } = await checkNpm('some-package-name')
105
+ // exists: true | false | null (null = network error)
106
+
107
+ // Extract imports from a string
108
+ const imports = extractImports(`
109
+ import React from 'react'
110
+ import { createAgent } from '@fake/pkg'
111
+ `)
112
+ // ['react', '@fake/pkg']
113
+ ```
114
+
115
+ ### TypeScript types
116
+
117
+ The package ships with full TypeScript declarations. Key types:
118
+
119
+ ```ts
120
+ import type { ScanResult, ScanOptions, ScaryEntry, NpmCheckResult } from 'ghostimport'
121
+
122
+ interface ScanResult {
123
+ scanned: number // total files scanned
124
+ packages: number // unique packages found
125
+ hallucinated: { // packages that DON'T exist on npm
126
+ pkg: string
127
+ files: string[]
128
+ }[]
129
+ notInPackageJson: { // packages that exist but aren't declared
130
+ pkg: string
131
+ files: string[]
132
+ }[]
133
+ errors: { // packages that couldn't be checked (network)
134
+ pkg: string
135
+ error: string
136
+ files: string[]
137
+ }[]
138
+ scary: ScaryEntry[] // supply chain risk entries (--scary mode)
139
+ cacheHits: number
140
+ }
141
+
142
+ interface ScanOptions {
143
+ onProgress?: (p: { pkg: string; exists: boolean | null; done: number; total: number }) => void
144
+ useCache?: boolean // default: true
145
+ scary?: boolean // default: false
146
+ config?: Config
147
+ }
148
+ ```
149
+
150
+ ---
151
+
152
+ ## What it scans
153
+
154
+ - `import x from 'pkg'`
155
+ - `import { x } from 'pkg'`
156
+ - `import * as x from 'pkg'`
157
+ - `require('pkg')`
158
+ - `await import('pkg')`
159
+ - `export { x } from 'pkg'`
160
+ - Scoped packages: `@scope/name`
161
+ - Subpath imports: `pkg/utils` → checks `pkg`
162
+
163
+ **Automatically ignores:**
164
+ - Node.js built-ins (`fs`, `path`, `crypto`, `node:*`, ...)
165
+ - Relative imports (`./`, `../`)
166
+ - `node_modules/`, `dist/`, `.git/`, `build/`
167
+
168
+ **Supported file types:** `.js` `.jsx` `.ts` `.tsx` `.mjs` `.cjs`
169
+
170
+ ---
171
+
172
+ ## Supply chain risk (`--scary`)
173
+
174
+ ```bash
175
+ ghostimport --scary
176
+ ```
177
+
178
+ In scary mode, `ghostimport` also checks whether hallucinated package names are available for malicious registration, and flags recently-published packages with suspicious signals (new, few downloads, single version).
179
+
180
+ ---
181
+
182
+ ## Config file
183
+
184
+ Create `.ghostimportrc.json` in your project root:
185
+
186
+ ```json
187
+ {
188
+ "ignore": ["@company/*", "internal-lib"],
189
+ "includeUndeclared": true
190
+ }
191
+ ```
192
+
193
+ | Field | Default | Description |
194
+ |---|---|---|
195
+ | `ignore` | `[]` | Packages or scope patterns to skip (`@scope/*` supported) |
196
+ | `includeUndeclared` | `true` | Warn on packages that exist on npm but aren't in `package.json` |
197
+
198
+ ---
199
+
200
+ ## Zero dependencies
201
+
202
+ `ghostimport` has **no runtime dependencies**. The published package uses only Node.js built-ins. This means:
203
+
204
+ - No supply chain risk from the tool itself
205
+ - Fast install
206
+ - Works offline for the scan phase (network only needed to check npm)
207
+
208
+ ---
209
+
210
+ ## Contributing
211
+
212
+ ```bash
213
+ git clone https://github.com/FGuerreir0/ghostimport
214
+ cd ghostimport
215
+ npm install
216
+
217
+ npm run build # type-check + emit .d.ts + bundle with esbuild
218
+ npm test # run test suite
219
+ npm run dev # run CLI from source (no build needed)
220
+ npm run typecheck # type-check only (no output)
221
+ ```
222
+
223
+ Source layout:
224
+
225
+ | File | Purpose |
226
+ |---|---|
227
+ | `src/types.ts` | All exported TypeScript interfaces |
228
+ | `src/imports.ts` | Import extraction (regex) |
229
+ | `src/cache.ts` | Local registry cache (`~/.ghostimport/`) |
230
+ | `src/config.ts` | `.ghostimportrc.json` loading |
231
+ | `src/npm.ts` | npm registry checks, supply chain heuristics |
232
+ | `src/files.ts` | File walker, `package.json` deps, monorepo support |
233
+ | `src/scan.ts` | Main `scan()` orchestrator |
234
+ | `src/cli.ts` | CLI interface |
235
+
236
+ ---
237
+
238
+ ## License
239
+
240
+ MIT
@@ -0,0 +1,4 @@
1
+ import type { CacheEntry } from './types';
2
+ export declare function loadCache(): Record<string, CacheEntry>;
3
+ export declare function saveCache(cache: Record<string, CacheEntry>): void;
4
+ export declare function getCached(cache: Record<string, CacheEntry>, pkgName: string): CacheEntry | null;
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};