@danypops/pi-lector 0.9.6 → 0.11.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 (46) hide show
  1. package/extension/src/{apply-patch-operations.ts → apply-patch/operations.ts} +3 -3
  2. package/extension/src/{apply-patch-rendering.ts → apply-patch/rendering.ts} +1 -1
  3. package/extension/src/{code-intelligence-operations.ts → code-intelligence/operations.ts} +2 -2
  4. package/extension/src/{code-intelligence-rendering.ts → code-intelligence/rendering.ts} +1 -1
  5. package/extension/src/{cross-workspace-search-operations.ts → cross-workspace-search/operations.ts} +1 -1
  6. package/extension/src/{cross-workspace-search-rendering.ts → cross-workspace-search/rendering.ts} +18 -6
  7. package/extension/src/{edit-operations.ts → edit/operations.ts} +2 -2
  8. package/extension/src/editor/apply-explorer-diffs.ts +45 -0
  9. package/extension/src/editor/directory-explorer-operations.ts +61 -0
  10. package/extension/src/editor/editor-state.ts +317 -0
  11. package/extension/src/editor/editor-theme.ts +7 -0
  12. package/extension/src/editor/explorer-component.ts +241 -0
  13. package/extension/src/editor/explorer-diff.ts +85 -0
  14. package/extension/src/editor/explorer-flow.ts +27 -0
  15. package/extension/src/editor/neovim-editor-component.ts +196 -0
  16. package/extension/src/editor/operations.ts +36 -0
  17. package/extension/src/{external-search-operations.ts → external-search/operations.ts} +1 -1
  18. package/extension/src/{external-search-rendering.ts → external-search/rendering.ts} +1 -1
  19. package/extension/src/{find-files-operations.ts → find-files/operations.ts} +1 -1
  20. package/extension/src/{find-files-rendering.ts → find-files/rendering.ts} +1 -1
  21. package/extension/src/{find-symbols-operations.ts → find-symbols/operations.ts} +1 -1
  22. package/extension/src/{find-symbols-rendering.ts → find-symbols/rendering.ts} +1 -1
  23. package/extension/src/{git-operations.ts → git/operations.ts} +1 -1
  24. package/extension/src/{git-rendering.ts → git/rendering.ts} +1 -1
  25. package/extension/src/index.ts +97 -34
  26. package/extension/src/{line-edit-operations.ts → line-edit/operations.ts} +4 -4
  27. package/extension/src/{line-edit-rendering.ts → line-edit/rendering.ts} +1 -1
  28. package/extension/src/{mutation-history-operations.ts → mutation-history/operations.ts} +2 -2
  29. package/extension/src/{package-source-operations.ts → package-source/operations.ts} +1 -1
  30. package/extension/src/{package-source-rendering.ts → package-source/rendering.ts} +35 -28
  31. package/extension/src/{read-operations.ts → read/operations.ts} +2 -2
  32. package/extension/src/{reference-based-rename-operations.ts → reference-based-rename/operations.ts} +1 -1
  33. package/extension/src/{rename-operations.ts → rename/operations.ts} +1 -1
  34. package/extension/src/{repo-cache-evict-operations.ts → repo-cache/evict-operations.ts} +2 -2
  35. package/extension/src/{repo-cache-list-operations.ts → repo-cache/list-operations.ts} +2 -2
  36. package/extension/src/{repo-cache-rendering.ts → repo-cache/rendering.ts} +1 -1
  37. package/extension/src/{repo-fetch-operations.ts → repo-fetch/operations.ts} +1 -1
  38. package/extension/src/{search-operations.ts → search/operations.ts} +1 -1
  39. package/extension/src/{search-rendering.ts → search/rendering.ts} +1 -1
  40. package/extension/src/{symbol-annotation-operations.ts → symbol-annotation/operations.ts} +1 -1
  41. package/extension/src/{symbol-annotation-rendering.ts → symbol-annotation/rendering.ts} +1 -1
  42. package/extension/src/{workspace-cache-operations.ts → workspace-cache/operations.ts} +1 -1
  43. package/extension/src/{workspace-cache-rendering.ts → workspace-cache/rendering.ts} +1 -1
  44. package/extension/src/{write-operations.ts → write/operations.ts} +2 -2
  45. package/package.json +3 -2
  46. /package/extension/src/{code-intelligence-hints.ts → code-intelligence/hints.ts} +0 -0
