@ff-labs/pi-fff 0.7.3-nightly.ff81719 → 0.8.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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/index.ts +40 -79
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ff-labs/pi-fff",
3
3
  "public": true,
4
- "version": "0.7.3-nightly.ff81719",
4
+ "version": "0.8.0",
5
5
  "description": "pi extension: FFF-powered fuzzy file and content search",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -43,8 +43,8 @@
43
43
  "@ff-labs/fff-node": "*"
44
44
  },
45
45
  "peerDependencies": {
46
- "@mariozechner/pi-coding-agent": "*",
47
- "@mariozechner/pi-tui": "*",
46
+ "@earendil-works/pi-coding-agent": "*",
47
+ "@earendil-works/pi-tui": "*",
48
48
  "@sinclair/typebox": "*"
49
49
  },
50
50
  "devDependencies": {
package/src/index.ts CHANGED
@@ -5,13 +5,13 @@
5
5
  * @-mention autocomplete suggestions in the interactive editor.
6
6
  */
7
7
 
8
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
- import { CustomEditor } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
+ import { CustomEditor } from "@earendil-works/pi-coding-agent";
10
10
  import {
11
11
  Text,
12
12
  type AutocompleteItem,
13
13
  type AutocompleteProvider,
14
- } from "@mariozechner/pi-tui";
14
+ } from "@earendil-works/pi-tui";
15
15
  import { Type } from "@sinclair/typebox";
16
16
  import { FileFinder } from "@ff-labs/fff-node";
17
17
  import type {
@@ -256,9 +256,7 @@ function createFffMentionProvider(
256
256
 
257
257
  const query = prefix.startsWith('@"') ? prefix.slice(2) : prefix.slice(1);
258
258
  const items = await getItems(query, options.signal);
259
- return options.signal.aborted || items.length === 0
260
- ? null
261
- : { items, prefix };
259
+ return options.signal.aborted || items.length === 0 ? null : { items, prefix };
262
260
  },
263
261
  applyCompletion(_lines, cursorLine, cursorCol, item, prefix) {
264
262
  const currentLine = _lines[cursorLine] || "";
@@ -267,11 +265,7 @@ function createFffMentionProvider(
267
265
  const newLine = before + item.value + after;
268
266
  const newCursorCol = cursorCol - prefix.length + item.value.length;
269
267
  return {
270
- lines: [
271
- ..._lines.slice(0, cursorLine),
272
- newLine,
273
- ..._lines.slice(cursorLine + 1),
274
- ],
268
+ lines: [..._lines.slice(0, cursorLine), newLine, ..._lines.slice(cursorLine + 1)],
275
269
  cursorLine,
276
270
  cursorCol: newCursorCol,
277
271
  };
@@ -381,22 +375,20 @@ export default function fffExtension(pi: ExtensionAPI) {
381
375
  const result = f.mixedSearch(query, { pageSize: MENTION_MAX_RESULTS });
382
376
  if (!result.ok) return [];
383
377
 
384
- return result.value.items
385
- .slice(0, MENTION_MAX_RESULTS)
386
- .map((mixed: MixedItem) => {
387
- if (mixed.type === "directory") {
388
- return {
389
- value: buildAtCompletionValue(mixed.item.relativePath),
390
- label: mixed.item.dirName,
391
- description: mixed.item.relativePath,
392
- };
393
- }
378
+ return result.value.items.slice(0, MENTION_MAX_RESULTS).map((mixed: MixedItem) => {
379
+ if (mixed.type === "directory") {
394
380
  return {
395
381
  value: buildAtCompletionValue(mixed.item.relativePath),
396
- label: mixed.item.fileName,
382
+ label: mixed.item.dirName,
397
383
  description: mixed.item.relativePath,
398
384
  };
399
- });
385
+ }
386
+ return {
387
+ value: buildAtCompletionValue(mixed.item.relativePath),
388
+ label: mixed.item.fileName,
389
+ description: mixed.item.relativePath,
390
+ };
391
+ });
400
392
  }
401
393
 
402
394
  // Editor wrapper that injects FFF @-mention autocomplete alongside base provider.
@@ -423,12 +415,8 @@ export default function fffExtension(pi: ExtensionAPI) {
423
415
  if (mentionResult) return mentionResult;
424
416
  // Fall back to base provider
425
417
  return (
426
- this.baseProvider?.getSuggestions(
427
- lines,
428
- cursorLine,
429
- cursorCol,
430
- options,
431
- ) ?? null
418
+ this.baseProvider?.getSuggestions(lines, cursorLine, cursorCol, options) ??
419
+ null
432
420
  );
433
421
  },
434
422
  applyCompletion: (lines, cursorLine, cursorCol, item, prefix) => {
@@ -464,14 +452,12 @@ export default function fffExtension(pi: ExtensionAPI) {
464
452
  ) => void;
465
453
  };
466
454
  }) {
467
- if (!shouldEnableMentions()) {
468
- ctx.ui.setEditorComponent(undefined);
469
- } else {
470
- ctx.ui.setEditorComponent(
471
- (tui: any, theme: any, keybindings: any) =>
472
- new FffEditor(tui, theme, keybindings),
473
- );
474
- }
455
+ if (!shouldEnableMentions()) return;
456
+
457
+ ctx.ui.setEditorComponent(
458
+ (tui: any, theme: any, keybindings: any) =>
459
+ new FffEditor(tui, theme, keybindings),
460
+ );
475
461
  }
476
462
 
477
463
  // --- Flags / lifecycle ---
@@ -482,22 +468,20 @@ export default function fffExtension(pi: ExtensionAPI) {
482
468
  });
