@danypops/lector 0.1.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/package.json +48 -0
  4. package/src/adapters/directory-size.ts +19 -0
  5. package/src/adapters/find-source-files.ts +35 -0
  6. package/src/adapters/git-repo-fetcher.ts +146 -0
  7. package/src/adapters/in-memory-content-cache.ts +19 -0
  8. package/src/adapters/in-memory-search-cache.ts +32 -0
  9. package/src/adapters/in-memory-symbol-graph.ts +80 -0
  10. package/src/adapters/in-memory-workspace.ts +28 -0
  11. package/src/adapters/local-filesystem-workspace.ts +96 -0
  12. package/src/adapters/local-git.ts +73 -0
  13. package/src/adapters/lsp/discover-seed-file.ts +130 -0
  14. package/src/adapters/lsp/json-rpc-stream.ts +67 -0
  15. package/src/adapters/lsp/language-server-process.ts +184 -0
  16. package/src/adapters/lsp/lsp-symbol-index.ts +470 -0
  17. package/src/adapters/lsp/process-resource-usage.ts +61 -0
  18. package/src/adapters/lsp/typescript-project-files.ts +51 -0
  19. package/src/adapters/read-only-workspace.ts +23 -0
  20. package/src/adapters/ripgrep-text-search.ts +97 -0
  21. package/src/adapters/source-manifest.ts +44 -0
  22. package/src/adapters/sqlite-content-cache.ts +67 -0
  23. package/src/adapters/sqlite-search-cache.ts +60 -0
  24. package/src/adapters/sqlite-symbol-graph.ts +154 -0
  25. package/src/adapters/tiered-search-cache.ts +29 -0
  26. package/src/adapters/tree-sitter/typescript-tree-sitter-symbol-index.ts +155 -0
  27. package/src/cli.ts +777 -0
  28. package/src/client.ts +63 -0
  29. package/src/constants.ts +16 -0
  30. package/src/daemon.ts +166 -0
  31. package/src/domain/assert-safe-git-argument.ts +19 -0
  32. package/src/domain/assert-safe-path-segment.ts +17 -0
  33. package/src/domain/assert-safe-repo-reference.ts +20 -0
  34. package/src/domain/assert-safe-search-query.ts +17 -0
  35. package/src/domain/bounded-job-executor.ts +219 -0
  36. package/src/domain/call-hierarchy.ts +23 -0
  37. package/src/domain/code-range.ts +11 -0
  38. package/src/domain/content-hash.ts +14 -0
  39. package/src/domain/diagnostic.ts +12 -0
  40. package/src/domain/diagnostics.ts +7 -0
  41. package/src/domain/document-symbol.ts +19 -0
  42. package/src/domain/document-symbols.ts +7 -0
  43. package/src/domain/exact-edit.ts +43 -0
  44. package/src/domain/find-references.ts +7 -0
  45. package/src/domain/find-workspace-symbols.ts +7 -0
  46. package/src/domain/git-diff-result.ts +5 -0
  47. package/src/domain/git-log-entry.ts +9 -0
  48. package/src/domain/git-status.ts +17 -0
  49. package/src/domain/go-to-definition.ts +7 -0
  50. package/src/domain/go-to-implementation.ts +7 -0
  51. package/src/domain/hover-at.ts +8 -0
  52. package/src/domain/hover.ts +7 -0
  53. package/src/domain/incoming-calls.ts +8 -0
  54. package/src/domain/language-server-descriptor.ts +122 -0
  55. package/src/domain/outgoing-calls.ts +8 -0
  56. package/src/domain/populate-symbol-graph.ts +91 -0
  57. package/src/domain/prepare-call-hierarchy.ts +8 -0
  58. package/src/domain/race-workspace-query.ts +39 -0
  59. package/src/domain/raw-read.ts +24 -0
  60. package/src/domain/reachable-symbols-from.ts +13 -0
  61. package/src/domain/repo-fetch-result.ts +18 -0
  62. package/src/domain/repo-reference.ts +7 -0
  63. package/src/domain/search-cache-key.ts +16 -0
  64. package/src/domain/search-text.ts +29 -0
  65. package/src/domain/symbol-edges-from.ts +9 -0
  66. package/src/domain/symbol-edges-to.ts +9 -0
  67. package/src/domain/symbol-graph-generation.ts +14 -0
  68. package/src/domain/symbol-node-id.ts +13 -0
  69. package/src/domain/text-search-result.ts +15 -0
  70. package/src/domain/workspace-query-outcome.ts +24 -0
  71. package/src/domain/workspace-symbol.ts +14 -0
  72. package/src/index.ts +121 -0
  73. package/src/ports/code-intelligence-port.ts +38 -0
  74. package/src/ports/content-cache-port.ts +52 -0
  75. package/src/ports/git-port.ts +23 -0
  76. package/src/ports/repo-fetcher-port.ts +11 -0
  77. package/src/ports/search-cache-port.ts +15 -0
  78. package/src/ports/symbol-graph-port.ts +35 -0
  79. package/src/ports/symbol-index-port.ts +11 -0
  80. package/src/ports/text-search-port.ts +12 -0
  81. package/src/ports/workspace-port.ts +35 -0
  82. package/src/service.ts +961 -0
  83. package/src/version.ts +6 -0