@@ -35,6 +35,7 @@ import {
35
35
  createReadToolDefinition,
36
36
  createWriteToolDefinition,
37
37
  type ExtensionAPI,
38
+ type ExtensionCommandContext,
38
39
  } from "@earendil-works/pi-coding-agent";
39
40
  import { Text, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
40
41
  import { renderBoundedTable, type TextMeasure } from "malevich-tui-components";
@@ -43,9 +44,9 @@ import { Type } from "typebox";
43
44
  /** Real ANSI-aware measurement for Table -- Malevich's own default is ASCII-only, unsafe against theme-styled cell/header text. */
44
45
  const tableMeasure: TextMeasure = { visibleWidth, truncateToWidth };
45
46
 
46
- import { createLectorApplyPatchOperations } from "./apply-patch-operations.ts";
47
- import { formatApplyPatchCall, formatApplyPatchResult } from "./apply-patch-rendering.ts";
48
- import { createLectorCodeIntelligenceOperations } from "./code-intelligence-operations.ts";
47
+ import { createLectorApplyPatchOperations } from "./apply-patch/operations.ts";
48
+ import { formatApplyPatchCall, formatApplyPatchResult } from "./apply-patch/rendering.ts";
49
+ import { createLectorCodeIntelligenceOperations } from "./code-intelligence/operations.ts";
49
50
  import {
50
51
  type CallHierarchyToolDetails,
51
52
  formatCallHierarchyCall,
@@ -66,29 +67,34 @@ import {
66
67
  formatReachableFromResult,
67
68
  formatWorkspaceMapCall,
68
69
  formatWorkspaceMapResult,
69
- } from "./code-intelligence-rendering.ts";
70
- import { type CrossWorkspaceOutcome, createLectorCrossWorkspaceSearchOperations } from "./cross-workspace-search-operations.ts";
71
- import { formatCrossWorkspaceCall, formatFindSymbolsAcrossProjectsResult, formatSearchTextAcrossProjectsResult } from "./cross-workspace-search-rendering.ts";
72
- import { createLectorEditOperations } from "./edit-operations.ts";
73
- import { createExternalSearchOperations } from "./external-search-operations.ts";
70
+ } from "./code-intelligence/rendering.ts";
71
+ import { type CrossWorkspaceOutcome, createLectorCrossWorkspaceSearchOperations } from "./cross-workspace-search/operations.ts";
72
+ import { formatCrossWorkspaceCall, formatFindSymbolsAcrossProjectsResult, formatSearchTextAcrossProjectsResult } from "./cross-workspace-search/rendering.ts";
73
+ import { createLectorEditOperations } from "./edit/operations.ts";
74
+ import { openDirectoryExplorer } from "./editor/directory-explorer-operations.ts";
75
+ import { ExplorerComponent, type ExplorerResult } from "./editor/explorer-component.ts";
76
+ import { runExplorerFlow } from "./editor/explorer-flow.ts";
77
+ import { NeovimEditorComponent, type NeovimEditorHost } from "./editor/neovim-editor-component.ts";
78
+ import { openEditorFile } from "./editor/operations.ts";
79
+ import { createExternalSearchOperations } from "./external-search/operations.ts";
74
80
  import {
75
81
  formatExternalSearchCall,
76
82
  formatGithubRepoSearchResult,
77
83
  formatNpmPackageSearchResult,
78
84
  formatSourcegraphCodeSearchResult,
79
- } from "./external-search-rendering.ts";
80
- import { createLectorFindFilesOperations } from "./find-files-operations.ts";
81
- import { formatFindFilesCall, formatFindFilesResult } from "./find-files-rendering.ts";
82
- import { createLectorFindSymbolsOperations } from "./find-symbols-operations.ts";
83
- import { describeFindSymbolSources, formatFindSymbolsCall, formatFindSymbolsResult } from "./find-symbols-rendering.ts";
84
- import { createLectorGitOperations } from "./git-operations.ts";
85
- import { formatGitCall, formatGitResult, type GitToolDetails } from "./git-rendering.ts";
85
+ } from "./external-search/rendering.ts";
86
+ import { createLectorFindFilesOperations } from "./find-files/operations.ts";
87
+ import { formatFindFilesCall, formatFindFilesResult } from "./find-files/rendering.ts";
88
+ import { createLectorFindSymbolsOperations } from "./find-symbols/operations.ts";
89
+ import { describeFindSymbolSources, formatFindSymbolsCall, formatFindSymbolsResult } from "./find-symbols/rendering.ts";
90
+ import { createLectorGitOperations } from "./git/operations.ts";
91
+ import { formatGitCall, formatGitResult, type GitToolDetails } from "./git/rendering.ts";
86
92
  import { setNewWorkspaceObserver } from "./lector-client.ts";
