@lucas-bur/pix 0.9.0 → 0.9.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.
Files changed (2) hide show
  1. package/dist/index.mjs +233 -93
  2. package/package.json +2 -1
package/dist/index.mjs CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import { NodeContext, NodeRuntime } from "@effect/platform-node";
4
- import { Clock, Console, Context, Data, Effect, Layer, Option } from "effect";
5
3
  import { Args, CliConfig, Command, Options } from "@effect/cli";
4
+ import { NodeContext, NodeRuntime } from "@effect/platform-node";
5
+ import { Clock, Context, Data, Effect, Layer, Option, Ref } from "effect";
6
+ import { styleText } from "node:util";
7
+ import * as clack from "@clack/prompts";
6
8
  import crypto from "node:crypto";
7
9
  import { FileSystem } from "@effect/platform";
8
10
  import { env } from "@huggingface/transformers";
@@ -32,6 +34,104 @@ var GetStatus = class extends Effect.Service()("GetStatus", {
32
34
  return { getStatus };
33
35
  })
34
36
  }) {};
37
+ Data.taggedEnum();
38
+ /** Display context tag — commands use `yield* Display` to produce output */
39
+ var Display = class extends Context.Tag("Display")() {};
40
+ /** Maps severity to the corresponding @clack/prompts log function */
41
+ const severityToClack = {
42
+ info: clack.log.info,
43
+ success: clack.log.success,
44
+ warn: clack.log.warning,
45
+ error: clack.log.error
46
+ };
47
+ /** Styling helpers using node:util styleText (zero-deps, Node 21+) */
48
+ const terminalStyle = {
49
+ status: (message) => styleText("bold", message),
50
+ dim: (message) => styleText("dim", message)
51
+ };
52
+ /** Extract the message text from an UpdateInteractivePayload */
53
+ const payloadText = (p) => typeof p === "string" ? p : p.message;
54
+ /**
55
+ * Compute the delta for a progress bar from the payload + current state. Returns 0 if there is no
56
+ * numeric payload or if the active element is a spinner.
57
+ */
58
+ const computeDelta = (p, state) => {
59
+ if (typeof p === "string") return 0;
60
+ if ("advanceBy" in p && p.advanceBy !== void 0) return Math.max(-state.value, p.advanceBy);
61
+ if ("setTo" in p && p.setTo !== void 0) return Math.max(0, Math.min(state.max, p.setTo)) - state.value;
62
+ if ("setToPercent" in p && p.setToPercent !== void 0) {
63
+ const target = Math.floor(state.max * p.setToPercent / 100);
64
+ return Math.max(-state.value, Math.min(state.max - state.value, target - state.value));
65
+ }
66
+ return 0;
67
+ };
68
+ /**
69
+ * Extracts the "guarded interactive" pattern: skip if already active, otherwise
70
+ * acquire-use-release.
71
+ */
72
+ const withInteractive = (activeRef, acquire, setActive, release, effect) => Ref.get(activeRef).pipe(Effect.flatMap((current) => current !== null ? effect : Effect.acquireUseRelease(acquire.pipe(Effect.tap((h) => Ref.set(activeRef, setActive(h)))), () => effect, (h, exit) => Ref.set(activeRef, null).pipe(Effect.andThen(release(h, exit))))));
73
+ /** Display implementation using @clack/prompts for interactive terminal output */
74
+ const ClackDisplay = { layer: Layer.effect(Display, Effect.gen(function* () {
75
+ const activeRef = yield* Ref.make(null);
76
+ return {
77
+ intro: (title) => Effect.sync(() => clack.intro(styleText("inverse", ` ${title} `))),
78
+ outro: (message) => Effect.sync(() => clack.outro(message)),
79
+ log: (message, severity) => Effect.sync(() => severityToClack[severity](terminalStyle.status(message))),
80
+ note: (content, title) => Effect.sync(() => clack.note(content, title)),
81
+ text: (message) => Effect.sync(() => clack.log.message(message)),
82
+ spinner: (message, effect) => withInteractive(activeRef, Effect.sync(() => {
83
+ const s = clack.spinner();
84
+ s.start(message);
85
+ return s;
86
+ }), (s) => ({
87
+ type: "spinner",
88
+ handle: s
89
+ }), (s, exit) => Effect.sync(() => s.stop(exit._tag === "Success" ? message : `${message} (failed)`)), effect),
90
+ progress: (opts, effect) => withInteractive(activeRef, Effect.sync(() => {
91
+ const bar = clack.progress({
92
+ max: opts.max,
93
+ style: opts.style ?? "heavy",
94
+ size: opts.size ?? 40,
95
+ indicator: opts.indicator ?? "dots"
96
+ });
97
+ bar.start(opts.message);
98
+ return bar;
99
+ }), (bar) => ({
100
+ type: "progress",
101
+ handle: bar,
102
+ value: 0,
103
+ max: opts.max
104
+ }), (bar, exit) => Effect.sync(() => exit._tag === "Success" ? bar.stop(opts.message) : bar.error(opts.message)), effect),
105
+ updateInteractive: (payload) => Ref.get(activeRef).pipe(Effect.flatMap((active) => {
106
+ if (!active) return Effect.void;
107
+ if (active.type === "spinner") return Effect.sync(() => active.handle.message(payloadText(payload)));
108
+ const delta = computeDelta(payload, {
109
+ value: active.value,
110
+ max: active.max
111
+ });
112
+ const newValue = Math.max(0, Math.min(active.max, active.value + delta));
113
+ return Effect.sync(() => {
114
+ active.handle.advance(delta, payloadText(payload));
115
+ }).pipe(Effect.andThen(Ref.update(activeRef, (current) => current && current.type === "progress" ? {
116
+ ...current,
117
+ value: newValue
118
+ } : current)));
119
+ })),
120
+ json: () => Effect.void
121
+ };
122
+ })) };
123
+ /** Display implementation for --json mode — no-ops interactive methods, writes JSON to stdout */
124
+ const JsonDisplay = { layer: Layer.succeed(Display, {
125
+ intro: () => Effect.void,
126
+ outro: () => Effect.void,
127
+ log: () => Effect.void,
128
+ note: () => Effect.void,
129
+ text: () => Effect.void,
130
+ spinner: (_message, effect) => effect,
131
+ progress: (_opts, effect) => effect,
132
+ updateInteractive: () => Effect.void,
133
+ json: (data) => Effect.sync(() => process.stdout.write(`${JSON.stringify(data)}\n`))
134
+ }) };
35
135
  //#endregion
