llmnav 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/LICENSE +21 -0
  3. package/README.md +294 -0
  4. package/ROADMAP.md +71 -0
  5. package/bin/llmnav.js +16 -0
  6. package/docs/agent-integration.md +114 -0
  7. package/docs/api.md +290 -0
  8. package/docs/architecture.md +286 -0
  9. package/docs/benchmarking.md +164 -0
  10. package/docs/ci.md +196 -0
  11. package/docs/cli.md +233 -0
  12. package/docs/configuration.md +117 -0
  13. package/docs/editor-integration.md +29 -0
  14. package/docs/faq.md +59 -0
  15. package/docs/graph.md +92 -0
  16. package/docs/language-examples.md +130 -0
  17. package/docs/migration.md +130 -0
  18. package/docs/performance-v0.2.md +42 -0
  19. package/docs/provider-neutral-integration.md +66 -0
  20. package/docs/publishing.md +86 -0
  21. package/docs/quickstart.md +139 -0
  22. package/docs/research.md +31 -0
  23. package/docs/spec.md +424 -0
  24. package/examples/provider-neutral-host.d.mts +17 -0
  25. package/examples/provider-neutral-host.mjs +40 -0
  26. package/package.json +79 -0
  27. package/schema/config.schema.json +296 -0
  28. package/src/agent-protocol.js +117 -0
  29. package/src/agent-tools.js +61 -0
  30. package/src/agents.js +127 -0
  31. package/src/boundaries.js +50 -0
  32. package/src/changes.js +168 -0
  33. package/src/cli.js +459 -0
  34. package/src/config.js +305 -0
  35. package/src/contracts.js +70 -0
  36. package/src/declaration.js +334 -0
  37. package/src/doctor.js +124 -0
  38. package/src/editor.js +107 -0
  39. package/src/evaluation.js +67 -0
  40. package/src/files.js +81 -0
  41. package/src/formatter.js +23 -0
  42. package/src/generator.js +528 -0
  43. package/src/graph-input.js +157 -0
  44. package/src/graph.js +403 -0
  45. package/src/incremental.js +262 -0
  46. package/src/index.d.ts +673 -0
  47. package/src/index.js +115 -0
  48. package/src/initializer.js +137 -0
  49. package/src/inverted-index.js +350 -0
  50. package/src/parser.js +449 -0
  51. package/src/project.js +65 -0
  52. package/src/prompt-bundle.js +108 -0
  53. package/src/registry.js +107 -0
  54. package/src/sarif.js +70 -0
  55. package/src/search-shards.js +75 -0
  56. package/src/search.js +636 -0
  57. package/src/spec.d.ts +27 -0
  58. package/src/spec.js +237 -0
  59. package/src/tokenizer.js +37 -0
  60. package/src/transaction.js +557 -0
  61. package/src/util.js +256 -0
  62. package/src/validator.js +635 -0
  63. package/templates/file-card.txt +8 -0
  64. package/templates/lexicon.json +7 -0
  65. package/templates/line-card.txt +9 -0
  66. package/templates/module-card.txt +9 -0
  67. package/templates/queries.jsonl +1 -0
  68. package/templates/symbol-card.txt +10 -0