87
- import { createLectorLineEditOperations } from "./line-edit-operations.ts";
88
- import { formatLineEditCall, formatLineEditResult } from "./line-edit-rendering.ts";
89
- import { createMutationHistoryOperations } from "./mutation-history-operations.ts";
93
+ import { createLectorLineEditOperations } from "./line-edit/operations.ts";
94
+ import { formatLineEditCall, formatLineEditResult } from "./line-edit/rendering.ts";
95
+ import { createMutationHistoryOperations } from "./mutation-history/operations.ts";
90
96
  import { isFilesystemRoot, nearestGitRoot } from "./nearest-workspace-root.ts";
91
- import { createLectorPackageSourceOperations, type PackageSourceListPage } from "./package-source-operations.ts";
97
+ import { createLectorPackageSourceOperations, type PackageSourceListPage } from "./package-source/operations.ts";
92
98
  import {
93
99
  buildPackageSourceListTableRows,
94
100
  formatPackageSourceCall,
@@ -99,12 +105,12 @@ import {
99
105
  PACKAGE_SOURCE_LIST_TABLE_COLUMNS,
100
106
  PACKAGE_SOURCE_LIST_VISIBLE_ROWS,
101
107
  packageSourceListMoreLine,
102
- } from "./package-source-rendering.ts";
103
- import { createLectorReadOperations } from "./read-operations.ts";
104
- import { createReferenceBasedRenameOperations } from "./reference-based-rename-operations.ts";
105
- import { createRenameOperations } from "./rename-operations.ts";
106
- import { createRepoCacheEvictOperations } from "./repo-cache-evict-operations.ts";
107
- import { createRepoCacheListOperations } from "./repo-cache-list-operations.ts";
108
+ } from "./package-source/rendering.ts";
109
+ import { createLectorReadOperations } from "./read/operations.ts";
110
+ import { createReferenceBasedRenameOperations } from "./reference-based-rename/operations.ts";
111
+ import { createRenameOperations } from "./rename/operations.ts";
112
+ import { createRepoCacheEvictOperations } from "./repo-cache/evict-operations.ts";
113
+ import { createRepoCacheListOperations } from "./repo-cache/list-operations.ts";
108
114
  import {
109
115
  buildRepoCacheTableRows,
110
116
  formatRepoCacheCall,
@@ -114,21 +120,21 @@ import {
114
120
  REPO_CACHE_TABLE_COLUMNS,
115
121
  REPO_CACHE_VISIBLE_ROWS,
116
122
  repoCacheMoreLine,
117
- } from "./repo-cache-rendering.ts";
118
- import { createLectorRepoFetchOperations } from "./repo-fetch-operations.ts";
119
- import { createLectorSearchOperations } from "./search-operations.ts";
120
- import { formatSearchCall, formatSearchResult } from "./search-rendering.ts";
121
- import { type AnnotationAnchorInput, createLectorSymbolAnnotationOperations } from "./symbol-annotation-operations.ts";
122
- import { formatAnnotationDetail, formatAnnotationListSummary, formatAnnotationSummary } from "./symbol-annotation-rendering.ts";
123
+ } from "./repo-cache/rendering.ts";
124
+ import { createLectorRepoFetchOperations } from "./repo-fetch/operations.ts";
125
+ import { createLectorSearchOperations } from "./search/operations.ts";
126
+ import { formatSearchCall, formatSearchResult } from "./search/rendering.ts";
127
+ import { type AnnotationAnchorInput, createLectorSymbolAnnotationOperations } from "./symbol-annotation/operations.ts";
128
+ import { formatAnnotationDetail, formatAnnotationListSummary, formatAnnotationSummary } from "./symbol-annotation/rendering.ts";
123
129
  import {
124
130
  type CachePresentationState,
125
131
  cacheContextMessage,
126
132
  createWorkspaceCacheOperations,
127
133
  describeCacheState,
128
134
  monitorWorkspaceCache,
129
- } from "./workspace-cache-operations.ts";
130
- import { formatJobSnapshotResult, formatWorkspaceCacheCall, formatWorkspaceCacheStatusResult } from "./workspace-cache-rendering.ts";
131
- import { createLectorWriteOperations } from "./write-operations.ts";
135
+ } from "./workspace-cache/operations.ts";
136
+ import { formatJobSnapshotResult, formatWorkspaceCacheCall, formatWorkspaceCacheStatusResult } from "./workspace-cache/rendering.ts";
137
+ import { createLectorWriteOperations } from "./write/operations.ts";
132
138
 
