@lucas-bur/pix 0.4.0 → 0.5.1
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 +2 -1
- package/dist/index.mjs +56 -51
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# @lucas-bur/pix
|
|
2
2
|
|
|
3
3
|
[](https://github.com/Lucas-Bur/pix/actions/workflows/ci.yml)
|
|
4
|
-
[](https://codecov.io/gh/Lucas-Bur/pix)
|
|
5
|
+
[](https://github.com/Lucas-Bur/pix/actions/workflows/ci.yml)
|
|
5
6
|
[](https://www.npmjs.com/package/@lucas-bur/pix)
|
|
6
7
|
[](https://www.npmjs.com/package/@lucas-bur/pix)
|
|
7
8
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
4
|
-
import { Clock, Context, Data, Effect, Layer, Option } from "effect";
|
|
4
|
+
import { Clock, Console, Context, Data, Effect, Layer, Option } from "effect";
|
|
5
5
|
import { Args, CliConfig, Command, Options } from "@effect/cli";
|
|
6
6
|
import crypto from "node:crypto";
|
|
7
7
|
import { FileSystem } from "@effect/platform";
|
|
8
8
|
import { env } from "@huggingface/transformers";
|
|
9
|
-
import fg from "fast-glob";
|
|
10
9
|
import ignore from "ignore";
|
|
11
10
|
//#region src/domain/ports.ts
|
|
12
11
|
var ConfigStore = class extends Context.Tag("ConfigStore")() {};
|
|
@@ -166,30 +165,22 @@ const indexCommand = Command.make("index", {
|
|
|
166
165
|
const result = yield* IndexProject.index().pipe(Effect.either);
|
|
167
166
|
if (result._tag === "Left") return yield* Effect.fail(result.left);
|
|
168
167
|
const duration = `${((Date.now() - startTime) / 1e3).toFixed(1)}s`;
|
|
169
|
-
if (json) return yield*
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}));
|
|
175
|
-
});
|
|
168
|
+
if (json) return yield* Console.log(JSON.stringify({
|
|
169
|
+
chunks: result.right.stats.chunks,
|
|
170
|
+
files: result.right.stats.files,
|
|
171
|
+
duration
|
|
172
|
+
}));
|
|
176
173
|
yield* Effect.logInfo(`Indexed ${result.right.stats.chunks} chunks from ${result.right.stats.files} files in ${duration}.`);
|
|
177
|
-
}).pipe(Effect.tapError((error) =>
|
|
178
|
-
console.log(formatError(error));
|
|
179
|
-
}))));
|
|
174
|
+
}).pipe(Effect.tapError((error) => Console.log(formatError(error)))));
|
|
180
175
|
//#endregion
|
|
181
176
|
//#region src/commands/init.ts
|
|
182
177
|
/** CLI command: pix init [--json] */
|
|
183
178
|
const initCommand = Command.make("init", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, ({ json }) => Effect.gen(function* () {
|
|
184
179
|
const result = yield* InitProject.init();
|
|
185
|
-
if (json) return yield*
|
|
186
|
-
console.log(JSON.stringify(result, null, 2));
|
|
187
|
-
});
|
|
180
|
+
if (json) return yield* Console.log(JSON.stringify(result, null, 2));
|
|
188
181
|
yield* Effect.logInfo("Created .pix/config.json with default settings.");
|
|
189
182
|
yield* Effect.logInfo("Reminder: Add `.pix` to your `.gitignore` file to avoid committing the index.");
|
|
190
|
-
}).pipe(Effect.tapError((error) =>
|
|
191
|
-
console.log(formatError(error));
|
|
192
|
-
}))));
|
|
183
|
+
}).pipe(Effect.tapError((error) => Console.log(formatError(error)))));
|
|
193
184
|
//#endregion
|
|
194
185
|
//#region src/commands/query.ts
|
|
195
186
|
const DEFAULT_TOP_K = 5;
|
|
@@ -229,7 +220,7 @@ const queryCommand = Command.make("query", {
|
|
|
229
220
|
const clamped = clampTopK(topK);
|
|
230
221
|
if (clamped.clamped) yield* Effect.logDebug(`topK clamped from ${topK} to ${clamped.value}`);
|
|
231
222
|
const results = yield* QueryProject.queryProject(queryText, clamped.value);
|
|
232
|
-
if (json)
|
|
223
|
+
if (json) {
|
|
233
224
|
const output = results.map((r) => ({
|
|
234
225
|
score: r.score,
|
|
235
226
|
file: r.file,
|
|
@@ -239,19 +230,17 @@ const queryCommand = Command.make("query", {
|
|
|
239
230
|
...ctxLines > 0 && r.contextBefore && { contextBefore: r.contextBefore },
|
|
240
231
|
...ctxLines > 0 && r.contextAfter && { contextAfter: r.contextAfter }
|
|
241
232
|
}));
|
|
242
|
-
|
|
243
|
-
}
|
|
233
|
+
return yield* Console.log(JSON.stringify(output, null, 2));
|
|
234
|
+
}
|
|
244
235
|
if (results.length === 0) {
|
|
245
236
|
yield* Effect.logInfo("No results found");
|
|
246
237
|
return;
|
|
247
238
|
}
|
|
248
|
-
for (const result of results)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
}).pipe(Effect.tapError((error) =>
|
|
253
|
-
console.log(formatError(error));
|
|
254
|
-
}))));
|
|
239
|
+
for (const result of results) {
|
|
240
|
+
yield* Console.log(formatResult(result));
|
|
241
|
+
yield* Console.log("---");
|
|
242
|
+
}
|
|
243
|
+
}).pipe(Effect.tapError((error) => Console.log(formatError(error)))));
|
|
255
244
|
//#endregion
|
|
256
245
|
//#region src/lib/format.ts
|
|
257
246
|
/** Format byte count as human-readable string (e.g. "1.5 MB") */
|
|
@@ -273,15 +262,13 @@ const resetCommand = Command.make("reset", { json: Options.boolean("json").pipe(
|
|
|
273
262
|
const start = yield* Clock.currentTimeMillis;
|
|
274
263
|
const result = yield* ResetIndex.reset();
|
|
275
264
|
const elapsedMs = (yield* Clock.currentTimeMillis) - start;
|
|
276
|
-
if (json) return yield*
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}));
|
|
284
|
-
});
|
|
265
|
+
if (json) return yield* Console.log(JSON.stringify({
|
|
266
|
+
status: "ok",
|
|
267
|
+
deletedChunks: result.deletedChunks,
|
|
268
|
+
deletedVectors: result.deletedVectors,
|
|
269
|
+
freedBytes: result.freedBytes,
|
|
270
|
+
elapsedMs
|
|
271
|
+
}));
|
|
285
272
|
if (!result.deletedChunks && !result.deletedVectors) {
|
|
286
273
|
yield* Effect.logInfo("Nothing to reset.");
|
|
287
274
|
return;
|
|
@@ -292,26 +279,20 @@ const resetCommand = Command.make("reset", { json: Options.boolean("json").pipe(
|
|
|
292
279
|
yield* Effect.logInfo(`Deleted: ${parts.join(", ")}`);
|
|
293
280
|
yield* Effect.logInfo(`Freed: ${formatBytes(result.freedBytes)}`);
|
|
294
281
|
yield* Effect.logInfo(`Time: ${elapsedMs}ms`);
|
|
295
|
-
}).pipe(Effect.tapError((error) =>
|
|
296
|
-
console.log(formatError(error));
|
|
297
|
-
}))));
|
|
282
|
+
}).pipe(Effect.tapError((error) => Console.log(formatError(error)))));
|
|
298
283
|
//#endregion
|
|
299
284
|
//#region src/commands/status.ts
|
|
300
285
|
/** CLI command: pix status [--json] */
|
|
301
286
|
const statusCommand = Command.make("status", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, ({ json }) => Effect.gen(function* () {
|
|
302
287
|
const result = yield* GetStatus.getStatus();
|
|
303
|
-
if (json) return yield*
|
|
304
|
-
console.log(JSON.stringify(result, null, 2));
|
|
305
|
-
});
|
|
288
|
+
if (json) return yield* Console.log(JSON.stringify(result, null, 2));
|
|
306
289
|
const lastIndexStr = result.lastIndex > 0 ? new Date(result.lastIndex).toISOString() : "never";
|
|
307
290
|
yield* Effect.logInfo(`Indexed: ${result.chunks} chunks across ${result.files} files`);
|
|
308
291
|
yield* Effect.logInfo(`Model: ${result.model || "none"}`);
|
|
309
292
|
yield* Effect.logInfo(`Total lines: ${result.totalLines.toLocaleString()}`);
|
|
310
293
|
yield* Effect.logInfo(`Index size: ${formatBytes(result.byteSize)}`);
|
|
311
294
|
yield* Effect.logInfo(`Last indexed: ${lastIndexStr}`);
|
|
312
|
-
}).pipe(Effect.tapError((error) =>
|
|
313
|
-
console.log(formatError(error));
|
|
314
|
-
}))));
|
|
295
|
+
}).pipe(Effect.tapError((error) => Console.log(formatError(error)))));
|
|
315
296
|
//#endregion
|
|
316
297
|
//#region src/cli.ts
|
|
317
298
|
const VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
@@ -462,9 +443,16 @@ const make$2 = Effect.gen(function* () {
|
|
|
462
443
|
const OnnxEmbedderLive = Layer.effect(Embedder, make$2);
|
|
463
444
|
//#endregion
|
|
464
445
|
//#region src/services/scanner.ts
|
|
446
|
+
const ALWAYS_IGNORE = new Set([
|
|
447
|
+
".pix",
|
|
448
|
+
"node_modules",
|
|
449
|
+
".git",
|
|
450
|
+
"dist",
|
|
451
|
+
"build",
|
|
452
|
+
".next"
|
|
453
|
+
]);
|
|
465
454
|
const make$1 = Effect.gen(function* () {
|
|
466
455
|
const fs = yield* FileSystem.FileSystem;
|
|
467
|
-
/** Loads all gitignore patterns from .gitignore files in the repo. */
|
|
468
456
|
const loadGitignoreRules = Effect.gen(function* () {
|
|
469
457
|
const ig = ignore();
|
|
470
458
|
const cwd = process.cwd();
|
|
@@ -477,13 +465,30 @@ const make$1 = Effect.gen(function* () {
|
|
|
477
465
|
}
|
|
478
466
|
return ig;
|
|
479
467
|
}).pipe(Effect.catchAll(() => Effect.succeed(ignore())));
|
|
468
|
+
const walk = (dir, extensions) => Effect.gen(function* () {
|
|
469
|
+
const entries = yield* fs.readDirectory(dir).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
470
|
+
let results = [];
|
|
471
|
+
for (const entry of entries) {
|
|
472
|
+
if (ALWAYS_IGNORE.has(entry)) continue;
|
|
473
|
+
const fullPath = `${dir}/${entry}`;
|
|
474
|
+
const info = yield* fs.stat(fullPath).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
475
|
+
if (!info) continue;
|
|
476
|
+
if (info.type === "Directory") {
|
|
477
|
+
const subResults = yield* walk(fullPath, extensions);
|
|
478
|
+
results.push(...subResults);
|
|
479
|
+
} else if (info.type === "File") {
|
|
480
|
+
const dotIndex = entry.lastIndexOf(".");
|
|
481
|
+
if (dotIndex === -1) continue;
|
|
482
|
+
const ext = entry.slice(dotIndex);
|
|
483
|
+
if (extensions.has(ext)) results.push(fullPath);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return results;
|
|
487
|
+
});
|
|
480
488
|
const scanFiles = (extensions) => Effect.gen(function* () {
|
|
481
489
|
const ig = yield* loadGitignoreRules;
|
|
482
490
|
const cwd = process.cwd();
|
|
483
|
-
const
|
|
484
|
-
const relativePaths = (yield* Effect.tryPromise(() => fg(pattern, { dot: false })).pipe(Effect.catchAll(() => Effect.succeed([])))).map((p) => {
|
|
485
|
-
return p.startsWith(cwd) ? p.slice(cwd.length + 1) : p;
|
|
486
|
-
});
|
|
491
|
+
const relativePaths = (yield* walk(cwd, new Set(extensions))).map((p) => p.startsWith(cwd) ? p.slice(cwd.length + 1) : p);
|
|
487
492
|
return ig.filter(relativePaths).map((p) => `${cwd}/${p}`);
|
|
488
493
|
});
|
|
489
494
|
return { scanFiles };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucas-bur/pix",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Lightweight local semantic project indexer",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -39,8 +39,14 @@
|
|
|
39
39
|
"test": "vp test",
|
|
40
40
|
"test:coverage": "vp test --coverage",
|
|
41
41
|
"check": "vp check",
|
|
42
|
-
"ci": "vp check && vp test
|
|
43
|
-
"lint:
|
|
42
|
+
"ci": "vp run check && vp run test:coverage && vp run build && vp run lint:effect && vp run lint:fallow",
|
|
43
|
+
"lint:effect": "effect-language-service diagnostics --project tsconfig.json --format pretty --strict",
|
|
44
|
+
"lint:effect:ci": "effect-language-service diagnostics --project tsconfig.json --format github-actions --strict",
|
|
45
|
+
"lint:effect:agent": "effect-language-service diagnostics --project tsconfig.json --format json --strict",
|
|
46
|
+
"lint:fallow": "fallow",
|
|
47
|
+
"lint:fallow:ci": "vpx fallow audit --base origin/main",
|
|
48
|
+
"lint:fallow:health": "fallow health --format badge",
|
|
49
|
+
"lint:fallow:agent": "fallow --format json",
|
|
44
50
|
"fallow": "fallow",
|
|
45
51
|
"prepublishOnly": "vp run build",
|
|
46
52
|
"pix": ""
|
|
@@ -51,7 +57,6 @@
|
|
|
51
57
|
"@effect/platform-node": "^0.106.0",
|
|
52
58
|
"@huggingface/transformers": "^4.2.0",
|
|
53
59
|
"effect": "^3.21.2",
|
|
54
|
-
"fast-glob": "^3.3.3",
|
|
55
60
|
"ignore": "^7.0.5"
|
|
56
61
|
},
|
|
57
62
|
"devDependencies": {
|