@@ -0,0 +1,117 @@
1
+ # Configuration reference
2
+
3
+ `llmnav init` creates `.llmnav/config.json` and a local JSON schema at `.llmnav/schema/config.schema.json`.
4
+
5
+ ## Complete shape
6
+
7
+ ```json
8
+ {
9
+ "$schema": "./schema/config.schema.json",
10
+ "version": 1,
11
+ "repositoryId": "example-service",
12
+ "sourceRoots": ["src", "packages"],
13
+ "includeExtensions": [".ts", ".tsx", ".go", ".rs", ".py"],
14
+ "excludeDirectories": ["node_modules", "dist", "target"],
15
+ "excludeFiles": ["*.generated.*", "*.gen.*"],
16
+ "coverageRules": [],
17
+ "graph": {
18
+ "indexFiles": []
19
+ },
20
+ "lint": {
21
+ "maxRoleLength": 180,
22
+ "maxSearchTerms": 6,
23
+ "minSearchTerms": 2,
24
+ "maxInvariants": 4,
25
+ "maxEffects": 6,
26
+ "maxRelations": 6,
27
+ "maxBlockBytes": {
28
+ "file": 400,
29
+ "module": 1200,
30
+ "symbol": 900
31
+ },
32
+ "maxSemanticRatio": 0.015,
33
+ "minimumSourceBytesForRatio": 50000,
34
+ "searchTermSaturation": 0.05,
35
+ "minimumCardsForSaturation": 20,
36
+ "genericSearchTerms": ["service", "manager", "handler"],
37
+ "vagueRoleWords": ["handle", "manage", "process"],
38
+ "strictRisks": ["auth", "money", "privacy"],
39
+ "additionalEffects": [],
40
+ "additionalRisks": [],
41
+ "additionalRelations": [],
42
+ "requireCanonicalOrder": true,
43
+ "requireCanonicalFormatting": true
44
+ },
45
+ "generation": {
46
+ "cacheDirectory": ".llmnav/cache",
47
+ "moduleDepth": 2,
48
+ "searchShardSize": 0,
49
+ "repositoryCatalogStabilities": ["architecture"],
50
+ "moduleCatalogStabilities": ["architecture", "contract"]
51
+ },
52
+ "evaluation": {
53
+ "queryFile": ".llmnav/eval/queries.jsonl",
54
+ "minimumRecallAt1": 0.75,
55
+ "minimumRecallAt5": 0.9
56
+ }
57
+ }
58
+ ```
59
+
60
+ Initialization writes the full default object. Later releases may add defaults without requiring every repository to rewrite its file because configuration is merged with the active profile. Unknown properties are rejected at every supported level, preventing misspelled enforcement settings from being silently ignored.
61
+
62
+ ## Repository identity
63
+
64
+ `repositoryId` distinguishes generated catalogs and future cross-repository relations. Use a stable lowercase repository name. Renaming an npm package does not require changing it when existing cross-repository IDs depend on the old value.
65
+
66
+ ## Source selection
67
+
68
+ `sourceRoots` defines the only directory trees scanned during a normal command. CLI path arguments narrow that set for formatting or diagnostics.
69
+
70
+ Every source root must remain inside the repository and cannot be a symbolic link. Nested symbolic-link entries are not traversed.
71
+
72
+ `includeExtensions` is an allowlist. LLMNav does not scan Markdown or YAML by default because documentation examples routinely contain card syntax.
73
+
74
+ `excludeDirectories` matches directory names at any depth. `excludeFiles` uses repository-relative globs with `*`, `?`, and `**`.
75
+
76
+ ## Coverage rules
77
+
78
+ Coverage is opt-in and path-specific.
79
+
80
+ ```json
81
+ {
82
+ "name": "payment webhooks",
83
+ "match": ["src/webhooks/payment/**/*.ts"],
84
+ "scope": "file",
85
+ "requiredFields": ["effect", "risk", "invariant"]
86
+ }
87
+ ```
88
+
89
+ A coverage rule checks files already selected by `sourceRoots` and extension filters. Its fields are closed and `requiredFields` must name valid LLMNav keys. It should target a real architectural boundary, never an entire source tree.
90
+
91
+ ## Lint profile
92
+
93
+ Byte and field-count limits prevent semantic cards from becoming mini-documents. `maxSemanticRatio` produces a warning after the scanned source exceeds `minimumSourceBytesForRatio`.
94
+
95
+ Search saturation warns when one exact search phrase appears in too many cards. Repository-wide vocabulary such as a product name usually belongs in module IDs or aliases rather than every card.
96
+
97
+ `additionalEffects`, `additionalRisks`, and `additionalRelations` extend controlled vocabularies. Add namespaced values rather than weakening validation globally. Each entry is a lower-case controlled identifier, not a complete source expression. Configure `queue.publish`, then write `effect=queue.publish(job_ready)` in a card. Configuration values such as `queue.publish(name)`, `Data Loss`, and `calls>` are rejected.
98
+
99
+ ## Generation
100
+
101
+ `generation.cacheDirectory` must be a relative path below `.llmnav/`. Generation removes and rebuilds that directory, so it cannot be pointed at ordinary source or arbitrary repository paths.
102
+
103
+ `moduleDepth` groups IDs by their first segments. With a depth of two, `auth.session.rotate` belongs to the `auth.session` catalog.
104
+
105
+ `searchShardSize` is `0` by default. Set a positive card limit in very large repositories to emit deterministic `search-shards.json` and `search-shards/NNNN.json` artifacts. Shards are sliced from the compact index without retokenizing cards. The compatible `search-index.json` remains available for existing consumers and preserves ranking behavior.
106
+
107
+ Stability filters decide which cards enter long-lived repository and module prompt material. Every card remains in `index.json` regardless of catalog filters.
108
+
109
+ ## Graph inputs
110
+
111
+ `graph.indexFiles` lists optional generated definition and reference indexes. Paths must remain inside the repository and are loaded as data only. See [Repository graph](graph.md) for the schema and validation contract.
112
+
113
+ ## Evaluation
114
+
115
+ `queryFile` must remain inside the repository. Each JSONL record contains task text and one or more accepted IDs.
116
+
117
+ The thresholds gate `llmnav eval`. Keep the default until the query set represents real work, then raise the values based on measured retrieval quality.
@@ -0,0 +1,29 @@
1
+ # Editor integration
2
+
3
+ ## Deterministic diagnostics
4
+
5
+ ```sh
6
+ llmnav check --format editor
7
+ ```
8
+
9
+ The schemaVersion 1 report groups diagnostics by repository-relative path. Ranges use zero-based line and character coordinates, while each item retains numeric severity, textual level, stable diagnostic code, source, and message. The output contains no absolute workspace path, timestamp, editor identity, or platform separator.
10
+
11
+ An editor adapter should bind the repository root itself, map each relative path to its workspace document, and replace the previous LLMNav diagnostics for that document. Do not let model-generated input choose the root.
12
+
13
+ ## Visual Studio Code
14
+
15
+ ```sh
16
+ llmnav editor vscode
17
+ ```
18
+
19
+ This prints a schemaVersion 1 integration envelope whose `config` value is a VS Code `tasks.json` configuration. The task runs the local package with `npm exec -- llmnav check` and maps LLMNav's standard text output into the Problems panel through a custom problem matcher.
20
+
21
+ If `.vscode/tasks.json` already exists, merge the generated task into its `tasks` array instead of replacing unrelated tasks. The generated command performs no file mutation.
22
+
23
+ The matcher follows the current [VS Code task and problem matcher contract](https://code.visualstudio.com/docs/debugtest/tasks): repository-relative file, line, column, severity, code, and message capture groups are declared explicitly.
24
+
25
+ ## Other editors
26
+
27
+ Editors and language-client wrappers should consume `check --format editor` or call `diagnosticsToEditor(diagnostics)`. This keeps coordinate conversion and severity mapping in LLMNav while leaving document URI construction, lifecycle, debounce, and Problems-panel ownership to the editor integration.
28
+
29
+ LLMNav does not run a watcher or language server. A host decides when to invoke checks and how to cancel or replace stale results.
package/docs/faq.md ADDED
@@ -0,0 +1,59 @@
1
+ # FAQ
2
+
3
+ ## Should every function receive a card?
4
+
5
+ No. That makes retrieval noisier and creates a second copy of the program in prose. Start with architectural modules, public boundaries, risky workflows, orchestration points, and non-obvious invariants.
6
+
7
+ ## Why not use JSDoc or docstrings alone?
8
+
9
+ JSDoc and docstrings target generated human documentation and language tooling. They do not define durable semantic IDs, controlled effects and risks, append-only catalog order, relation validation, deterministic search artifacts, or search regression gates. They can coexist with LLMNav cards.
10
+
11
+ ## Does v0.5 change the `llmnav/1` syntax?
12
+
13
+ No. The source grammar remains `llmnav/1`. v0.5 adds independently versioned agent-operation, prompt-bundle, and editor-diagnostic contracts beside the existing schemaVersion 1 `index.json`.
14
+
15
+ ## Does every query still tokenize every card?
16
+
17
+ No. `search-index.json` stores a sorted token dictionary, compact posting lists, and normalized phrase documents. A query tokenizes task text and reads only matching posting lists, while preserving v0.1 ranking behavior. Missing or incompatible search indexes are rebuilt in memory from `index.json`.
18
+
19
+ ## What is the difference between `file-state.json` and `stat-hints.json`?
20
+
21
+ `file-state.json` is deterministic generated cache and can be committed. It stores content hashes and parsed records.
22
+
23
+ `stat-hints.json` is volatile and ignored. It stores size, mtime, and ctime only to avoid reading unchanged files. Deleting it changes performance, not generated output.
24
+
25
+ ## What happens when generation fails halfway through?
26
+
27
+ The live cache is never edited one file at a time. LLMNav builds and verifies a staged cache, journals the directory swap, and retains a backup until commit. A thrown error rolls back immediately. A killed process is recovered by the next query, generation, or doctor command.
28
+
29
+ ## Why is the built-in search lexical?
30
+
31
+ It is deterministic, local, measurable, multilingual enough for routing aliases, and requires no model or vector database. Embeddings should be considered only after real evaluation queries expose a persistent lexical gap.
32
+
33
+ ## Why are paths and callers forbidden in cards?
34
+
35
+ They change during routine refactoring and can be derived more accurately. LLMNav writes current paths and declarations into generated indexes. Generated structure is never copied back into source comments.
36
+
37
+ ## Why commit `.llmnav/cache`?
38
+
39
+ The files are deterministic agent context. Committing them makes drift reviewable, lets agents query without an initial setup run, and allows CI to verify byte equality. Do not commit `.llmnav/state`, `.llmnav/.transactions`, or an interrupted transaction journal.
40
+
41
+ ## Does LLMNav execute project code?
42
+
43
+ No. Version 0.5 reads text and configured repository-local JSON files, then writes generated artifacts or explicitly requested card formatting. Its tool dispatcher remains local and has no plugin loader, install hook, or network request.
44
+
45
+ ## Can cards be written in Korean?
46
+
47
+ Yes, but one repository language produces more consistent retrieval. Keep source cards in the codebase's primary language and place Korean task phrases in `.llmnav/lexicon.json`.
48
+
49
+ ## Is the generated cache a model prompt cache?
50
+
51
+ No. It is stable prompt material designed to be placed before volatile task context. The model provider or agent harness controls actual prompt caching.
52
+
53
+ ## Are benchmark numbers guaranteed on another machine?
54
+
55
+ No. The repository records exact measured values, environment, fixture size, and methodology. Correctness and memory limits are regression gates; wall-clock numbers must be remeasured on the target machine.
56
+
57
+ ## Can multiple repositories share IDs?
58
+
59
+ Each repository has a `repositoryId`. Configured graph inputs can add qualified `repository-id/semantic.id` nodes to one explicit workspace. Qualified IDs resolve exactly; unqualified external IDs resolve only when unique. LLMNav does not scan sibling directories or fetch remote repositories automatically.
package/docs/graph.md ADDED
@@ -0,0 +1,92 @@
1
+ # Repository graph
2
+
3
+ ## Optional generated inputs
4
+
5
+ LLMNav never executes repository code to discover definitions or references. Configure explicit generated JSON inputs instead:
6
+
7
+ ```json
8
+ {
9
+ "graph": {
10
+ "indexFiles": [".llmnav/imports/payments.json"]
11
+ }
12
+ }
13
+ ```
14
+
15
+ Every path must stay inside the repository, use forward slashes, avoid parent traversal, and pass symlink traversal checks. Missing, malformed, or unsafe inputs produce blocking diagnostic `LNV014` during `check` and `generate`.
16
+
17
+ ## Input schema
18
+
19
+ ```json
20
+ {
21
+ "schemaVersion": 1,
22
+ "repositoryId": "payments",
23
+ "generator": "scip-adapter@1",
24
+ "definitions": [
25
+ {
26
+ "id": "billing.capture.run",
27
+ "symbol": "runCapture",
28
+ "path": "src/capture.ts",
29
+ "line": 8,
30
+ "kind": "function"
31
+ }
32
+ ],
33
+ "references": [
34
+ {
35
+ "from": "billing.capture.run",
36
+ "to": "accounts/auth.session.rotate",
37
+ "kind": "calls",
38
+ "path": "src/capture.ts",
39
+ "line": 19,
40
+ "confidence": 0.9
41
+ }
42
+ ]
43
+ }
44
+ ```
45
+
46
+ Local IDs are qualified with the input `repositoryId`. A repository-qualified ID uses `repository-id/semantic.id`. Definitions are sorted by qualified ID and references by source, target, kind, path, and line.
47
+
48
+ `confidence` is a number from 0 to 1 and defaults to `0.8`. It records the generator's evidence strength; it does not grant authority to modify source or override semantic cards. Unknown fields, duplicate definitions, unsafe paths, invalid IDs, and out-of-range confidence values are rejected.
49
+
50
+ This normalized input is deliberately smaller than SCIP, LSIF, or a language-server database. External adapters own format conversion. LLMNav owns validation, qualification, graph generation, ranking, and bounded context consumption.
51
+
52
+ ## Generated graph
53
+
54
+ Generation writes `.llmnav/cache/graph.json` schemaVersion 1. Nodes use `repository-id/semantic.id` keys and retain local card roles, generated locations, imported definitions, external status, and unresolved status.
55
+
56
+ Edges contain a stable SHA-256 ID, qualified `from` and `to` keys, kind, confidence, and provenance:
57
+
58
+ | Provenance | Default confidence | Evidence |
59
+ | --- | --- | --- |
60
+ | `source-card` | `1.0` | explicit semantic `rel` field |
61
+ | `local-import` | `0.85` | deterministically resolved relative source import |
62
+ | `generated-index` | supplied value, default `0.8` | configured definition/reference index |
63
+
64
+ Provenance records the owning input or source file, optional referenced path and line, and generator identity. Missing targets remain unresolved placeholder nodes rather than disappearing. The graph is part of the transactional cache and its bytes are covered by `manifest.json`.
65
+
66
+ ## Incremental invalidation
67
+
68
+ Generation also writes `.llmnav/cache/graph-state.json` schemaVersion 1. It is a disposable, content-addressed acceleration artifact split into one partition per local card and configured graph input.
69
+
70
+ Local partition hashes cover graph-relevant card fields plus a repository path-to-ID resolution hash. A body-only edit therefore reuses the graph partition, while a role, relation, import, declaration location, or import-resolution change rebuilds it. Imported partitions use the validated input content hash. Removed cards and inputs remove their partitions and edges.
71
+
72
+ Malformed, incompatible, or wrong-repository state is ignored and rebuilt from current source and configured inputs. Incremental and forced-full generation must serialize byte-identical `graph.json` and `graph-state.json` files. Both files are published in the same recoverable cache transaction and covered by `manifest.json`.
73
+
74
+ ## Ranking and context
75
+
76
+ Lexical retrieval remains the seed authority. LLMNav takes at most the first three lexical seeds and applies a bounded one-hop graph bonus:
77
+
78
+ ```text
79
+ seed score × 0.08 × edge confidence × direction weight
80
+ ```
81
+
82
+ Outgoing edges use direction weight `1.0`; incoming edges use `0.6`. An edge cannot create a result for a node that has no loaded card. Result reasons retain direction, kind, and confidence such as `graph-out:calls@0.90`.
83
+
84
+ `llmnav context` traverses confidence-ordered incoming and outgoing edges breadth-first. `--depth`, `--budget`, and `--max-edges` independently bound traversal and output. Selected cards are packed before compact edge evidence so a small token budget preserves the requested root card first.
85
+
86
+ ## Workspace and cross-repository IDs
87
+
88
+ Configured graph inputs may contribute nodes from multiple repository IDs. LLMNav treats those imported nodes as one explicit logical workspace; it does not scan sibling directories automatically.
89
+
90
+ Qualified IDs such as `accounts/auth.session.rotate` resolve exactly. An unqualified ID first prefers the local repository. If no local node exists, it resolves only when exactly one workspace node has that semantic ID. Multiple matches return an ambiguity with sorted qualified candidates and require the caller to choose one.
91
+
92
+ `show` renders external definitions when no local card exists. `context` can start from an external or unresolved graph node and pack its definitions and bounded edge evidence. External nodes do not become searchable lexical cards merely because they were imported.
@@ -0,0 +1,130 @@
1
+ # Language examples
2
+
3
+ ## TypeScript and JavaScript
4
+
5
+ Place file or module cards before imports. Symbol cards may precede JSDoc and decorators.
6
+
7
+ ```ts
8
+ /* llmnav/1 symbol
9
+ id=billing.credit.reserve
10
+ role=Reserve credits before an external generation job starts.
11
+ search=credit hold|reserve credits|generation billing
12
+ invariant=Captured credits never exceed the active reservation.
13
+ effect=db.write(credit_reservations)
14
+ risk=concurrency
15
+ stability=contract
16
+ */
17
+ /** Creates an expiring reservation. */
18
+ export async function reserveCredits() {}
19
+ ```
20
+
21
+ ## Go
22
+
23
+ A Go package card belongs after `package` and before `import` so it does not interfere with the normal package doc comment.
24
+
25
+ ```go
26
+ package session
27
+
28
+ /* llmnav/1 module
29
+ id=auth.session
30
+ role=Own refresh-token issuance, rotation, replay detection, and revocation.
31
+ owns=refresh-token family|session revocation
32
+ excludes=access-token signing|user profile storage
33
+ search=session lifecycle|token family|session revocation
34
+ invariant=One token family has at most one live refresh token.
35
+ stability=architecture
36
+ */
37
+
38
+ import "context"
39
+ ```
40
+
41
+ A symbol card belongs before the ordinary exported declaration comment.
42
+
43
+ ```go
44
+ /* llmnav/1 symbol
45
+ id=auth.session.rotate
46
+ role=Rotate one refresh-token family atomically and reject replayed tokens.
47
+ search=refresh token|token rotation|replay detection
48
+ stability=contract
49
+ */
50
+
51
+ // RotateSession returns the replacement token.
52
+ func RotateSession(ctx context.Context) error { return nil }
53
+ ```
54
+
55
+ ## Rust
56
+
57
+ ```rust
58
+ /* llmnav/1 symbol
59
+ id=privacy.export.prepare
60
+ role=Build one immutable export manifest from the user's authorized data snapshot.
61
+ search=data export|privacy archive|export manifest
62
+ invariant=Every manifest entry belongs to the authorized subject.
63
+ effect=db.read(privacy_snapshot)|fs.write
64
+ risk=privacy
65
+ rel=test>privacy.export.prepare.contract
66
+ stability=contract
67
+ */
68
+ pub async fn prepare_export() -> Result<Manifest, Error> {
69
+ todo!()
70
+ }
71
+ ```
72
+
73
+ ## Python
74
+
75
+ Use explicit `# /llmnav` termination. The card may precede decorators.
76
+
77
+ ```py
78
+ # llmnav/1 symbol
79
+ # id=billing.payment.apply-provider-event
80
+ # role=Apply one provider event idempotently to the payment ledger.
81
+ # search=duplicate webhook|provider event|payment idempotency
82
+ # invariant=One provider event ID changes the ledger at most once.
83
+ # effect=db.write(payment_ledger)
84
+ # risk=money
85
+ # rel=test>billing.payment.apply-provider-event.contract
86
+ # stability=contract
87
+ # /llmnav
88
+ @transactional
89
+ def apply_provider_event(event):
90
+ ...
91
+ ```
92
+
93
+ ## SQL
94
+
95
+ Use `--` line cards for migration files and terminate explicitly.
96
+
97
+ ```sql
98
+ -- llmnav/1 file
99
+ -- id=billing.migration.credit-reservations
100
+ -- role=Create the reservation ledger without changing existing credit balances.
101
+ -- search=credit migration|reservation ledger|billing schema
102
+ -- invariant=Existing account balances remain unchanged after migration.
103
+ -- effect=db.write(schema)
104
+ -- risk=migration|money
105
+ -- rel=test>billing.migration.credit-reservations.contract
106
+ -- stability=contract
107
+ -- /llmnav
108
+
109
+ CREATE TABLE credit_reservations (...);
110
+ ```
111
+
112
+ ## Svelte, Astro, Vue, and HTML
113
+
114
+ Use a block comment inside the script region when the card attaches to a script declaration. Use an HTML comment for a component or page file card.
115
+
116
+ ```svelte
117
+ <!-- llmnav/1 file
118
+ id=ui.checkout.page
119
+ role=Render checkout state and submit one payment confirmation request.
120
+ search=checkout page|payment confirmation|purchase flow
121
+ effect=net.call(billing.confirm)
122
+ risk=money
123
+ rel=test>ui.checkout.page.contract
124
+ stability=contract
125
+ -->
126
+ ```
127
+
128
+ ## Shell and YAML-adjacent files
129
+
130
+ Use `#` cards only in source types included by configuration. LLMNav does not scan Markdown or YAML by default because documentation examples and configuration comments would create false cards.
@@ -0,0 +1,130 @@
1
+ # Gradual migration
2
+
3
+ ## Upgrade to 0.5
4
+
5
+ No source-card migration is required. Keep every `llmnav/1` comment and the existing `.llmnav/cache/index.json` consumer contract.
6
+
7
+ Upgrade and regenerate:
8
+
9
+ ```sh
10
+ npm install --save-dev llmnav@^0.5.1
11
+ npx llmnav init --agents all
12
+ npx llmnav generate
13
+ npx llmnav doctor
14
+ ```
15
+
16
+ The regeneration preserves the schemaVersion 1 primary index and existing graph artifacts, then adds deterministic `prompt-prefix.json`. Initialization refreshes the managed agent protocol so structured-tool hosts learn the trusted-root boundary. Volatile `state/`, `.transactions/`, and `generation-transaction.json` remain ignored.
17
+
18
+ Review `generate --json` during the first upgrade. Existing cards are reported as added only when no previous compatible primary index exists. The prompt-prefix artifact appears in affected catalogs when its bytes change. `LNV009` remains a non-failing contract-fingerprint review signal.
19
+
20
+ Do not delete `index.json`, rewrite semantic IDs, or copy generated paths or graph edges into comments. v0.5 tool schemas, prompt partitions, editor diagnostics, and host examples are additive; no source-card migration is required.
21
+
22
+ ## Do not annotate the whole repository
23
+
24
+ The fastest way to destroy LLMNav's value is a campaign that adds a card to every declaration. It creates search-term competition, maintenance burden, and larger prompts before the repository has evidence that those cards help.
25
+
26
+ Adopt it in measured layers.
27
+
28
+ ## Phase 1: baseline
29
+
30
+ Collect 20 to 50 historical coding tasks before adding cards.
31
+
32
+ Record:
33
+
34
+ * first correct file or symbol rank
35
+ * number of files opened
36
+ * grep or search calls before the correct location
37
+ * uncached input tokens when available
38
+ * task success and test result
39
+
40
+ These tasks become the first `.llmnav/eval/queries.jsonl` entries.
41
+
42
+ ## Phase 2: architecture cards
43
+
44
+ Add one `module` card to each domain boundary that agents routinely confuse.
45
+
46
+ Prioritize ownership and exclusions. A strong module card prevents an agent from entering an adjacent service with similar vocabulary.
47
+
48
+ Generate the repository core catalog and update agent instructions.
49
+
50
+ ## Phase 3: high-cost behavioral boundaries
51
+
52
+ Add `symbol` or `file` cards to:
53
+
54
+ * authentication and authorization flows
55
+ * money and credit accounting
56
+ * privacy access and deletion
57
+ * migrations and compatibility boundaries
58
+ * external provider orchestration
59
+ * event consumers with idempotency requirements
60
+ * concurrency-sensitive state transitions
61
+ * algorithms with non-obvious invariants
62
+
63
+ Require test relations for auth, money, and privacy cards.
64
+
65
+ ## Phase 4: aliases
66
+
67
+ Read failed and low-ranked historical queries. Add aliases when the task language uses product terminology, local language, abbreviations, or retired names that source identifiers do not contain.
68
+
69
+ Do not add aliases for every synonym an LLM can invent. Add phrases observed in real tasks.
70
+
71
+ ## Phase 5: search gates
72
+
73
+ Turn `llmnav eval` into a required CI check after the benchmark contains representative tasks.
74
+
75
+ The initial default gates are not sacred. Raise them when the repository's query set is broad enough that a higher score reflects real navigation quality.
76
+
77
+ ## Phase 6: selective expansion
78
+
79
+ Add cards only when one of these is true:
80
+
81
+ * repeated task queries fail to retrieve a boundary
82
+ * a high fan-in symbol is hard to identify by name
83
+ * a serious bug was caused by an unstated invariant
84
+ * agents repeatedly edit the wrong adjacent module
85
+ * a semantic workflow or fallback is invisible to syntax
86
+
87
+ A missing card should have a concrete navigation failure behind it.
88
+
89
+ ## Existing JSDoc and docstrings
90
+
91
+ Keep human-facing API documentation. LLMNav sits before it and serves a different purpose.
92
+
93
+ ```ts
94
+ /* llmnav/1 symbol
95
+ id=auth.session.rotate
96
+ role=Rotate one refresh-token family atomically and reject replayed tokens.
97
+ search=refresh token|token rotation|replay detection
98
+ stability=contract
99
+ */
100
+ /** Rotates a refresh token and returns its replacement. */
101
+ export async function rotateSession() {}
102
+ ```
103
+
104
+ Do not copy full JSDoc into `role`. Do not add LLMNav fields to generated API documentation unless the output has a clear consumer.
105
+
106
+ ## Renames and moves
107
+
108
+ Keep the semantic ID unchanged.
109
+
110
+ Run generation to update path, line, signature, structure hash, and body hash.
111
+
112
+ A rename that leaves behavior intact should usually produce no semantic hash change.
113
+
114
+ ## Splits and merges
115
+
116
+ When one capability splits, mark the old registry record as replaced.
117
+
118
+ ```json
119
+ {"id":"billing.credit.charge","state":"replaced","by":["billing.credit.reserve","billing.credit.capture"]}
120
+ ```
121
+
122
+ A multi-target replacement is intentionally ambiguous. `show` reports every candidate and `context` requires the caller to choose one; LLMNav never treats array order as semantic priority. Use `redirect` when exactly one successor should resolve automatically.
123
+
124
+ When an old name redirects to one capability:
125
+
126
+ ```json
127
+ {"id":"auth.session.renew","state":"redirect","to":"auth.session.rotate"}
128
+ ```
129
+
130
+ Never assign a retired ID to unrelated new code.
@@ -0,0 +1,42 @@
1
+ # LLMNav v0.2 performance report
2
+
3
+ This report contains measured results from `npm run benchmark:v0.2`. It is not an estimate. The raw record is `benchmarks/results/v0.2-win32-x64-node24.json`.
4
+
5
+ ## Environment
6
+
7
+ | Property | Value |
8
+ | --- | --- |
9
+ | Platform | win32 x64 |
10
+ | Node.js | v24.18.0 |
11
+ | CPU | AMD Ryzen 5 7430U with Radeon Graphics |
12
+ | Logical CPUs | 12 |
13
+ | Fixture | 1,000 files, 5,000 cards |
14
+ | Filesystem cache | Not flushed |
15
+
16
+ ## Generation
17
+
18
+ | Scenario | Time | Parsed files | Reused files | Retokenized cards |
19
+ | --- | ---: | ---: | ---: | ---: |
20
+ | Cold initial generation | 7190.90 ms | 1000 | 0 | 5000 |
21
+ | One-file incremental regeneration | 4901.54 ms | 1 | 999 | 1 |
22
+ | Same change, forced full regeneration | 7450.25 ms | 1000 | 0 | 5000 |
23
+ | No-op incremental regeneration | 2209.92 ms | 0 | 1000 | 0 |
24
+
25
+ The one-file incremental run was 1.52× faster than the forced full run on this machine. Incremental and full generation produced byte-identical cache trees across 507 files (33.25 MiB).
26
+
27
+ ## Fresh-process query
28
+
29
+ Each sample started a fresh Node.js process and included reading and parsing the required generated JSON. The operating-system filesystem cache was not flushed.
30
+
31
+ | Search path | Correct runs | Median | p95 | Median RSS | Input artifacts |
32
+ | --- | ---: | ---: | ---: | ---: | ---: |
33
+ | v0.1-compatible legacy retokenization | 7/7 | 394.47 ms | 429.81 ms | 146.52 MiB | 4.78 MiB index |
34
+ | v0.2 deterministic inverted index | 7/7 | 221.11 ms | 253.12 ms | 118.12 MiB | 16.22 MiB index + search index |
35
+
36
+ The v0.2 median was 1.78× faster. Both paths returned the same result checksum, and all runs returned the expected semantic ID.
37
+
38
+ ## Interpretation
39
+
40
+ The query comparison deliberately includes JSON loading. It therefore measures the cost seen by a one-shot CLI process rather than only the in-memory ranking loop. The generation comparison uses two copies of the same fixture and verifies the resulting cache bytes before reporting the timing.
41
+
42
+ The changed source file contains five cards, so the machine-readable body-hash diff reports five modified cards. Only one card changed searchable fields, and the card-level inverted index retokenized exactly that one card.
@@ -0,0 +1,66 @@
1
+ # Provider-neutral host integration
2
+
3
+ LLMNav exposes repository navigation contracts without choosing a model provider, transport, tool-call event shape, or cache-control header.
4
+
5
+ ## Bind authority outside model input
6
+
7
+ The host chooses one repository root and closes over it. The model never receives a `root` field in a tool schema and cannot redirect a call to another checkout.
8
+
9
+ ```js
10
+ import { createLlmnavHost } from "llmnav/examples/provider-neutral-host.mjs";
11
+
12
+ const host = await createLlmnavHost(process.cwd());
13
+ ```
14
+
15
+ The packaged example returns fixed tool definitions, verified prompt partitions, and one executor. It loads the index, search postings, graph, lexicon, and registry once into a project session instead of reopening the generated cache for every tool call. Copy the small adapter into a host when its packaging system does not import example files directly.
16
+
17
+ ## Register tools
18
+
19
+ Pass `host.toolDefinitions` through the provider SDK's tool-definition mapping without changing names, descriptions, input properties, required fields, or order. Provider-specific wrapper keys may differ; the nested LLMNav JSON Schemas stay unchanged.
20
+
21
+ When a tool call arrives:
22
+
23
+ ```js
24
+ const result = await host.execute({
25
+ name: call.name,
26
+ input: call.arguments,
27
+ });
28
+ ```
29
+
30
+ Return the complete result envelope to the model. Do not convert an expected `LNVAP002` or `LNVAP404` result into an uncaught transport failure.
31
+
32
+ The session is an intentional immutable snapshot. After generation or a checkout change, call `await host.refresh()` before accepting more navigation calls. Refresh reloads both prompt partitions and the project session; LLMNav does not silently mix old and new generation state.
33
+
34
+ ## Assemble a cacheable prefix
35
+
36
+ ```js
37
+ const base = host.basePromptPartitions;
38
+ const selected = host.selectPromptPartitions(["auth.session"]);
39
+ ```
40
+
41
+ `basePromptPartitions` contains package tool definitions, the agent protocol, and the repository core in declared order. The selected form appends requested module partitions in bundle order. A missing module fails explicitly instead of silently dropping context.
42
+
43
+ Map each partition's `cacheBoundaryAfter` hint to the provider's cache-control mechanism only when that mechanism exists. Append user requests, branch state, diffs, source bodies, and tool results after all selected LLMNav partitions. Never store those volatile values back into `prompt-prefix.json`.
44
+
45
+ ## Minimal host loop
46
+
47
+ ```js
48
+ const host = await createLlmnavHost(repositoryRoot);
49
+ const request = {
50
+ tools: mapToolDefinitions(host.toolDefinitions),
51
+ prefix: mapPromptPartitions(host.basePromptPartitions),
52
+ messages: currentMessages,
53
+ };
54
+
55
+ const response = await provider.generate(request);
56
+ for (const call of extractToolCalls(response)) {
57
+ const result = await host.execute({ name: call.name, input: call.arguments });
58
+ currentMessages.push(mapToolResult(call, result));
59
+ }
60
+ ```
61
+
62
+ `mapToolDefinitions`, `mapPromptPartitions`, `extractToolCalls`, and `mapToolResult` are intentionally host-owned. This keeps provider churn outside the LLMNav contract and makes it possible to test the navigation adapter without network access or model credentials.
63
+
64
+ ## Editor diagnostics
65
+
66
+ Headless hosts can call the `llmnav_check` operation. Editor hosts that need document ranges should run `llmnav check --format editor` or call `diagnosticsToEditor`; see [Editor integration](editor-integration.md).