133
139
  function describeIntelligenceSource(provenance: IntelligenceProvenance): string {
134
140
  return `${provenance.fidelity} via ${provenance.backend}`;
@@ -344,6 +350,63 @@ export default function (pi: ExtensionAPI) {
344
350
  });
345
351
 
346
352
  const codeIntelligenceOperations = createLectorCodeIntelligenceOperations();
353
+
354
+ const editorOverlayOptions = { overlay: true, overlayOptions: { width: "100%", maxHeight: "100%", anchor: "center" } } as const;
355
+
356
+ async function openFileInEditor(commandCtx: ExtensionCommandContext, absolutePath: string): Promise<void> {
357
+ let session: Awaited<ReturnType<typeof openEditorFile>>;
358
+ try {
359
+ session = await openEditorFile(absolutePath);
360
+ } catch (error) {
361
+ commandCtx.ui.notify(`Could not open ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`, "error");
362
+ return;
363
+ }
364
+
365
+ await commandCtx.ui.custom<void>((tui, theme, _keybindings, done) => {
366
+ const host: NeovimEditorHost = {
367
+ filePath: absolutePath,
368
+ save: (text) => session.save(text),
369
+ hover: async (line, character) => {
370
+ const result = await codeIntelligenceOperations.hover(absolutePath, line, character);
371
+ return result.hover;
372
+ },
373
+ };
374
+ return new NeovimEditorComponent(tui, theme, host, session.content, () => done(undefined));
375
+ }, editorOverlayOptions);
376
+ }
377
+
378
+ /** Oil-style: /editor with no path browses the caller's cwd (nearest git root, matching workspaceForDirectory's own convention). runExplorerFlow owns the browse/open/return-to-explorer loop; this just wires it to the real UI and Lector session. */
379
+ async function openExplorerFlow(commandCtx: ExtensionCommandContext): Promise<void> {
380
+ let session: Awaited<ReturnType<typeof openDirectoryExplorer>>;
381
+ try {
382
+ session = await openDirectoryExplorer(commandCtx.cwd);
383
+ } catch (error) {
384
+ commandCtx.ui.notify(`Could not open explorer at ${commandCtx.cwd}: ${error instanceof Error ? error.message : String(error)}`, "error");
385
+ return;
386
+ }
387
+
388
+ await runExplorerFlow(session, {
389
+ showExplorer: (explorerSession, relativePath) =>
390
+ commandCtx.ui.custom<ExplorerResult>(
391
+ (tui, theme, _keybindings, done) => new ExplorerComponent(tui, theme, explorerSession, relativePath, done),
392
+ editorOverlayOptions,
393
+ ),
394
+ showEditor: (absolutePath) => openFileInEditor(commandCtx, absolutePath),
395
+ });
396
+ }
397
+
398
+ pi.registerCommand("editor", {
399
+ description: "Open a file in a neovim-style modal code editor, or a filesystem explorer with no path",
400
+ handler: async (args, commandCtx) => {
401
+ const target = args.trim();
402
+ if (!target) {
403
+ await openExplorerFlow(commandCtx);
404
+ return;
405
+ }
406
+ await openFileInEditor(commandCtx, resolve(commandCtx.cwd, target));
407
+ },
408
+ });
409
+
347
410
  const referenceBasedRenameOperations = createReferenceBasedRenameOperations();
348
411
  const renameOperations = createRenameOperations();