package/src/index.ts ADDED
@@ -0,0 +1,121 @@
1
+ export { GitRepoFetcher, type GitRepoFetcherOptions } from "./adapters/git-repo-fetcher.ts";
2
+ export { InMemoryContentCache } from "./adapters/in-memory-content-cache.ts";
3
+ export { InMemorySearchCache, type InMemorySearchCacheOptions } from "./adapters/in-memory-search-cache.ts";
4
+ export { InMemorySymbolGraph } from "./adapters/in-memory-symbol-graph.ts";
5
+ export { InMemoryWorkspace } from "./adapters/in-memory-workspace.ts";
6
+ export { LocalFilesystemWorkspace, PathEscapesWorkspaceRoot } from "./adapters/local-filesystem-workspace.ts";
7
+ export { LocalGit } from "./adapters/local-git.ts";
8
+ export {
9
+ LanguageServerProcess,
10
+ LanguageServerProcessExited,
11
+ LanguageServerRequestTimedOut,
12
+ } from "./adapters/lsp/language-server-process.ts";
13
+ export { LspSymbolIndex } from "./adapters/lsp/lsp-symbol-index.ts";
14
+ export { ReadOnlyWorkspace, WorkspaceIsReadOnly } from "./adapters/read-only-workspace.ts";
15
+ export { RipgrepTextSearch } from "./adapters/ripgrep-text-search.ts";
16
+ export { deriveSourceManifest, type SourceManifest, SourceManifestLimitExceeded } from "./adapters/source-manifest.ts";
17
+ export { SqliteContentCache } from "./adapters/sqlite-content-cache.ts";
18
+ export { SqliteSearchCache, type SqliteSearchCacheOptions } from "./adapters/sqlite-search-cache.ts";
19
+ export { SqliteSymbolGraph } from "./adapters/sqlite-symbol-graph.ts";
20
+ export { TieredSearchCache } from "./adapters/tiered-search-cache.ts";
21
+ export { TreeSitterSymbolIndex } from "./adapters/tree-sitter/typescript-tree-sitter-symbol-index.ts";
22
+ export {
23
+ type ConnectLectorClientOptions,
24
+ connectLectorClient,
25
+ connectLectorClientAt,
26
+ type LectorClient,
27
+ remoteErrorIs,
28
+ } from "./client.ts";
29
+ export { resolveLectorPaths } from "./constants.ts";
30
+ export { buildLectorApp, type LectorDaemonOptions, serveMain, startLectorDaemon } from "./daemon.ts";
31
+ export { assertSafeGitArgument, UnsafeGitArgument } from "./domain/assert-safe-git-argument.ts";
32
+ export { assertSafePathSegment, UnsafePathSegment } from "./domain/assert-safe-path-segment.ts";
33
+ export { assertSafeRepoReference } from "./domain/assert-safe-repo-reference.ts";
34
+ export { assertSafeSearchQuery, UnsafeSearchQuery } from "./domain/assert-safe-search-query.ts";
35
+ export {
36
+ BoundedJobExecutor,
37
+ type BoundedJobExecutorOptions,
38
+ JobCapacityExceeded,
39
+ JobExecutorClosed,
40
+ JobNotFound,
41
+ type JobPriority,
42
+ type JobSnapshot,
43
+ } from "./domain/bounded-job-executor.ts";
44
+ export type { CallHierarchyEntry, IncomingCall, OutgoingCall } from "./domain/call-hierarchy.ts";
45
+ export type { CodeRange } from "./domain/code-range.ts";
46
+ export { type ContentHash, contentHashOf } from "./domain/content-hash.ts";
47
+ export type { Diagnostic, DiagnosticSeverity } from "./domain/diagnostic.ts";
48
+ export { diagnostics } from "./domain/diagnostics.ts";
49
+ export type { DocumentSymbolEntry } from "./domain/document-symbol.ts";
50
+ export { documentSymbols } from "./domain/document-symbols.ts";
51
+ export {
52
+ type EditOutcome,
53
+ type ExpectedHashEdit,
54
+ exactEdit,
55
+ StaleExpectedHash,
56
+ } from "./domain/exact-edit.ts";
57
+ export { findReferences } from "./domain/find-references.ts";
58
+ export { findWorkspaceSymbols } from "./domain/find-workspace-symbols.ts";
59
+ export type { GitDiffResult } from "./domain/git-diff-result.ts";
60
+ export type { GitLogEntry } from "./domain/git-log-entry.ts";
61
+ export type { GitStatusEntry, GitStatusSummary } from "./domain/git-status.ts";
62
+ export { goToDefinition } from "./domain/go-to-definition.ts";
63
+ export { goToImplementation } from "./domain/go-to-implementation.ts";
64
+ export type { Hover } from "./domain/hover.ts";
65
+ export { hoverAt } from "./domain/hover-at.ts";
66
+ export { incomingCalls } from "./domain/incoming-calls.ts";
67
+ export {
68
+ descriptorForExtension,
69
+ LANGUAGE_SERVER_DESCRIPTORS,
70
+ type LanguageServerDescriptor,
71
+ PYTHON_DESCRIPTOR,
72
+ TYPESCRIPT_DESCRIPTOR,
73
+ } from "./domain/language-server-descriptor.ts";
74
+ export { outgoingCalls } from "./domain/outgoing-calls.ts";
75
+ export { type PopulateSymbolGraphResult, populateSymbolGraph } from "./domain/populate-symbol-graph.ts";
76
+ export { prepareCallHierarchy } from "./domain/prepare-call-hierarchy.ts";
77
+ export { raceWorkspaceQuery } from "./domain/race-workspace-query.ts";
78
+ export { type RawRead, rawRead, WorkspaceEntryNotFound } from "./domain/raw-read.ts";
79
+ export { reachableSymbolsFrom } from "./domain/reachable-symbols-from.ts";
80
+ export { RepoFetchFailed, type RepoFetchResult } from "./domain/repo-fetch-result.ts";
81
+ export type { RepoReference } from "./domain/repo-reference.ts";
82
+ export { deriveSearchCacheKey, type SearchCacheKey } from "./domain/search-cache-key.ts";
83
+ export { searchText } from "./domain/search-text.ts";
84
+ export { symbolEdgesFrom } from "./domain/symbol-edges-from.ts";
85
+ export { symbolEdgesTo } from "./domain/symbol-edges-to.ts";
86
+ export type { SymbolGraphGeneration, WorkspaceCacheStatus } from "./domain/symbol-graph-generation.ts";
87
+ export { deriveSymbolNodeId, type SymbolNodeId } from "./domain/symbol-node-id.ts";
88
+ export type { TextSearchMatch, TextSearchResult } from "./domain/text-search-result.ts";
89
+ export type { WorkspaceQueryOutcome, WorkspaceQueryStatus } from "./domain/workspace-query-outcome.ts";
90
+ export type { WorkspaceLocation, WorkspaceSymbol } from "./domain/workspace-symbol.ts";
91
+ export type { CodeIntelligencePort } from "./ports/code-intelligence-port.ts";
92
+ export type { ContentCacheEntry, ContentCachePort, ContentSymbol } from "./ports/content-cache-port.ts";
93
+ export type { GitPort } from "./ports/git-port.ts";
94
+ export type { RepoFetcherPort } from "./ports/repo-fetcher-port.ts";
95
+ export type { SearchCachePort } from "./ports/search-cache-port.ts";
96
+ export type { SymbolEdgeKind, SymbolGraphPort, SymbolNode } from "./ports/symbol-graph-port.ts";
97
+ export type { SymbolIndexPort } from "./ports/symbol-index-port.ts";
98
+ export type { TextSearchOptions, TextSearchPort } from "./ports/text-search-port.ts";
99
+ export type { MissingWorkspaceEntry, PresentWorkspaceEntry, WorkspaceEntry, WorkspacePort } from "./ports/workspace-port.ts";
100
+ export {
101
+ type ClosableSymbolIndex,
102
+ CodeIntelligenceUnavailable,
103
+ createLectorService,
104
+ InvalidJobInput,
105
+ InvalidWorkspaceRoot,
106
+ JobWaitTooLong,
107
+ type LectorService,
108
+ type LectorServiceOptions,
109
+ NotAGitRepository,
110
+ OPERATION_NAMES,
111
+ type OperationInputs,
112
+ type OperationName,
113
+ type OperationOutputs,
114
+ RepoFetcherNotConfigured,
115
+ SymbolQueryUnavailable,
116
+ UnknownWorkspace,
117
+ UnsupportedJobOperation,
118
+ WorkspaceChangedDuringPopulation,
119
+ type WorkspaceId,
120
+ } from "./service.ts";
121
+ export { lectorVersion } from "./version.ts";
@@ -0,0 +1,38 @@
1
+ import type { CallHierarchyEntry, IncomingCall, OutgoingCall } from "../domain/call-hierarchy.ts";
2
+ import type { Diagnostic } from "../domain/diagnostic.ts";
3
+ import type { DocumentSymbolEntry } from "../domain/document-symbol.ts";
4
+ import type { Hover } from "../domain/hover.ts";
5
+ import type { WorkspaceLocation } from "../domain/workspace-symbol.ts";
6
+
7
+ /**
8
+ * CodeIntelligencePort -- the role a driven adapter plays for the semantic,
9
+ * position-based queries a real language server (not a syntax-only parser)
10
+ * can honestly answer: where is this actually defined (across files, through
11
+ * re-exports and aliasing), what does this resolve to, what's its type/doc,
12
+ * what's declared in this file. Deliberately separate from SymbolIndexPort
13
+ * (findSymbols, a fuzzy name search both an LSP and a tree-sitter backend
14
+ * can serve): tree-sitter has no type system and cannot honestly resolve
15
+ * cross-file references or hover types, so it does not implement this port
16
+ * at all rather than faking a degraded answer. Backed by the same warm LSP
17
+ * process findSymbols already keeps alive per workspace -- not a second one.
18
+ */
19
+ export interface CodeIntelligencePort {
20
+ /** Where the symbol at `at` is actually declared -- may cross files, may return more than one candidate. */
21
+ goToDefinition(at: WorkspaceLocation): Promise<WorkspaceLocation[]>;
22
+ /** Every concrete implementation of the interface/abstract member at `at` -- unlike goToDefinition, crosses a port/interface boundary into its real adapters. */
23
+ goToImplementation(at: WorkspaceLocation): Promise<WorkspaceLocation[]>;
24
+ /** Every project-wide usage of the symbol at `at`. */
25
+ findReferences(at: WorkspaceLocation, includeDeclaration: boolean): Promise<WorkspaceLocation[]>;
26
+ /** Type/doc information for the symbol at `at`, or undefined when the server has none. */
27
+ hover(at: WorkspaceLocation): Promise<Hover | undefined>;
28
+ /** Every symbol declared in one file, hierarchically. */
29
+ documentSymbols(path: string): Promise<DocumentSymbolEntry[]>;
30
+ /** Every diagnostic currently known for one file, as of the server's last analysis (push-based, not a live re-check). */
31
+ diagnostics(path: string): Promise<Diagnostic[]>;
32
+ /** The call-hierarchy root(s) the symbol at `at` resolves to -- usually zero or one. */
33
+ prepareCallHierarchy(at: WorkspaceLocation): Promise<CallHierarchyEntry[]>;
34
+ /** Every real caller of the symbol at `at`, project-wide. */
35
+ incomingCalls(at: WorkspaceLocation): Promise<IncomingCall[]>;
36
+ /** Every function/method the symbol at `at` itself calls. */
37
+ outgoingCalls(at: WorkspaceLocation): Promise<OutgoingCall[]>;
38
+ }
@@ -0,0 +1,52 @@
1
+ import type { ContentHash } from "../domain/content-hash.ts";
2
+
3
+ /**
4
+ * A symbol as derivable purely from content -- deliberately NOT WorkspaceSymbol:
5
+ * WorkspaceSymbol.location.path is a property of which *path* currently holds
6
+ * this content, not a property of the content itself. Doc 38db976d states two
7
+ * different files with byte-identical content share one cache entry; if this
8
+ * type carried a path, the second file to query a shared hash would get back
9
+ * the *first* file's path attached to its own symbols -- a real, silent
10
+ * correctness bug this type exists specifically to make impossible. Callers
11
+ * reattach the current path when turning a ContentSymbol back into a
12
+ * WorkspaceSymbol for a specific query.
13
+ */
14
+ export interface ContentSymbol {
15
+ readonly name: string;
16
+ readonly kind: string;
17
+ readonly line: number;
18
+ readonly character: number;
19
+ readonly containerName?: string;
20
+ }
21
+
22
+ /**
23
+ * One shared, content-addressed cache entry. May hold any subset of the
24
+ * lenses derived from the same immutable content -- raw bytes, extracted
25
+ * symbols, and (later) whatever else is derived from that content -- never
26
+ * two independently-invalidated entries for the same hash.
27
+ */
28
+ export interface ContentCacheEntry {
29
+ readonly rawContent?: string;
30
+ readonly symbols?: readonly ContentSymbol[];
31
+ }
32
+
33
+ /**
34
+ * ContentCachePort -- the one store both the filesystem lens (raw text) and
35
+ * the code-intelligence lens (parsed symbols) read and write, keyed by
36
+ * ContentHash. Populated incrementally: whichever lens touches a hash first
37
+ * creates the entry with just that lens present; a later put for the other
38
+ * lens on the *same* hash adds to the existing entry rather than replacing
39
+ * it -- this is what makes bidirectional warming possible by construction,
40
+ * not by hand-wiring each pair of operations to know about each other.
41
+ *
42
+ * Content-addressed, so there is no invalidation method: a hash's entry
43
+ * never becomes wrong (the content it was computed from cannot change
44
+ * without producing a different hash). What can change is which hash a
45
+ * *path* currently points to -- that mapping lives above this port, not in
46
+ * it (see WorkspacePort).
47
+ */
48
+ export interface ContentCachePort {
49
+ get(hash: ContentHash): Promise<ContentCacheEntry | undefined>;
50
+ putRawContent(hash: ContentHash, content: string): Promise<void>;
51
+ putSymbols(hash: ContentHash, symbols: readonly ContentSymbol[]): Promise<void>;
52
+ }
@@ -0,0 +1,23 @@
1
+ import type { GitDiffResult } from "../domain/git-diff-result.ts";
2
+ import type { GitLogEntry } from "../domain/git-log-entry.ts";
3
+ import type { GitStatusSummary } from "../domain/git-status.ts";
4
+
5
+ /**
6
+ * GitPort -- the role a driven adapter plays for read-only git queries
7
+ * against a workspace's repository. Deliberately read-only: Lector's git
8
+ * layer never constructs a caller-influenced `-c` config override or
9
+ * mutating flag (clone/push/merge), the exact surface simple-git's own
10
+ * history shows is where git CLI injection CVEs live. A workspace with no
11
+ * `.git` directory is a real, expected case (not every registered
12
+ * workspace is a git repository), not an error condition callers must
13
+ * special-case per operation.
14
+ */
15
+ export interface GitPort {
16
+ /** Undefined when the workspace root is not inside a git repository. */
17
+ isGitRepository(): Promise<boolean>;
18
+ status(): Promise<GitStatusSummary>;
19
+ /** Most recent commits first, bounded to maxCount. */
20
+ log(maxCount: number): Promise<readonly GitLogEntry[]>;
21
+ /** Diff against `ref` (defaults to HEAD) of the current working tree, bounded to maxBytes. */
22
+ diff(ref: string | undefined, maxBytes: number): Promise<GitDiffResult>;
23
+ }
@@ -0,0 +1,11 @@
1
+ import type { RepoFetchResult } from "../domain/repo-fetch-result.ts";
2
+ import type { RepoReference } from "../domain/repo-reference.ts";
3
+
4
+ /**
5
+ * RepoFetcherPort -- ensures a shallow local clone of an external repo exists, content-addressed
6
+ * by (host, owner, repo, ref). A caller never mutates a returned checkout; it's a foreign
7
+ * directory to read and analyze, not one the caller owns.
8
+ */
9
+ export interface RepoFetcherPort {
10
+ fetch(reference: RepoReference): Promise<RepoFetchResult>;
11
+ }
@@ -0,0 +1,15 @@
1
+ import type { SearchCacheKey } from "../domain/search-cache-key.ts";
2
+ import type { TextSearchResult } from "../domain/text-search-result.ts";
3
+
4
+ /**
5
+ * SearchCachePort -- caches searchText results by (workspaceId, query, options), a genuinely
6
+ * different shape from ContentCachePort's per-content-hash keying: a search result isn't
7
+ * derived from one file's content, it's derived from a query run against a whole workspace at
8
+ * a point in time, so it needs its own TTL rather than ContentCachePort's "never invalidates"
9
+ * contract (which only holds because content-addressing makes invalidation structurally
10
+ * impossible to need -- that reasoning doesn't apply here).
11
+ */
12
+ export interface SearchCachePort {
13
+ get(key: SearchCacheKey): Promise<TextSearchResult | undefined>;
14
+ set(key: SearchCacheKey, result: TextSearchResult): Promise<void>;
15
+ }
@@ -0,0 +1,35 @@
1
+ import type { SymbolGraphGeneration } from "../domain/symbol-graph-generation.ts";
2
+ import type { SymbolNodeId } from "../domain/symbol-node-id.ts";
3
+ import type { WorkspaceLocation } from "../domain/workspace-symbol.ts";
4
+
5
+ /** A node's own identity and declaration -- what's shown, not derived relationships. */
6
+ export interface SymbolNode {
7
+ readonly id: SymbolNodeId;
8
+ readonly name: string;
9
+ readonly kind: string;
10
+ readonly location: WorkspaceLocation;
11
+ }
12
+
13
+ export type SymbolEdgeKind = "calls" | "references" | "contains";
14
+
15
+ /**
16
+ * SymbolGraphPort -- a persisted, queryable graph of symbol relationships,
17
+ * populated by a batch indexing pass rather than answered live per query,
18
+ * so multi-hop questions (transitive callers, reachability) don't require
19
+ * chaining many sequential LSP calls. maxDepth is required: an unbounded
20
+ * traversal has no place in a bounded-resource daemon.
21
+ */
22
+ export interface SymbolGraphPort {
23
+ addNode(node: SymbolNode): Promise<void>;
24
+ getNode(id: SymbolNodeId): Promise<SymbolNode | undefined>;
25
+ addEdge(from: SymbolNodeId, to: SymbolNodeId, kind: SymbolEdgeKind): Promise<void>;
26
+ /** Direct out-edges from `id` -- who/what `id` points to. */
27
+ edgesFrom(id: SymbolNodeId, kind?: SymbolEdgeKind): Promise<readonly SymbolNodeId[]>;
28
+ /** Direct in-edges to `id` -- who/what points to `id`. */
29
+ edgesTo(id: SymbolNodeId, kind?: SymbolEdgeKind): Promise<readonly SymbolNodeId[]>;
30
+ /** Every node reachable from `id` by following out-edges, up to `maxDepth` hops, excluding `id` itself. */
31
+ reachableFrom(id: SymbolNodeId, options: { maxDepth: number; kind?: SymbolEdgeKind }): Promise<readonly SymbolNodeId[]>;
32
+ getGeneration(): Promise<SymbolGraphGeneration | undefined>;
33
+ setGeneration(generation: SymbolGraphGeneration): Promise<void>;
34
+ close(): Promise<void>;
35
+ }
@@ -0,0 +1,11 @@
1
+ import type { WorkspaceSymbol } from "../domain/workspace-symbol.ts";
2
+
3
+ /**
4
+ * SymbolIndexPort -- the role a driven adapter plays for symbol queries:
5
+ * given a fuzzy query string, return matching workspace symbols. Implemented
6
+ * by an LSP-backed adapter and a tree-sitter-backed adapter; both satisfy
7
+ * this same interface.
8
+ */
9
+ export interface SymbolIndexPort {
10
+ findSymbols(query: string): Promise<WorkspaceSymbol[]>;
11
+ }
@@ -0,0 +1,12 @@
1
+ import type { TextSearchResult } from "../domain/text-search-result.ts";
2
+
3
+ export interface TextSearchOptions {
4
+ readonly maxMatches: number;
5
+ /** Bound on the total bytes of matched line text returned, not on bytes scanned. */
6
+ readonly maxBytes: number;
7
+ }
8
+
9
+ /** TextSearchPort -- multi-file text/regex search scoped to one workspace root. */
10
+ export interface TextSearchPort {
11
+ search(rootPath: string, query: string, options: TextSearchOptions): Promise<TextSearchResult>;
12
+ }
@@ -0,0 +1,35 @@
1
+ import type { ContentHash } from "../domain/content-hash.ts";
2
+
3
+ /** A workspace entry that does not (yet) exist. Distinguished from empty content. */
4
+ export interface MissingWorkspaceEntry {
5
+ readonly exists: false;
6
+ }
7
+
8
+ /** A workspace entry that exists, with its raw content. */
9
+ export interface PresentWorkspaceEntry {
10
+ readonly exists: true;
11
+ readonly content: string;
12
+ }
13
+
14
+ export type WorkspaceEntry = MissingWorkspaceEntry | PresentWorkspaceEntry;
15
+
16
+ /**
17
+ * WorkspacePort — the role a driven adapter plays for Lector's core: give the
18
+ * domain raw access to one workspace's entries, and apply an expected-hash-
19
+ * guarded edit atomically. `writeEntry` must reject a stale `expectedHash`
20
+ * (see StaleExpectedHash) rather than silently overwrite.
21
+ *
22
+ * Implemented by InMemoryWorkspace for the walking skeleton and contract
23
+ * tests, and later by a local-filesystem adapter for real workspaces.
24
+ */
25
+ export interface WorkspacePort {
26
+ readEntry(path: string): Promise<WorkspaceEntry>;
27
+
28
+ /**
29
+ * @param expectedHash The hash the caller last observed at `path`, or
30
+ * `null` to assert the path does not yet exist.
31
+ * @throws StaleExpectedHash when the entry's current hash does not match
32
+ * `expectedHash`.
33
+ */
34
+ writeEntry(path: string, expectedHash: ContentHash | null, content: string): Promise<{ previousHash: ContentHash | null; newHash: ContentHash }>;
35
+ }