36
136
  //#region src/domain/config.ts
37
137
  var ConfigError = class extends Data.TaggedError("ConfigError") {};
@@ -51,7 +151,7 @@ const DEFAULT_CONFIG = {
51
151
  //#region src/application/index-project.ts
52
152
  /**
53
153
  * Use case: index project files. Pipeline: scan → chunk → embed → store. Depends on ConfigStore,
54
- * Scanner, Chunker, Embedder, VectorStore via Effect tags.
154
+ * Scanner, Chunker, Embedder, VectorStore, Display via Effect tags.
55
155
  */
56
156
  var IndexProject = class extends Effect.Service()("IndexProject", {
57
157
  accessors: true,
@@ -61,6 +161,7 @@ var IndexProject = class extends Effect.Service()("IndexProject", {
61
161
  const chunker = yield* Chunker;
62
162
  const embedder = yield* Embedder;
63
163
  const vectorStore = yield* VectorStore;
164
+ const d = yield* Display;
64
165
  const index = () => Effect.gen(function* () {
65
166
  if (!(yield* configStore.configExists())) yield* configStore.writeConfig(DEFAULT_CONFIG);
66
167
  const config = yield* configStore.readConfig();
@@ -70,23 +171,23 @@ var IndexProject = class extends Effect.Service()("IndexProject", {
70
171
  ".js",
71
172
  ".jsx"
72
173
  ];
174
+ yield* d.updateInteractive("Scanning source files...");
73
175
  const scanResult = yield* scanner.scanFiles(extensions);
176
+ yield* d.updateInteractive(`Chunking ${scanResult.files.length} files...`);
74
177
  const allChunks = (yield* Effect.forEach(scanResult.files, (file) => chunker.chunkFile(file), { concurrency: Math.max(1, config.chunkConcurrency ?? 8) })).flat();
75
178
  const totalChunks = allChunks.length;
76
179
  const totalFiles = new Set(allChunks.map((c) => c.file)).size;
77
180
  const totalLines = allChunks.reduce((sum, c) => sum + (c.endLine - c.startLine + 1), 0);
78
- if (totalChunks === 0) {
79
- yield* Effect.logInfo("No chunks to index.");
80
- return {
81
- success: true,
82
- status: {
83
- chunks: 0,
84
- files: 0,
85
- totalLines: 0,
86
- byteSize: 0
87
- }
88
- };
89
- }
181
+ if (totalChunks === 0) return {
182
+ success: true,
183
+ status: {
184
+ chunks: 0,
185
+ files: 0,
186
+ totalLines: 0,
187
+ byteSize: 0
188
+ }
189
+ };
190
+ yield* d.updateInteractive(`Embedding ${totalChunks} chunks...`);
90
191
  const texts = allChunks.map((c) => c.text);
91
192
  const embeddings = yield* embedder.batch(texts);
92
193
  yield* vectorStore.store(allChunks, embeddings);
@@ -183,41 +284,41 @@ const formatError = (error) => JSON.stringify({
183
284
  message: messageFromError(error),
184
285
  cause: causeFromError(error)
185
286
  });
186
- /** Log the error as JSON to stdout, then re-fail to preserve non-zero exit code. */
187
- const reportError = (error) => Console.log(formatError(error)).pipe(Effect.flatMap(() => Effect.fail(error)));
287
+ /** Log the error to Display in human + agent format, then re-fail to preserve non-zero exit code. */
288
+ const reportError = (error) => Effect.gen(function* () {
289
+ const d = yield* Display;
290
+ yield* d.log(`${codeFromError(error)}: ${messageFromError(error)}`, "error");
291
+ yield* d.json(JSON.parse(formatError(error)));
292
+ return yield* Effect.fail(error);
293
+ });
188
294
  //#endregion
189
295
  //#region src/commands/index-cmd.ts
190
- const logFlagWarnings = (force, verbose, json) => {
191
- if (json) return Effect.void;
192
- const warnings = [force ? "--force is currently not implemented and only a placeholder." : void 0, verbose ? "--verbose is currently not implemented and only a placeholder." : void 0].filter((msg) => msg !== void 0);
193
- return Effect.forEach(warnings, (msg) => Effect.logInfo(msg), { discard: true });
194
- };
195
- const logHumanOutput = (chunks, files, duration) => Effect.logInfo(`Indexed ${chunks} chunks from ${files} files in ${duration}.`);
196
296
  /** CLI command: pix index [--force] [--verbose] [--json] */
197
297
  const indexCommand = Command.make("index", {
198
298
  force: Options.boolean("force").pipe(Options.withDefault(false)),
199
299
  verbose: Options.boolean("verbose").pipe(Options.withDefault(false)),
200
300
  json: Options.boolean("json").pipe(Options.withDefault(false))
201
- }, ({ force, verbose, json }) => Effect.gen(function* () {
202
- yield* logFlagWarnings(force, verbose, json);
203
- const startTime = Date.now();
204
- const result = yield* IndexProject.index();
205
- const duration = `${((Date.now() - startTime) / 1e3).toFixed(1)}s`;
206
- if (json) return yield* Console.log(JSON.stringify({
301
+ }, ({ force, verbose }) => Effect.gen(function* () {
302
+ const d = yield* Display;
303
+ if (force) yield* d.log("--force is currently not implemented and only a placeholder.", "warn");
304
+ if (verbose) yield* d.log("--verbose is currently not implemented and only a placeholder.", "warn");
305
+ const result = yield* d.spinner("Indexing project...", IndexProject.index());
306
+ yield* d.json({
207
307
  chunks: result.status.chunks,
208
- files: result.status.files,
209
- duration
210
- }));
211
- yield* logHumanOutput(result.status.chunks, result.status.files, duration);
308
+ files: result.status.files
309
+ });
310
+ if (result.status.chunks === 0) yield* d.log("No chunks to index.", "warn");
311
+ else yield* d.log(`Indexed ${result.status.chunks} chunks from ${result.status.files} files.`, "success");
212
312
  }).pipe(Effect.catchAll(reportError)));
213
313
  //#endregion
214
314
  //#region src/commands/init.ts
215
315
  /** CLI command: pix init [--json] */
216
- const initCommand = Command.make("init", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, ({ json }) => Effect.gen(function* () {
217
- const result = yield* InitProject.init();
218
- if (json) return yield* Console.log(JSON.stringify(result, null, 2));
219
- yield* Effect.logInfo("Created .pix/config.json with default settings.");
220
- yield* Effect.logInfo("Reminder: Add `.pix` to your `.gitignore` file to avoid committing the index.");
316
+ const initCommand = Command.make("init", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, () => Effect.gen(function* () {
317
+ const d = yield* Display;
318
+ const result = yield* d.spinner("Initializing...", InitProject.init());
319
+ yield* d.json(result);
320
+ yield* d.log("Created .pix/config.json with default settings.", "success");
321
+ yield* d.note("Add `.pix` to your `.gitignore` file to avoid committing the index.", "Reminder");
221
322
  }).pipe(Effect.catchTags({
222
323
  ConfigError: reportError,
223
324
  DiskFullError: reportError
@@ -258,30 +359,22 @@ const toJsonOutput = (results, ctxLines) => results.map((r) => ({
258
359
  ...ctxLines > 0 && r.contextBefore && { contextBefore: r.contextBefore },
259
360
  ...ctxLines > 0 && r.contextAfter && { contextAfter: r.contextAfter }
260
361
  }));
261
- const renderResults = (results) => Effect.gen(function* () {
262
- if (results.length === 0) {
263
- yield* Effect.logInfo("No results found");
264
- return;
265
- }
266
- for (const result of results) {
267
- yield* Console.log(formatResult(result));
268
- yield* Console.log("---");
269
- }
270
- });
271
362
  /** CLI command: pix query "<text>" [--top N] [--json] [--context-lines N] */
272
363
  const queryCommand = Command.make("query", {
273
364
  queryText: Args.text({ name: "query" }),
274
365
  top: Options.integer("top").pipe(Options.withDefault(DEFAULT_TOP_K), Options.optional),
275
366
  json: Options.boolean("json").pipe(Options.withDefault(false)),
276
367
  contextLines: Options.integer("context-lines").pipe(Options.withDefault(DEFAULT_CONTEXT_LINES), Options.optional)
277
- }, ({ queryText, top, json, contextLines }) => Effect.gen(function* () {
368
+ }, ({ queryText, top, contextLines }) => Effect.gen(function* () {
369
+ const d = yield* Display;
278
370
  const topK = Option.getOrElse(top, () => DEFAULT_TOP_K);
279
371
  const ctxLines = Option.getOrElse(contextLines, () => DEFAULT_CONTEXT_LINES);
280
372
  const clamped = clampTopK(topK);
281
- if (clamped.clamped && !json) yield* Effect.logDebug(`topK clamped from ${topK} to ${clamped.value}`);
282
- const results = yield* QueryProject.queryProject(queryText, clamped.value);
283
- if (json) return yield* Console.log(JSON.stringify(toJsonOutput(results, ctxLines), null, 2));
284
- yield* renderResults(results);
373
+ if (clamped.clamped) yield* d.log(`topK clamped from ${topK} to ${clamped.value}`, "warn");
374
+ const results = yield* d.spinner("Searching...", QueryProject.queryProject(queryText, clamped.value));
375
+ yield* d.json(toJsonOutput(results, ctxLines));
376
+ if (results.length === 0) yield* d.log("No results found", "warn");
377
+ else for (const result of results) yield* d.text(formatResult(result));
285
378
  }).pipe(Effect.catchTags({
286
379
  ModelLoadError: reportError,
287
380
  InferenceError: reportError,
@@ -305,30 +398,26 @@ const formatBytes = (bytes) => {
305
398
  };
306
399
  //#endregion
307
400
  //#region src/commands/reset.ts
308
- const logJsonResult = (result, elapsedMs) => Console.log(JSON.stringify({
309
- status: "ok",
310
- deletedChunks: result.deletedChunks,
311
- deletedVectors: result.deletedVectors,
312
- freedBytes: result.freedBytes,
313
- elapsedMs
314
- }));
315
- const logHumanResult = (result, elapsedMs) => Effect.gen(function* () {
316
- const deletedParts = [result.deletedChunks ? "chunks.jsonl" : null, result.deletedVectors ? "vectors.bin" : null].filter((part) => part !== null);
317
- if (deletedParts.length === 0) {
318
- yield* Effect.logInfo("Nothing to reset.");
319
- return;
320
- }
321
- yield* Effect.logInfo(`Deleted: ${deletedParts.join(", ")}`);
322
- yield* Effect.logInfo(`Freed: ${formatBytes(result.freedBytes)}`);
323
- yield* Effect.logInfo(`Time: ${elapsedMs}ms`);
324
- });
325
401
  /** CLI command: pix reset [--json] */
326
- const resetCommand = Command.make("reset", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, ({ json }) => Effect.gen(function* () {
402
+ const resetCommand = Command.make("reset", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, () => Effect.gen(function* () {
403
+ const d = yield* Display;
327
404
  const start = yield* Clock.currentTimeMillis;
328
- const result = yield* ResetIndex.reset();
405
+ const result = yield* d.spinner("Resetting index...", ResetIndex.reset());
329
406
  const elapsedMs = (yield* Clock.currentTimeMillis) - start;
330
- if (json) return yield* logJsonResult(result, elapsedMs);
331
- yield* logHumanResult(result, elapsedMs);
407
+ yield* d.json({
408
+ status: "ok",
409
+ deletedChunks: result.deletedChunks,
410
+ deletedVectors: result.deletedVectors,
411
+ freedBytes: result.freedBytes,
412
+ elapsedMs
413
+ });
414
+ const deletedParts = [result.deletedChunks ? "chunks.jsonl" : null, result.deletedVectors ? "vectors.bin" : null].filter((part) => part !== null);
415
+ if (deletedParts.length === 0) yield* d.log("Nothing to reset.", "info");
416
+ else {
417
+ yield* d.log(`Deleted: ${deletedParts.join(", ")}`, "success");
418
+ yield* d.log(`Freed: ${formatBytes(result.freedBytes)}`, "info");
419
+ yield* d.log(`Time: ${elapsedMs}ms`, "info");
420
+ }
332
421
  }).pipe(Effect.catchTags({
333
422
  DiskFullError: reportError,
334
423
  StoreError: reportError
@@ -336,22 +425,22 @@ const resetCommand = Command.make("reset", { json: Options.boolean("json").pipe(
336
425
  //#endregion
337
426
  //#region src/commands/status.ts
338
427
  /** CLI command: pix status [--json] */
339
- const statusCommand = Command.make("status", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, ({ json }) => Effect.gen(function* () {
428
+ const statusCommand = Command.make("status", { json: Options.boolean("json").pipe(Options.withDefault(false)) }, () => Effect.gen(function* () {
429
+ const d = yield* Display;
340
430
  const result = yield* GetStatus.getStatus();
341
- if (json) return yield* Console.log(JSON.stringify(result, null, 2));
342
- const lastIndexStr = result.lastIndex > 0 ? new Date(result.lastIndex).toISOString() : "never";
343
- yield* Effect.logInfo(`Indexed: ${result.chunks} chunks across ${result.files} files`);
344
- yield* Effect.logInfo(`Model: ${result.model || "none"}`);
345
- yield* Effect.logInfo(`Total lines: ${result.totalLines.toLocaleString()}`);
346
- yield* Effect.logInfo(`Index size: ${formatBytes(result.byteSize)}`);
347
- yield* Effect.logInfo(`Last indexed: ${lastIndexStr}`);
431
+ yield* d.json(result);
432
+ const lastIndexStr = result.lastIndex > 0 ? new Date(result.lastIndex).toLocaleString() : "never";
433
+ yield* d.log(`Indexed: ${result.chunks} chunks across ${result.files} files`, "info");
434
+ yield* d.log(`Model: ${result.model || "none"}`, "info");
435
+ yield* d.log(`Total lines: ${result.totalLines.toLocaleString()}`, "info");
436
+ yield* d.log(`Index size: ${result.byteSize.toLocaleString()} bytes`, "info");
437
+ yield* d.log(`Last indexed: ${lastIndexStr}`, "info");
348
438
  }).pipe(Effect.catchTags({ StoreError: reportError })));
349
439
  //#endregion
350
440
  //#region src/cli.ts
351
441
  const VERSION = createRequire(import.meta.url)("../package.json").version;
352
442
  const pix = Command.make("pix", {}, () => Effect.gen(function* () {
353
- yield* Effect.logInfo("pix - Lightweight local semantic project indexer");
354
- yield* Effect.logInfo("Use `pix --help` to see available commands.");
443
+ yield* (yield* Display).log(`pix v${VERSION} - Lightweight local semantic project indexer`, "info");
355
444
  })).pipe(Command.withSubcommands([
356
445
  initCommand,
357
446
  statusCommand,
@@ -359,10 +448,50 @@ const pix = Command.make("pix", {}, () => Effect.gen(function* () {
359
448
  queryCommand,
360
449
  resetCommand
361
450
  ]));
362
- const cli = (args) => Command.run(pix, {
363
- name: "pix",
364
- version: VERSION
365
- })(args).pipe(Effect.provide(CliConfig.layer({ showTypes: false })));
451
+ const cli = (args) => {
452
+ const displayLayer = args.some((a) => a === "--json") ? JsonDisplay.layer : ClackDisplay.layer;
453
+ return {
454
+ effect: Command.run(pix, {
455
+ name: "pix",
456
+ version: VERSION
457
+ })(args),
458
+ displayLayer
459
+ };
460
+ };
461
+ //#endregion
462
+ //#region src/display/terminalCleanup.ts
463
+ /**
464
+ * Terminal cleanup for abrupt exits.
465
+ *
466
+ * @clack/prompts' spinner and taskLog call stdin.setRawMode(true) and hide
467
+ * the cursor via escape sequences. When the process is killed by a signal
468
+ * handler that calls process.exit() directly, clack's own cleanup is
469
+ * bypassed and the terminal is left in raw mode with the cursor hidden.
470
+ *
471
+ * Registering a process 'exit' listener that restores these guarantees the
472
+ * terminal is always left in a usable state.
473
+ */
474
+ /** Escape sequence to show the cursor (DECTCEM). */
475
+ const SHOW_CURSOR = "\x1B[?25h";
476
+ /**
477
+ * Creates a synchronous exit handler that restores terminal state. Extracted as a pure function so
478
+ * it can be unit-tested without side effects.
479
+ */
480
+ const makeTerminalCleanupHandler = (stdin, stdout) => () => {
481
+ if (stdin.isTTY && stdin.setRawMode) try {
482
+ stdin.setRawMode(false);
483
+ } catch {}
484
+ if (stdout.isTTY) try {
485
+ stdout.write(SHOW_CURSOR);
486
+ } catch {}
487
+ };
488
+ /**
489
+ * Registers the terminal cleanup handler on process 'exit'. Call once at program startup
490
+ * (index.ts).
491
+ */
492
+ const setupTerminalCleanup = () => {
493
+ process.on("exit", makeTerminalCleanupHandler(process.stdin, process.stdout));
494
+ };
366
495
  //#endregion
367
496
  //#region src/domain/errors.ts
368
497
  /** Config file or directory does not exist. Run pix init first. */
@@ -559,7 +688,13 @@ const createExtractor = (opts) => Effect.tryPromise(async () => {
559
688
  const createExtractorWithFallback = (opts) => {
560
689
  if (opts.device === "cpu") return createExtractor(opts);
561
690
  return createExtractor(opts).pipe(Effect.catchAll((originalError) => Effect.gen(function* () {
562
- yield* Effect.logWarning(`Embedding device "${opts.device}" failed, falling back to "cpu": ${originalError.message}`);
691
+ const d = yield* Display;
692
+ yield* d.log(`GPU (${opts.device}) failed, falling back to CPU...`, "warn");
693
+ yield* d.json({
694
+ event: "embedder_fallback",
695
+ originalDevice: opts.device,
696
+ reason: originalError.message
697
+ });
563
698
  return yield* createExtractor({
564
699
  ...opts,
565
700
  device: "cpu"
@@ -567,7 +702,9 @@ const createExtractorWithFallback = (opts) => {
567
702
  })));
568
703
  };
569
704
  const make$2 = Effect.gen(function* () {
570
- const cfg = yield* resolveEmbedderConfig(yield* ConfigStore);
705
+ const configStore = yield* ConfigStore;
706
+ const d = yield* Display;
707
+ const cfg = yield* resolveEmbedderConfig(configStore);
571
708
  const getExtractor = yield* Effect.cached(createExtractorWithFallback(cfg));
572
709
  const embed = (text) => Effect.gen(function* () {
573
710
  const extractor = yield* getExtractor;
@@ -582,7 +719,7 @@ const make$2 = Effect.gen(function* () {
582
719
  vector: normalize(data),
583
720
  dims: cfg.dims
584
721
  };
585
- });
722
+ }).pipe(Effect.provideService(Display, d));
586
723
  const batch = (texts) => Effect.gen(function* () {
587
724
  const extractor = yield* getExtractor;
588
725
  const results = [];
@@ -606,7 +743,7 @@ const make$2 = Effect.gen(function* () {
606
743
  vector,
607
744
  dims: cfg.dims
608
745
  }));
609
- });
746
+ }).pipe(Effect.provideService(Display, d));
610
747
  return {
611
748
  embed,
612
749
  batch
@@ -893,6 +1030,9 @@ const ChunkerLayer = ChunkerLive.pipe(Layer.provide(ServicesLayer));
893
1030
  const InfraLayer = Layer.mergeAll(ServicesLayer, ChunkerLayer).pipe(Layer.provide(NodeContext.layer));
894
1031
  const UseCaseLayer = Layer.mergeAll(InitProject.Default, GetStatus.Default, QueryProject.Default, IndexProject.Default, ResetIndex.Default);
895
1032
  const AppLayer = Layer.merge(UseCaseLayer.pipe(Layer.provide(InfraLayer)), NodeContext.layer);
896
- cli(process.argv).pipe(Effect.provide(AppLayer), NodeRuntime.runMain({ disableErrorReporting: true }));
1033
+ const { effect, displayLayer } = cli(process.argv);
1034
+ const cliLayer = Layer.mergeAll(displayLayer, CliConfig.layer({ showTypes: false }));
1035
+ setupTerminalCleanup();
1036
+ effect.pipe(Effect.provide(AppLayer.pipe(Layer.provideMerge(cliLayer))), NodeRuntime.runMain({ disableErrorReporting: true }));
897
1037
  //#endregion
898
1038
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucas-bur/pix",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Lightweight local semantic project indexer",
5
5
  "keywords": [
6
6
  "cli",
@@ -52,6 +52,7 @@
52
52
  "pix": ""
53
53
  },
54
54
  "dependencies": {
55
+ "@clack/prompts": "^1.4.0",
55
56
  "@effect/cli": "^0.75.1",
56
57
  "@effect/platform": "^0.96.1",
57
58
  "@effect/platform-node": "^0.106.0",