349
412
  const positionParameters = {
@@ -1,12 +1,12 @@
1
1
  import { type LineEdit, type LineEditOutcome, type LineHash, lineHashOf } from "@danypops/lector";
2
- import { lectorClient, withWorkspace, workspaceForPath } from "./lector-client.ts";
3
- import { toWorkspaceRelativePath } from "./workspace-relative-path.ts";
2
+ import { lectorClient, withWorkspace, workspaceForPath } from "../lector-client.ts";
3
+ import { toWorkspaceRelativePath } from "../workspace-relative-path.ts";
4
4
 
5
5
  /**
6
6
  * Thin wrapper over workspace.lineEdit -- distinct from the generic edit tool (backed by
7
- * exactEdit's whole-file hash guard, see edit-operations.ts): every edit here is guarded by
7
+ * exactEdit's whole-file hash guard, see edit/operations.ts): every edit here is guarded by
8
8
  * its own referenced line(s)' hash, so a concurrent change to a line no edit references never
9
- * invalidates this one. `path` is an absolute file path, the same convention edit-operations.ts
9
+ * invalidates this one. `path` is an absolute file path, the same convention edit/operations.ts
10
10
  * already uses -- the workspace is resolved from the file itself, not a separate directory arg.
11
11
  */
12
12
  export interface LineEditOperations {
@@ -1,5 +1,5 @@
1
1
  import type { LineEditOutcome } from "@danypops/lector";
2
- import type { LectorTheme } from "./lector-tui-theme.ts";
2
+ import type { LectorTheme } from "../lector-tui-theme.ts";
3
3
 
4
4
  export function formatLineEditCall(args: { path?: unknown; edits?: unknown }, theme: LectorTheme): string {
5
5
  const path = typeof args.path === "string" ? args.path : "";
@@ -1,6 +1,6 @@
1
1
  import type { MutationHistoryEntry } from "@danypops/lector";
2
- import { lectorClient, withWorkspace, workspaceForPath } from "./lector-client.ts";
3
- import { toWorkspaceRelativePath } from "./workspace-relative-path.ts";
2
+ import { lectorClient, withWorkspace, workspaceForPath } from "../lector-client.ts";
3
+ import { toWorkspaceRelativePath } from "../workspace-relative-path.ts";
4
4
 
5
5
  /** Thin wrapper over Lector's mutation history: every successful edit is recorded, and any entry can be reverted -- guarded the same way every other Lector write is. */
6
6
  export interface MutationHistoryOperations {
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_PACKAGE_SOURCE_BOUNDS, type PackageEcosystem, type PackageSourceListEntry, type PackageSourceOperationResult } from "@danypops/lector";
2
- import { lectorClient } from "./lector-client.ts";
2
+ import { lectorClient } from "../lector-client.ts";
3
3
 
4
4
  export interface PackageSourceListPage {
5
5
  readonly entries: readonly PackageSourceListEntry[];
@@ -1,7 +1,7 @@
1
1
  import type { PackageSourceListEntry, PackageSourceOperationResult } from "@danypops/lector";
2
2
  import { keyHint } from "@earendil-works/pi-coding-agent";
3
3
  import { renderTruncatedList, type TableColumn } from "malevich-tui-components";
4
- import type { LectorTheme } from "./lector-tui-theme.ts";
4
+ import type { LectorTheme } from "../lector-tui-theme.ts";
5
5
 
6
6
  const DEFAULT_VISIBLE_CANDIDATES = 5;
7
7
 
@@ -36,33 +36,40 @@ export function formatPackageSourceCall(
36
36
  export function formatPackageSourceResult(result: PackageSourceOperationResult | undefined, expanded: boolean, theme: LectorTheme): string {
37
37
  if (!result) return theme.fg("dim", "No package-source result.");
38
38
  const { outcome } = result;
39
- if (outcome.status === "verified") {
40
- return [
41
- `${theme.fg("accent", result.workspaceId ?? "unregistered")} ${theme.fg("success", `${outcome.coordinate.name}@${outcome.coordinate.resolvedVersion}`)}`,
42
- `${outcome.workspace.cachePath}`,
43
- `${outcome.repository.url ?? "local source"}@${outcome.repository.resolvedRef ?? "local"} ${outcome.repository.commit ?? outcome.verification.integrity}`,
44
- ].join("\n");
39
+ switch (outcome.status) {
40
+ case "verified":
41
+ return [
42
+ `${theme.fg("accent", result.workspaceId ?? "unregistered")} ${theme.fg("success", `${outcome.coordinate.name}@${outcome.coordinate.resolvedVersion}`)}`,
43
+ `${outcome.workspace.cachePath}`,
44
+ `${outcome.repository.url ?? "local source"}@${outcome.repository.resolvedRef ?? "local"} ${outcome.repository.commit ?? outcome.verification.integrity}`,
45
+ ].join("\n");
46
+ case "ambiguous": {
47
+ const lines = [
48
+ theme.fg("warning", `Ambiguous package source (${outcome.code})`),
49
+ ...renderTruncatedList({
50
+ items: outcome.candidates,
51
+ expanded,
52
+ visibleCount: DEFAULT_VISIBLE_CANDIDATES,
53
+ formatItem: (candidate) => `${candidate.version} -- ${candidate.source}`,
54
+ moreLine: (hidden) => theme.fg("dim", `… ${hidden} more`),
55
+ truncationWarning: outcome.truncated ? theme.fg("dim", "More candidates were truncated by the daemon.") : undefined,
56
+ }),
57
+ ];
58
+ return lines.join("\n");
59
+ }
60
+ case "unauthenticated":
61
+ return theme.fg("warning", `Authentication required (${outcome.code}): configure ${outcome.requiredCredentialNames.join(", ")}`);
62
+ case "oversized":
63
+ return theme.fg("warning", `Source resolution exceeded ${outcome.resource} limit ${outcome.limit}.`);
64
+ case "mismatched":
65
+ return theme.fg("error", `Source mismatch (${outcome.code}): expected ${outcome.expected}, got ${outcome.actual}.`);
66
+ case "unavailable":
67
+ return theme.fg("warning", `Source unavailable (${outcome.code}).`);
68
+ default: {
69
+ const exhaustive: never = outcome;
70
+ throw new Error(`unhandled package source outcome status: ${JSON.stringify(exhaustive)}`);
71
+ }
45
72
  }
46
- if (outcome.status === "ambiguous") {
47
- const lines = [
48
- theme.fg("warning", `Ambiguous package source (${outcome.code})`),
49
- ...renderTruncatedList({
50
- items: outcome.candidates,
51
- expanded,
52
- visibleCount: DEFAULT_VISIBLE_CANDIDATES,
53
- formatItem: (candidate) => `${candidate.version} -- ${candidate.source}`,
54
- moreLine: (hidden) => theme.fg("dim", `… ${hidden} more`),
55
- truncationWarning: outcome.truncated ? theme.fg("dim", "More candidates were truncated by the daemon.") : undefined,
56
- }),
57
- ];
58
- return lines.join("\n");
59
- }
60
- if (outcome.status === "unauthenticated") {
61
- return theme.fg("warning", `Authentication required (${outcome.code}): configure ${outcome.requiredCredentialNames.join(", ")}`);
62
- }
63
- if (outcome.status === "oversized") return theme.fg("warning", `Source resolution exceeded ${outcome.resource} limit ${outcome.limit}.`);
64
- if (outcome.status === "mismatched") return theme.fg("error", `Source mismatch (${outcome.code}): expected ${outcome.expected}, got ${outcome.actual}.`);
65
- return theme.fg("warning", `Source unavailable (${outcome.code}).`);
66
73
  }
67
74
 
68
75
  /** Empty-state fallback only -- a non-empty page renders as a real Table (see buildPackageSourceListTableRows) so the human channel actually shows what's resolved, not just a bare count. Mirrors formatRepoCacheListResult. */
@@ -74,7 +81,7 @@ export function formatPackageSourceListResult(
74
81
  return count === 0 ? theme.fg("dim", "no resolved package sources") : theme.fg("success", `${count} resolved package source${count === 1 ? "" : "s"}`);
75
82
  }
76
83
 
77
- /** Powers of 1024, one decimal past the first. Mirrors repo-cache-rendering.ts's own formatCacheSize -- kept as a small local duplicate rather than a shared export, matching that file's own precedent. */
84
+ /** Powers of 1024, one decimal past the first. Mirrors repo-cache/rendering.ts's own formatCacheSize -- kept as a small local duplicate rather than a shared export, matching that file's own precedent. */
78
85
  function formatCacheSize(bytes: number): string {
79
86
  const units = ["B", "KB", "MB", "GB", "TB"] as const;
80
87
  let value = bytes;
@@ -1,8 +1,8 @@
1
1
  import { constants } from "node:fs";
2
2
  import { access as fsAccess, readFile as fsReadFile } from "node:fs/promises";
3
3
  import type { ReadOperations } from "@earendil-works/pi-coding-agent";
4
- import { lectorClient, withWorkspace, workspaceForPath } from "./lector-client.ts";
5
- import { toWorkspaceRelativePath } from "./workspace-relative-path.ts";
4
+ import { lectorClient, withWorkspace, workspaceForPath } from "../lector-client.ts";
5
+ import { toWorkspaceRelativePath } from "../workspace-relative-path.ts";
6
6
 
7
7
  /**
8
8
  * Lector's core domain (RawRead/ExpectedHashEdit) is deliberately text-only
@@ -1,5 +1,5 @@
1
1
  import type { OperationOutputs } from "@danypops/lector";
2
- import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from "./lector-client.ts";
2
+ import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from "../lector-client.ts";
3
3
 
4
4
  /**
5
5
  * Thin wrapper over Lector's non-LSP reference-based rename: moves a file and rewrites every
@@ -1,5 +1,5 @@
1
1
  import type { OperationOutputs } from "@danypops/lector";
2
- import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from "./lector-client.ts";
2
+ import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from "../lector-client.ts";
3
3
 
4
4
  /**
5
5
  * Thin wrappers over Lector's LSP-driven prepareRename/rename -- position-based (path + 1-indexed
@@ -1,8 +1,8 @@
1
- import { lectorClient } from "./lector-client.ts";
1
+ import { lectorClient } from "../lector-client.ts";
2
2
 
3
3
  /**
4
4
  * Thin wrapper over repo.evictCache -- no `directory`/workspaceForDirectory resolution (matching
5
- * repo-fetch-operations.ts and repo-cache-list-operations.ts: this targets the daemon-wide fetch
5
+ * repo-fetch/operations.ts and repo-cache/list-operations.ts: this targets the daemon-wide fetch
6
6
  * cache, not a workspace-scoped concept).
7
7
  */
8
8
  export interface RepoCacheEvictOperations {
@@ -1,9 +1,9 @@
1
1
  import type { CachedRepositoryPage } from "@danypops/lector";
2
- import { lectorClient } from "./lector-client.ts";
2
+ import { lectorClient } from "../lector-client.ts";
3
3
 
4
4
  /**
5
5
  * Thin wrapper over repo.listCache -- no network, no cache mutation, no `directory`/
6
- * workspaceForDirectory resolution (matching repo-fetch-operations.ts: this queries the
6
+ * workspaceForDirectory resolution (matching repo-fetch/operations.ts: this queries the
7
7
  * daemon-wide fetch cache, not a workspace-scoped concept).
8
8
  */
9
9
  export interface RepoCacheListOperations {
@@ -1,7 +1,7 @@
1
1
  import type { CachedRepositoryEntry, CachedRepositoryPage, RepoFetchResult } from "@danypops/lector";
2
2
  import { keyHint } from "@earendil-works/pi-coding-agent";
3
3
  import type { TableColumn } from "malevich-tui-components";
4
- import type { LectorTheme } from "./lector-tui-theme.ts";
4
+ import type { LectorTheme } from "../lector-tui-theme.ts";
5
5
 
6
6
  /** Table has no row-count bound of its own; a cache can grow arbitrarily large even though repo_cache's own `maxResults` bounds any one page, so the display itself still needs a cap independent of that. */
7
7
  export const REPO_CACHE_VISIBLE_ROWS = 20;
@@ -1,5 +1,5 @@
1
1
  import type { RepoFetchResult } from "@danypops/lector";
2
- import { lectorClient } from "./lector-client.ts";
2
+ import { lectorClient } from "../lector-client.ts";
3
3
 
4
4
  /**
5
5
  * Thin wrapper over repo.fetch. No `directory`/workspaceForDirectory resolution here -- unlike
@@ -1,5 +1,5 @@
1
1
  import type { TextSearchResult } from "@danypops/lector";
2
- import { lectorClient, withWorkspace, workspaceForDirectory } from "./lector-client.ts";
2
+ import { lectorClient, withWorkspace, workspaceForDirectory } from "../lector-client.ts";
3
3
 
4
4
  /**
5
5
  * Thin wrapper over workspace.searchText. `directory` is required, same convention as
@@ -1,7 +1,7 @@
1
1
  import type { TextSearchResult } from "@danypops/lector";
2
2
  import { keyHint } from "@earendil-works/pi-coding-agent";
3
3
  import { renderTruncatedList } from "malevich-tui-components";
4
- import type { LectorTheme } from "./lector-tui-theme.ts";
4
+ import type { LectorTheme } from "../lector-tui-theme.ts";
5
5
 
6
6
  const DEFAULT_VISIBLE_MATCHES = 20;
7
7
 
@@ -1,5 +1,5 @@
1
1
  import type { OperationInputs, OperationOutputs } from "@danypops/lector";
2
- import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from "./lector-client.ts";
2
+ import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from "../lector-client.ts";
3
3
 
4
4
  /** A bare symbol position an anchor is given as -- symbolNodeId and the anchor's baseline file hash are derived server-side, never supplied by the caller. */
5
5
  export interface AnnotationAnchorInput {
@@ -1,5 +1,5 @@
1
1
  import type { SymbolAnnotation } from "@danypops/lector";
2
- import type { LectorTheme } from "./lector-tui-theme.ts";
2
+ import type { LectorTheme } from "../lector-tui-theme.ts";
3
3
 
4
4
  const STATUS_COLOR: Record<SymbolAnnotation["status"], "success" | "warning" | "dim"> = {
5
5
  fresh: "success",
@@ -1,5 +1,5 @@
1
1
  import type { JobSnapshot, PopulateSymbolGraphResult, WorkspaceCacheStatus } from "@danypops/lector";
2
- import { lectorClient, withWorkspace, workspaceForDirectory } from "./lector-client.ts";
2
+ import { lectorClient, withWorkspace, workspaceForDirectory } from "../lector-client.ts";
3
3
 
4
4
  export interface WorkspaceCacheOperations {
5
5
  status(directory: string, maxFiles: number, maxSymbolsPerFile: number): Promise<WorkspaceCacheStatus>;
@@ -1,5 +1,5 @@
1
1
  import type { JobSnapshot, PopulateSymbolGraphResult, WorkspaceCacheStatus } from "@danypops/lector";
2
- import type { LectorTheme } from "./lector-tui-theme.ts";
2
+ import type { LectorTheme } from "../lector-tui-theme.ts";
3
3
 
4
4
  type WorkspaceCacheAction = "status" | "populate" | "job_status";
5
5
 
@@ -1,7 +1,7 @@
1
1
  import { type ContentHash, remoteErrorIs } from "@danypops/lector";
2
2
  import type { WriteOperations } from "@earendil-works/pi-coding-agent";
3
- import { lectorClient, withWorkspace, workspaceForPath } from "./lector-client.ts";
4
- import { toWorkspaceRelativePath } from "./workspace-relative-path.ts";
3
+ import { lectorClient, withWorkspace, workspaceForPath } from "../lector-client.ts";
4
+ import { toWorkspaceRelativePath } from "../workspace-relative-path.ts";
5
5
 
6
6
  const MAX_STALE_HASH_RETRIES = 3;
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-lector",
3
- "version": "0.9.6",
3
+ "version": "0.11.0",
4
4
  "description": "Pi host adapter for Lector: overrides read/write/edit with a daemon-backed, hash-guarded filesystem",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,10 +19,11 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@danypops/vehicle-client": "^0.2.0",
22
- "@danypops/lector": "^0.12.0",
22
+ "@danypops/lector": "^0.15.0",
23
23
  "malevich-tui-components": "^0.19.0"
24
24
  },
25
25
  "devDependencies": {
26
+ "@danypops/pi-tui-harness": "^0.0.1",
26
27
  "@earendil-works/pi-ai": "^0.81.1",
27
28
  "@earendil-works/pi-coding-agent": "^0.81.1",
28
29
  "@earendil-works/pi-tui": "^0.81.1",