483
469
 
484
470
  pi.registerFlag("fff-frecency-db", {
485
- description:
486
- "Path to the frecency database (overrides FFF_FRECENCY_DB env)",
471
+ description: "Path to the frecency database (overrides FFF_FRECENCY_DB env)",
487
472
  type: "string",
488
473
  });
489
474
 
490
475
  pi.registerFlag("fff-history-db", {
491
- description:
492
- "Path to the query history database (overrides FFF_HISTORY_DB env)",
476
+ description: "Path to the query history database (overrides FFF_HISTORY_DB env)",
493
477
  type: "string",
494
478
  });
495
479
 
496
480
  pi.on("session_start", async (_event, ctx) => {
497
481
  try {
498
482
  activeCwd = ctx.cwd;
483
+ if (shouldEnableMentions()) applyEditorMode(ctx);
499
484
  await ensureFinder(activeCwd);
500
- applyEditorMode(ctx);
501
485
  } catch (e: unknown) {
502
486
  ctx.ui.notify(
503
487
  `FFF init failed: ${e instanceof Error ? e.message : String(e)}`,
@@ -519,20 +503,15 @@ export default function fffExtension(pi: ExtensionAPI) {
519
503
  context: any,
520
504
  maxLines = 15,
521
505
  ) => {
522
- const text =
523
- (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
524
- const output =
525
- result.content?.find((c) => c.type === "text")?.text?.trim() ?? "";
506
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
507
+ const output = result.content?.find((c) => c.type === "text")?.text?.trim() ?? "";
526
508
  if (!output) {
527
509
  text.setText(theme.fg("muted", "No output"));
528
510
  return text;
529
511
  }
530
512
 
531
513
  const lines = output.split("\n");
532
- const displayLines = lines.slice(
533
- 0,
534
- options.expanded ? lines.length : maxLines,
535
- );
514
+ const displayLines = lines.slice(0, options.expanded ? lines.length : maxLines);
536
515
  let content = `\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`;
537
516
  if (lines.length > displayLines.length) {
538
517
  content += theme.fg(
@@ -604,8 +583,7 @@ export default function fffExtension(pi: ExtensionAPI) {
604
583
  // as a valid regex, otherwise plain literal. The fuzzy fallback below
605
584
  // only kicks in for plain mode — regex queries are intentional.
606
585
  const hasRegexSyntax =
607
- params.pattern !==
608
- params.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
586
+ params.pattern !== params.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
609
587
  let mode: GrepMode = hasRegexSyntax ? "regex" : "plain";
610
588
  if (mode === "regex") {
611
589
  try {
@@ -677,14 +655,10 @@ export default function fffExtension(pi: ExtensionAPI) {
677
655
  let output = formatGrepOutput(result);
678
656
  const notices: string[] = [];
679
657
  if (result.regexFallbackError) {
680
- notices.push(
681
- `Invalid regex: ${result.regexFallbackError}, used literal match`,
682
- );
658
+ notices.push(`Invalid regex: ${result.regexFallbackError}, used literal match`);
683
659
  }
684
660
  if (result.nextCursor) {
685
- notices.push(
686
- `Continue with cursor="${storeCursor(result.nextCursor)}"`,
687
- );
661
+ notices.push(`Continue with cursor="${storeCursor(result.nextCursor)}"`);
688
662
  }
689
663
 
690
664
  if (notices.length > 0) output += `\n\n[${notices.join(". ")}]`;
@@ -700,8 +674,7 @@ export default function fffExtension(pi: ExtensionAPI) {
700
674
  },
701
675
 
702
676
  renderCall(args, theme, context) {
703
- const text =
704
- (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
677
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
705
678
  const pattern = args?.pattern ?? "";
706
679
  const path = args?.path ?? ".";
707
680
  let content =
@@ -797,8 +770,7 @@ export default function fffExtension(pi: ExtensionAPI) {
797
770
  // shown so far there's another page to fetch.
798
771
  const shownSoFar = pageIndex * effectiveLimit + result.items.length;
799
772
  const hasMore =
800
- result.items.length >= effectiveLimit &&
801
- result.totalMatched > shownSoFar;
773
+ result.items.length >= effectiveLimit && result.totalMatched > shownSoFar;
802
774
 
803
775
  const notices: string[] = [];
804
776
  if (formatted.weak && formatted.shownCount > 0)
@@ -832,8 +804,7 @@ export default function fffExtension(pi: ExtensionAPI) {
832
804
  },
833
805
 
834
806
  renderCall(args, theme, context) {
835
- const text =
836
- (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
807
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
837
808
  const pattern = args?.pattern ?? "";
838
809
  const path = args?.path ?? ".";
839
810
  let content =
@@ -866,9 +837,7 @@ export default function fffExtension(pi: ExtensionAPI) {
866
837
  constraints: Type.Optional(
867
838
  Type.String({ description: "File filter, e.g. '*.{ts,tsx} !test/'" }),
868
839
  ),
869
- context: Type.Optional(
870
- Type.Number({ description: "Context lines before+after" }),
871
- ),
840
+ context: Type.Optional(Type.Number({ description: "Context lines before+after" })),
872
841
  limit: Type.Optional(
873
842
  Type.Number({
874
843
  description: `Max matches (default ${DEFAULT_GREP_LIMIT})`,
@@ -934,8 +903,7 @@ export default function fffExtension(pi: ExtensionAPI) {
934
903
  },
935
904
 
936
905
  renderCall(args, theme, context) {
937
- const text =
938
- (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
906
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
939
907
  const patterns = args?.patterns ?? [];
940
908
  const constraints = args?.constraints;
941
909
  let content =
@@ -957,8 +925,7 @@ export default function fffExtension(pi: ExtensionAPI) {
957
925
  // --- commands ---
958
926
 
959
927
  pi.registerCommand("fff-mode", {
960
- description:
961
- "Show or set FFF mode: /fff-mode [tools-and-ui | tools-only | override]",
928
+ description: "Show or set FFF mode: /fff-mode [tools-and-ui | tools-only | override]",
962
929
  handler: async (args, ctx) => {
963
930
  const arg = (args || "").trim();
964
931
 
@@ -967,19 +934,13 @@ export default function fffExtension(pi: ExtensionAPI) {
967
934
  const mode = getMode();
968
935
  const flag = pi.getFlag("fff-mode") ?? "unset";
969
936
  const env = process.env.PI_FFF_MODE ?? "unset";
970
- ctx.ui.notify(
971
- `Current mode: '${mode}'\nFlag: ${flag}, Env: ${env}`,
972
- "info",
973
- );
937
+ ctx.ui.notify(`Current mode: '${mode}'\nFlag: ${flag}, Env: ${env}`, "info");
974
938
  return;
975
939
  }
976
940
 
977
941
  // Validate and set mode
978
942
  if (!VALID_MODES.includes(arg as FffMode)) {
979
- ctx.ui.notify(
980
- `Usage: /fff-mode [${VALID_MODES.join(" | ")}]`,
981
- "warning",
982
- );
943
+ ctx.ui.notify(`Usage: /fff-mode [${VALID_MODES.join(" | ")}]`, "warning");
983
944
  return;
984
945
  }
985
946