raggrep 0.1.7 → 0.2.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 +62 -95
- package/dist/app/indexer/index.d.ts +25 -0
- package/dist/cli/main.js +200 -31
- package/dist/cli/main.js.map +9 -9
- package/dist/index.js +26 -10
- package/dist/index.js.map +7 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,160 +1,127 @@
|
|
|
1
1
|
# RAGgrep
|
|
2
2
|
|
|
3
|
-
**Local
|
|
3
|
+
**Local semantic search for codebases** — find code using natural language queries.
|
|
4
4
|
|
|
5
|
-
RAGgrep indexes your code and
|
|
5
|
+
RAGgrep indexes your code and lets you search it using natural language. Everything runs locally — no external API calls required.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- **📝 TypeScript-optimized** — AST-based parsing extracts functions, classes, interfaces, types.
|
|
15
|
-
- **🎯 Zero config** — Works out of the box with sensible defaults.
|
|
9
|
+
- **Zero-config search** — Just run `raggrep query` and it works. Index is created and updated automatically.
|
|
10
|
+
- **Local-first** — All indexing and search happens on your machine. No cloud dependencies.
|
|
11
|
+
- **Incremental** — Only re-indexes files that have changed. Instant search when nothing changed.
|
|
12
|
+
- **Watch mode** — Keep the index fresh in real-time as you code.
|
|
13
|
+
- **Hybrid search** — Combines semantic similarity with keyword matching for best results.
|
|
16
14
|
|
|
17
15
|
## Installation
|
|
18
16
|
|
|
19
17
|
```bash
|
|
20
|
-
# Install globally
|
|
18
|
+
# Install globally
|
|
21
19
|
npm install -g raggrep
|
|
22
20
|
|
|
23
|
-
# Or with Bun (recommended)
|
|
24
|
-
bun install -g raggrep
|
|
25
|
-
|
|
26
21
|
# Or use without installing
|
|
27
|
-
npx raggrep
|
|
22
|
+
npx raggrep query "your search"
|
|
28
23
|
```
|
|
29
24
|
|
|
30
|
-
##
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Search Your Code
|
|
31
28
|
|
|
32
29
|
```bash
|
|
33
|
-
# Index your project
|
|
34
30
|
cd your-project
|
|
35
|
-
raggrep index
|
|
36
|
-
|
|
37
|
-
# Search your codebase
|
|
38
31
|
raggrep query "user authentication"
|
|
39
32
|
```
|
|
40
33
|
|
|
34
|
+
That's it. The first query creates the index automatically. Subsequent queries are instant if files haven't changed. Modified files are re-indexed on the fly.
|
|
35
|
+
|
|
41
36
|
### Example Output
|
|
42
37
|
|
|
43
38
|
```
|
|
39
|
+
Index updated: 42 indexed
|
|
40
|
+
|
|
41
|
+
RAGgrep Search
|
|
42
|
+
==============
|
|
43
|
+
|
|
44
|
+
Searching for: "user authentication"
|
|
45
|
+
|
|
44
46
|
Found 3 results:
|
|
45
47
|
|
|
46
48
|
1. src/auth/authService.ts:24-55 (login)
|
|
47
|
-
Score: 34.4% | Type: function | exported
|
|
48
|
-
export async function login(credentials: LoginCredentials): Promise<AuthResult>
|
|
49
|
+
Score: 34.4% | Type: function | via TypeScript | exported
|
|
50
|
+
export async function login(credentials: LoginCredentials): Promise<AuthResult> {
|
|
51
|
+
const { email, password } = credentials;
|
|
49
52
|
|
|
50
|
-
2. src/auth/
|
|
51
|
-
Score:
|
|
52
|
-
export
|
|
53
|
+
2. src/auth/session.ts:10-25 (createSession)
|
|
54
|
+
Score: 28.2% | Type: function | via TypeScript | exported
|
|
55
|
+
export function createSession(user: User): Session {
|
|
53
56
|
|
|
54
57
|
3. src/users/types.ts:3-12 (User)
|
|
55
|
-
Score: 26.0% | Type: interface | exported
|
|
58
|
+
Score: 26.0% | Type: interface | via TypeScript | exported
|
|
56
59
|
export interface User {
|
|
57
60
|
id: string;
|
|
58
61
|
```
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
import raggrep from "raggrep";
|
|
64
|
-
|
|
65
|
-
// Index a directory
|
|
66
|
-
await raggrep.index("./my-project");
|
|
63
|
+
### Watch Mode
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
const results = await raggrep.search("./my-project", "user authentication");
|
|
70
|
-
console.log(raggrep.formatSearchResults(results));
|
|
65
|
+
Keep your index fresh in real-time while you code:
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
```bash
|
|
68
|
+
raggrep index --watch
|
|
74
69
|
```
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
This monitors file changes and re-indexes automatically. Useful during active development when you want instant search results.
|
|
77
72
|
|
|
78
|
-
```bash
|
|
79
|
-
# Index commands
|
|
80
|
-
raggrep index # Index current directory
|
|
81
|
-
raggrep index --watch # Watch mode: re-index on file changes
|
|
82
|
-
raggrep index --model bge-small-en-v1.5 # Use different embedding model
|
|
83
|
-
raggrep index --verbose # Show detailed progress
|
|
84
|
-
|
|
85
|
-
# Search commands
|
|
86
|
-
raggrep query "user login" # Basic search
|
|
87
|
-
raggrep query "error handling" --top 5 # Limit results
|
|
88
|
-
raggrep query "database" --min-score 0.1 # Lower threshold (more results)
|
|
89
|
-
raggrep query "interface" --type ts # Filter by file type
|
|
90
|
-
|
|
91
|
-
# Maintenance
|
|
92
|
-
raggrep cleanup # Remove stale index entries
|
|
93
|
-
raggrep status # Show index status
|
|
94
73
|
```
|
|
74
|
+
┌─────────────────────────────────────────┐
|
|
75
|
+
│ Watching for changes... (Ctrl+C to stop) │
|
|
76
|
+
└─────────────────────────────────────────┘
|
|
95
77
|
|
|
96
|
-
|
|
78
|
+
[Watch] language/typescript: 2 indexed, 0 errors
|
|
79
|
+
```
|
|
97
80
|
|
|
98
|
-
|
|
81
|
+
## CLI Quick Reference
|
|
99
82
|
|
|
100
|
-
|
|
83
|
+
```bash
|
|
84
|
+
# Search (auto-indexes if needed)
|
|
85
|
+
raggrep query "user login"
|
|
86
|
+
raggrep query "error handling" --top 5
|
|
87
|
+
raggrep query "database" --type ts
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- Works on any text file
|
|
89
|
+
# Watch mode
|
|
90
|
+
raggrep index --watch
|
|
105
91
|
|
|
106
|
-
|
|
92
|
+
# Check index status
|
|
93
|
+
raggrep status
|
|
94
|
+
```
|
|
107
95
|
|
|
108
|
-
|
|
109
|
-
- **Semantic embeddings** for natural language understanding
|
|
110
|
-
- **Symbolic index** for fast BM25 candidate filtering
|
|
96
|
+
## How It Works
|
|
111
97
|
|
|
112
|
-
|
|
98
|
+
1. **First query** — Creates the index (takes 1-2 min for ~1000 files)
|
|
99
|
+
2. **Subsequent queries** — Uses cached index (instant if no changes)
|
|
100
|
+
3. **Files changed** — Re-indexes only modified files automatically
|
|
101
|
+
4. **Files deleted** — Stale entries cleaned up automatically
|
|
113
102
|
|
|
114
|
-
|
|
115
|
-
Query → Core (symbol/BM25) ─┐
|
|
116
|
-
├→ Merge & rank → Results
|
|
117
|
-
Query → TypeScript (BM25 filter → semantic) ─┘
|
|
118
|
-
```
|
|
103
|
+
The index is stored in a system temp directory, keeping your project clean.
|
|
119
104
|
|
|
120
105
|
## What Gets Indexed
|
|
121
106
|
|
|
122
|
-
**File types:** `.ts`, `.tsx`, `.js`, `.jsx`, `.py`, `.go`, `.rs`, `.java`, `.md`
|
|
123
|
-
|
|
124
|
-
**Code structures:**
|
|
125
|
-
|
|
126
|
-
- Functions (regular, async, arrow)
|
|
127
|
-
- Classes (including abstract)
|
|
128
|
-
- Interfaces
|
|
129
|
-
- Type aliases
|
|
130
|
-
- Enums
|
|
131
|
-
- Exported variables
|
|
107
|
+
**File types:** `.ts`, `.tsx`, `.js`, `.jsx`, `.py`, `.go`, `.rs`, `.java`, `.md`, `.txt`
|
|
132
108
|
|
|
133
|
-
**
|
|
109
|
+
**Code structures:** Functions, classes, interfaces, types, enums, exports
|
|
134
110
|
|
|
135
|
-
|
|
136
|
-
- `.next`, `.nuxt`, `__pycache__`, `venv`
|
|
137
|
-
- See [Configuration](./docs/configuration.md) for full list
|
|
111
|
+
**Automatically ignored:** `node_modules`, `dist`, `build`, `.git`, and other common directories
|
|
138
112
|
|
|
139
113
|
## Documentation
|
|
140
114
|
|
|
141
|
-
- [Getting Started](./docs/getting-started.md) — Installation and first steps
|
|
115
|
+
- [Getting Started](./docs/getting-started.md) — Installation options and first steps
|
|
142
116
|
- [CLI Reference](./docs/cli-reference.md) — All commands and options
|
|
143
|
-
- [
|
|
117
|
+
- [SDK Reference](./docs/sdk.md) — Programmatic API for Node.js/Bun
|
|
118
|
+
- [Advanced](./docs/advanced.md) — Configuration, maintenance commands
|
|
144
119
|
- [Architecture](./docs/architecture.md) — How RAGgrep works internally
|
|
145
120
|
|
|
146
|
-
## Performance
|
|
147
|
-
|
|
148
|
-
| Operation | Time | Notes |
|
|
149
|
-
| ------------------------ | ---------- | -------------------------------------- |
|
|
150
|
-
| Initial index (1k files) | 1-2 min | Embedding generation is the bottleneck |
|
|
151
|
-
| Incremental update | <2s | Only changed files |
|
|
152
|
-
| Search | ~100-500ms | Depends on codebase size |
|
|
153
|
-
|
|
154
121
|
## Requirements
|
|
155
122
|
|
|
156
123
|
- Node.js 18+ or Bun 1.0+
|
|
157
|
-
- ~50MB disk space for models (cached
|
|
124
|
+
- ~50MB disk space for models (cached at `~/.cache/raggrep/models/`)
|
|
158
125
|
|
|
159
126
|
## License
|
|
160
127
|
|
|
@@ -10,6 +10,16 @@ export interface IndexOptions {
|
|
|
10
10
|
model?: EmbeddingModelName;
|
|
11
11
|
/** Show detailed progress */
|
|
12
12
|
verbose?: boolean;
|
|
13
|
+
/** Suppress most output (for use during query) */
|
|
14
|
+
quiet?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface EnsureFreshResult {
|
|
17
|
+
/** Number of files indexed (new or modified) */
|
|
18
|
+
indexed: number;
|
|
19
|
+
/** Number of stale entries removed (deleted files) */
|
|
20
|
+
removed: number;
|
|
21
|
+
/** Number of files unchanged (used cache) */
|
|
22
|
+
unchanged: number;
|
|
13
23
|
}
|
|
14
24
|
export interface CleanupResult {
|
|
15
25
|
moduleId: string;
|
|
@@ -40,6 +50,21 @@ export interface IndexStatus {
|
|
|
40
50
|
* Index a directory using all enabled modules
|
|
41
51
|
*/
|
|
42
52
|
export declare function indexDirectory(rootDir: string, options?: IndexOptions): Promise<IndexResult[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Ensure the index is fresh by checking for changes and updating incrementally.
|
|
55
|
+
* This function is designed to be called before search to transparently manage the index.
|
|
56
|
+
*
|
|
57
|
+
* - If no index exists, creates a full index
|
|
58
|
+
* - If index version is incompatible, rebuilds from scratch
|
|
59
|
+
* - If files have changed, re-indexes only the modified files
|
|
60
|
+
* - If files have been deleted, removes stale entries
|
|
61
|
+
* - If nothing changed, returns immediately (uses cache)
|
|
62
|
+
*
|
|
63
|
+
* @param rootDir - Root directory of the project
|
|
64
|
+
* @param options - Index options
|
|
65
|
+
* @returns Statistics about what was updated
|
|
66
|
+
*/
|
|
67
|
+
export declare function ensureIndexFresh(rootDir: string, options?: IndexOptions): Promise<EnsureFreshResult>;
|
|
43
68
|
/**
|
|
44
69
|
* Clean up stale index entries for files that no longer exist
|
|
45
70
|
* @param rootDir - Root directory of the project
|
package/dist/cli/main.js
CHANGED
|
@@ -3383,6 +3383,7 @@ __export(exports_indexer, {
|
|
|
3383
3383
|
watchDirectory: () => watchDirectory,
|
|
3384
3384
|
indexDirectory: () => indexDirectory,
|
|
3385
3385
|
getIndexStatus: () => getIndexStatus,
|
|
3386
|
+
ensureIndexFresh: () => ensureIndexFresh,
|
|
3386
3387
|
cleanupIndex: () => cleanupIndex
|
|
3387
3388
|
});
|
|
3388
3389
|
import { glob } from "glob";
|
|
@@ -3390,10 +3391,13 @@ import * as fs6 from "fs/promises";
|
|
|
3390
3391
|
import * as path12 from "path";
|
|
3391
3392
|
async function indexDirectory(rootDir, options = {}) {
|
|
3392
3393
|
const verbose = options.verbose ?? false;
|
|
3394
|
+
const quiet = options.quiet ?? false;
|
|
3393
3395
|
rootDir = path12.resolve(rootDir);
|
|
3394
3396
|
const location = getIndexLocation(rootDir);
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
+
if (!quiet) {
|
|
3398
|
+
console.log(`Indexing directory: ${rootDir}`);
|
|
3399
|
+
console.log(`Index location: ${location.indexDir}`);
|
|
3400
|
+
}
|
|
3397
3401
|
const config = await loadConfig(rootDir);
|
|
3398
3402
|
const introspection = new IntrospectionIndex(rootDir);
|
|
3399
3403
|
await introspection.initialize();
|
|
@@ -3406,16 +3410,24 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
3406
3410
|
await registerBuiltInModules();
|
|
3407
3411
|
const enabledModules = registry.getEnabled(config);
|
|
3408
3412
|
if (enabledModules.length === 0) {
|
|
3409
|
-
|
|
3413
|
+
if (!quiet) {
|
|
3414
|
+
console.log("No modules enabled. Check your configuration.");
|
|
3415
|
+
}
|
|
3410
3416
|
return [];
|
|
3411
3417
|
}
|
|
3412
|
-
|
|
3418
|
+
if (!quiet) {
|
|
3419
|
+
console.log(`Enabled modules: ${enabledModules.map((m) => m.id).join(", ")}`);
|
|
3420
|
+
}
|
|
3413
3421
|
const files = await findFiles(rootDir, config);
|
|
3414
|
-
|
|
3422
|
+
if (!quiet) {
|
|
3423
|
+
console.log(`Found ${files.length} files to index`);
|
|
3424
|
+
}
|
|
3415
3425
|
const results = [];
|
|
3416
3426
|
for (const module of enabledModules) {
|
|
3417
|
-
|
|
3427
|
+
if (!quiet) {
|
|
3428
|
+
console.log(`
|
|
3418
3429
|
[${module.name}] Starting indexing...`);
|
|
3430
|
+
}
|
|
3419
3431
|
const moduleConfig = getModuleConfig(config, module.id);
|
|
3420
3432
|
if (module.initialize && moduleConfig) {
|
|
3421
3433
|
const configWithOverrides = { ...moduleConfig };
|
|
@@ -3430,7 +3442,9 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
3430
3442
|
const result = await indexWithModule(rootDir, files, module, config, verbose, introspection);
|
|
3431
3443
|
results.push(result);
|
|
3432
3444
|
if (module.finalize) {
|
|
3433
|
-
|
|
3445
|
+
if (!quiet) {
|
|
3446
|
+
console.log(`[${module.name}] Building secondary indexes...`);
|
|
3447
|
+
}
|
|
3434
3448
|
const ctx = {
|
|
3435
3449
|
rootDir,
|
|
3436
3450
|
config,
|
|
@@ -3446,12 +3460,167 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
3446
3460
|
};
|
|
3447
3461
|
await module.finalize(ctx);
|
|
3448
3462
|
}
|
|
3449
|
-
|
|
3463
|
+
if (!quiet) {
|
|
3464
|
+
console.log(`[${module.name}] Complete: ${result.indexed} indexed, ${result.skipped} skipped, ${result.errors} errors`);
|
|
3465
|
+
}
|
|
3450
3466
|
}
|
|
3451
3467
|
await introspection.save(config);
|
|
3452
3468
|
await updateGlobalManifest(rootDir, enabledModules, config);
|
|
3453
3469
|
return results;
|
|
3454
3470
|
}
|
|
3471
|
+
async function isIndexVersionCompatible(rootDir) {
|
|
3472
|
+
const config = await loadConfig(rootDir);
|
|
3473
|
+
const globalManifestPath = getGlobalManifestPath(rootDir, config);
|
|
3474
|
+
try {
|
|
3475
|
+
const content = await fs6.readFile(globalManifestPath, "utf-8");
|
|
3476
|
+
const manifest = JSON.parse(content);
|
|
3477
|
+
return manifest.version === INDEX_SCHEMA_VERSION;
|
|
3478
|
+
} catch {
|
|
3479
|
+
return false;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
async function deleteIndex(rootDir) {
|
|
3483
|
+
const indexDir = getRaggrepDir(rootDir);
|
|
3484
|
+
try {
|
|
3485
|
+
await fs6.rm(indexDir, { recursive: true, force: true });
|
|
3486
|
+
} catch {}
|
|
3487
|
+
}
|
|
3488
|
+
async function ensureIndexFresh(rootDir, options = {}) {
|
|
3489
|
+
const verbose = options.verbose ?? false;
|
|
3490
|
+
const quiet = options.quiet ?? false;
|
|
3491
|
+
rootDir = path12.resolve(rootDir);
|
|
3492
|
+
const status = await getIndexStatus(rootDir);
|
|
3493
|
+
if (!status.exists) {
|
|
3494
|
+
if (!quiet) {
|
|
3495
|
+
console.log(`No index found. Creating index...
|
|
3496
|
+
`);
|
|
3497
|
+
}
|
|
3498
|
+
const results = await indexDirectory(rootDir, { ...options, quiet });
|
|
3499
|
+
const totalIndexed2 = results.reduce((sum, r) => sum + r.indexed, 0);
|
|
3500
|
+
return { indexed: totalIndexed2, removed: 0, unchanged: 0 };
|
|
3501
|
+
}
|
|
3502
|
+
const versionCompatible = await isIndexVersionCompatible(rootDir);
|
|
3503
|
+
if (!versionCompatible) {
|
|
3504
|
+
if (!quiet) {
|
|
3505
|
+
console.log(`Index version incompatible. Rebuilding...
|
|
3506
|
+
`);
|
|
3507
|
+
}
|
|
3508
|
+
await deleteIndex(rootDir);
|
|
3509
|
+
const results = await indexDirectory(rootDir, { ...options, quiet });
|
|
3510
|
+
const totalIndexed2 = results.reduce((sum, r) => sum + r.indexed, 0);
|
|
3511
|
+
return { indexed: totalIndexed2, removed: 0, unchanged: 0 };
|
|
3512
|
+
}
|
|
3513
|
+
const config = await loadConfig(rootDir);
|
|
3514
|
+
await registerBuiltInModules();
|
|
3515
|
+
const enabledModules = registry.getEnabled(config);
|
|
3516
|
+
if (enabledModules.length === 0) {
|
|
3517
|
+
return { indexed: 0, removed: 0, unchanged: 0 };
|
|
3518
|
+
}
|
|
3519
|
+
const introspection = new IntrospectionIndex(rootDir);
|
|
3520
|
+
await introspection.initialize();
|
|
3521
|
+
const currentFiles = await findFiles(rootDir, config);
|
|
3522
|
+
const currentFileSet = new Set(currentFiles.map((f) => path12.relative(rootDir, f)));
|
|
3523
|
+
let totalIndexed = 0;
|
|
3524
|
+
let totalRemoved = 0;
|
|
3525
|
+
let totalUnchanged = 0;
|
|
3526
|
+
for (const module of enabledModules) {
|
|
3527
|
+
const moduleConfig = getModuleConfig(config, module.id);
|
|
3528
|
+
if (module.initialize && moduleConfig) {
|
|
3529
|
+
const configWithOverrides = { ...moduleConfig };
|
|
3530
|
+
if (options.model && module.id === "language/typescript") {
|
|
3531
|
+
configWithOverrides.options = {
|
|
3532
|
+
...configWithOverrides.options,
|
|
3533
|
+
embeddingModel: options.model
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
await module.initialize(configWithOverrides);
|
|
3537
|
+
}
|
|
3538
|
+
const manifest = await loadModuleManifest(rootDir, module.id, config);
|
|
3539
|
+
const indexPath = getModuleIndexPath(rootDir, module.id, config);
|
|
3540
|
+
const filesToRemove = [];
|
|
3541
|
+
for (const filepath of Object.keys(manifest.files)) {
|
|
3542
|
+
if (!currentFileSet.has(filepath)) {
|
|
3543
|
+
filesToRemove.push(filepath);
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
for (const filepath of filesToRemove) {
|
|
3547
|
+
if (verbose) {
|
|
3548
|
+
console.log(` Removing stale: ${filepath}`);
|
|
3549
|
+
}
|
|
3550
|
+
const indexFilePath = path12.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
|
|
3551
|
+
try {
|
|
3552
|
+
await fs6.unlink(indexFilePath);
|
|
3553
|
+
} catch {}
|
|
3554
|
+
delete manifest.files[filepath];
|
|
3555
|
+
totalRemoved++;
|
|
3556
|
+
}
|
|
3557
|
+
const ctx = {
|
|
3558
|
+
rootDir,
|
|
3559
|
+
config,
|
|
3560
|
+
readFile: async (filepath) => {
|
|
3561
|
+
const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
|
|
3562
|
+
return fs6.readFile(fullPath, "utf-8");
|
|
3563
|
+
},
|
|
3564
|
+
getFileStats: async (filepath) => {
|
|
3565
|
+
const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
|
|
3566
|
+
const stats = await fs6.stat(fullPath);
|
|
3567
|
+
return { lastModified: stats.mtime.toISOString() };
|
|
3568
|
+
},
|
|
3569
|
+
getIntrospection: (filepath) => introspection.getFile(filepath)
|
|
3570
|
+
};
|
|
3571
|
+
for (const filepath of currentFiles) {
|
|
3572
|
+
const relativePath = path12.relative(rootDir, filepath);
|
|
3573
|
+
try {
|
|
3574
|
+
const stats = await fs6.stat(filepath);
|
|
3575
|
+
const lastModified = stats.mtime.toISOString();
|
|
3576
|
+
const existingEntry = manifest.files[relativePath];
|
|
3577
|
+
if (existingEntry && existingEntry.lastModified === lastModified) {
|
|
3578
|
+
totalUnchanged++;
|
|
3579
|
+
continue;
|
|
3580
|
+
}
|
|
3581
|
+
if (verbose) {
|
|
3582
|
+
console.log(` Indexing: ${relativePath}`);
|
|
3583
|
+
}
|
|
3584
|
+
const content = await fs6.readFile(filepath, "utf-8");
|
|
3585
|
+
introspection.addFile(relativePath, content);
|
|
3586
|
+
const fileIndex = await module.indexFile(relativePath, content, ctx);
|
|
3587
|
+
if (fileIndex) {
|
|
3588
|
+
await writeFileIndex(rootDir, module.id, relativePath, fileIndex, config);
|
|
3589
|
+
manifest.files[relativePath] = {
|
|
3590
|
+
lastModified,
|
|
3591
|
+
chunkCount: fileIndex.chunks.length
|
|
3592
|
+
};
|
|
3593
|
+
totalIndexed++;
|
|
3594
|
+
}
|
|
3595
|
+
} catch (error) {
|
|
3596
|
+
if (verbose) {
|
|
3597
|
+
console.error(` Error indexing ${relativePath}:`, error);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
if (totalIndexed > 0 || totalRemoved > 0) {
|
|
3602
|
+
manifest.lastUpdated = new Date().toISOString();
|
|
3603
|
+
await writeModuleManifest(rootDir, module.id, manifest, config);
|
|
3604
|
+
if (module.finalize) {
|
|
3605
|
+
await module.finalize(ctx);
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
if (totalRemoved > 0) {
|
|
3609
|
+
await cleanupEmptyDirectories(indexPath);
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
if (totalIndexed > 0) {
|
|
3613
|
+
await introspection.save(config);
|
|
3614
|
+
}
|
|
3615
|
+
if (totalIndexed > 0 || totalRemoved > 0) {
|
|
3616
|
+
await updateGlobalManifest(rootDir, enabledModules, config);
|
|
3617
|
+
}
|
|
3618
|
+
return {
|
|
3619
|
+
indexed: totalIndexed,
|
|
3620
|
+
removed: totalRemoved,
|
|
3621
|
+
unchanged: totalUnchanged
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3455
3624
|
async function indexWithModule(rootDir, files, module, config, verbose, introspection) {
|
|
3456
3625
|
const result = {
|
|
3457
3626
|
moduleId: module.id,
|
|
@@ -3557,7 +3726,7 @@ async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
|
|
|
3557
3726
|
async function updateGlobalManifest(rootDir, modules, config) {
|
|
3558
3727
|
const manifestPath = getGlobalManifestPath(rootDir, config);
|
|
3559
3728
|
const manifest = {
|
|
3560
|
-
version:
|
|
3729
|
+
version: INDEX_SCHEMA_VERSION,
|
|
3561
3730
|
lastUpdated: new Date().toISOString(),
|
|
3562
3731
|
modules: modules.map((m) => m.id)
|
|
3563
3732
|
};
|
|
@@ -3697,6 +3866,7 @@ async function getIndexStatus(rootDir) {
|
|
|
3697
3866
|
}
|
|
3698
3867
|
return status;
|
|
3699
3868
|
}
|
|
3869
|
+
var INDEX_SCHEMA_VERSION = "1.0.0";
|
|
3700
3870
|
var init_indexer = __esm(() => {
|
|
3701
3871
|
init_config2();
|
|
3702
3872
|
init_registry();
|
|
@@ -3851,7 +4021,7 @@ init_embeddings();
|
|
|
3851
4021
|
// package.json
|
|
3852
4022
|
var package_default = {
|
|
3853
4023
|
name: "raggrep",
|
|
3854
|
-
version: "0.
|
|
4024
|
+
version: "0.2.0",
|
|
3855
4025
|
description: "Local filesystem-based RAG system for codebases - semantic search using local embeddings",
|
|
3856
4026
|
type: "module",
|
|
3857
4027
|
main: "./dist/index.js",
|
|
@@ -4093,8 +4263,11 @@ Options:
|
|
|
4093
4263
|
-h, --help Show this help message
|
|
4094
4264
|
|
|
4095
4265
|
Note:
|
|
4096
|
-
|
|
4097
|
-
|
|
4266
|
+
The index is managed automatically like a cache:
|
|
4267
|
+
- First query creates the index
|
|
4268
|
+
- Changed files are re-indexed automatically
|
|
4269
|
+
- Deleted files are cleaned up automatically
|
|
4270
|
+
- Unchanged files use the cached index (instant)
|
|
4098
4271
|
|
|
4099
4272
|
Examples:
|
|
4100
4273
|
raggrep query "user authentication"
|
|
@@ -4105,7 +4278,7 @@ Examples:
|
|
|
4105
4278
|
process.exit(0);
|
|
4106
4279
|
}
|
|
4107
4280
|
const { search: search2, formatSearchResults: formatSearchResults2 } = await Promise.resolve().then(() => (init_search(), exports_search));
|
|
4108
|
-
const {
|
|
4281
|
+
const { ensureIndexFresh: ensureIndexFresh2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
|
|
4109
4282
|
const query = flags.remaining[0];
|
|
4110
4283
|
if (!query) {
|
|
4111
4284
|
console.error("Usage: raggrep query <search query>");
|
|
@@ -4113,24 +4286,20 @@ Examples:
|
|
|
4113
4286
|
process.exit(1);
|
|
4114
4287
|
}
|
|
4115
4288
|
try {
|
|
4116
|
-
const
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
model: flags.model,
|
|
4125
|
-
verbose: false
|
|
4126
|
-
});
|
|
4127
|
-
console.log(`
|
|
4128
|
-
================`);
|
|
4129
|
-
console.log("Summary:");
|
|
4130
|
-
for (const result of indexResults) {
|
|
4131
|
-
console.log(` ${result.moduleId}: ${result.indexed} indexed, ${result.skipped} skipped, ${result.errors} errors`);
|
|
4289
|
+
const freshStats = await ensureIndexFresh2(process.cwd(), {
|
|
4290
|
+
model: flags.model,
|
|
4291
|
+
quiet: true
|
|
4292
|
+
});
|
|
4293
|
+
if (freshStats.indexed > 0 || freshStats.removed > 0) {
|
|
4294
|
+
const parts = [];
|
|
4295
|
+
if (freshStats.indexed > 0) {
|
|
4296
|
+
parts.push(`${freshStats.indexed} indexed`);
|
|
4132
4297
|
}
|
|
4133
|
-
|
|
4298
|
+
if (freshStats.removed > 0) {
|
|
4299
|
+
parts.push(`${freshStats.removed} removed`);
|
|
4300
|
+
}
|
|
4301
|
+
console.log(`Index updated: ${parts.join(", ")}
|
|
4302
|
+
`);
|
|
4134
4303
|
}
|
|
4135
4304
|
console.log("RAGgrep Search");
|
|
4136
4305
|
console.log(`==============
|
|
@@ -4286,4 +4455,4 @@ Run 'raggrep <command> --help' for more information.
|
|
|
4286
4455
|
}
|
|
4287
4456
|
main();
|
|
4288
4457
|
|
|
4289
|
-
//# debugId=
|
|
4458
|
+
//# debugId=4B6F0EA9EEB7164864756E2164756E21
|