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.
- package/CHANGELOG.md +113 -0
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/ROADMAP.md +71 -0
- package/bin/llmnav.js +16 -0
- package/docs/agent-integration.md +114 -0
- package/docs/api.md +290 -0
- package/docs/architecture.md +286 -0
- package/docs/benchmarking.md +164 -0
- package/docs/ci.md +196 -0
- package/docs/cli.md +233 -0
- package/docs/configuration.md +117 -0
- package/docs/editor-integration.md +29 -0
- package/docs/faq.md +59 -0
- package/docs/graph.md +92 -0
- package/docs/language-examples.md +130 -0
- package/docs/migration.md +130 -0
- package/docs/performance-v0.2.md +42 -0
- package/docs/provider-neutral-integration.md +66 -0
- package/docs/publishing.md +86 -0
- package/docs/quickstart.md +139 -0
- package/docs/research.md +31 -0
- package/docs/spec.md +424 -0
- package/examples/provider-neutral-host.d.mts +17 -0
- package/examples/provider-neutral-host.mjs +40 -0
- package/package.json +79 -0
- package/schema/config.schema.json +296 -0
- package/src/agent-protocol.js +117 -0
- package/src/agent-tools.js +61 -0
- package/src/agents.js +127 -0
- package/src/boundaries.js +50 -0
- package/src/changes.js +168 -0
- package/src/cli.js +459 -0
- package/src/config.js +305 -0
- package/src/contracts.js +70 -0
- package/src/declaration.js +334 -0
- package/src/doctor.js +124 -0
- package/src/editor.js +107 -0
- package/src/evaluation.js +67 -0
- package/src/files.js +81 -0
- package/src/formatter.js +23 -0
- package/src/generator.js +528 -0
- package/src/graph-input.js +157 -0
- package/src/graph.js +403 -0
- package/src/incremental.js +262 -0
- package/src/index.d.ts +673 -0
- package/src/index.js +115 -0
- package/src/initializer.js +137 -0
- package/src/inverted-index.js +350 -0
- package/src/parser.js +449 -0
- package/src/project.js +65 -0
- package/src/prompt-bundle.js +108 -0
- package/src/registry.js +107 -0
- package/src/sarif.js +70 -0
- package/src/search-shards.js +75 -0
- package/src/search.js +636 -0
- package/src/spec.d.ts +27 -0
- package/src/spec.js +237 -0
- package/src/tokenizer.js +37 -0
- package/src/transaction.js +557 -0
- package/src/util.js +256 -0
- package/src/validator.js +635 -0
- package/templates/file-card.txt +8 -0
- package/templates/lexicon.json +7 -0
- package/templates/line-card.txt +9 -0
- package/templates/module-card.txt +9 -0
- package/templates/queries.jsonl +1 -0
- package/templates/symbol-card.txt +10 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executeAgentOperation,
|
|
3
|
+
createProjectSession,
|
|
4
|
+
getAgentToolDefinitions,
|
|
5
|
+
loadPromptPrefixBundle,
|
|
6
|
+
} from "llmnav";
|
|
7
|
+
|
|
8
|
+
export async function createLlmnavHost(root) {
|
|
9
|
+
let bundle = await loadPromptPrefixBundle(root);
|
|
10
|
+
let session = await createProjectSession(root);
|
|
11
|
+
const host = {
|
|
12
|
+
toolDefinitions: getAgentToolDefinitions(),
|
|
13
|
+
basePromptPartitions: selectPromptPartitions(bundle),
|
|
14
|
+
selectPromptPartitions(moduleIds = []) {
|
|
15
|
+
return selectPromptPartitions(bundle, moduleIds);
|
|
16
|
+
},
|
|
17
|
+
execute(call) {
|
|
18
|
+
return executeAgentOperation(root, call.name, call.input ?? {}, { session });
|
|
19
|
+
},
|
|
20
|
+
async refresh() {
|
|
21
|
+
[bundle, session] = await Promise.all([
|
|
22
|
+
loadPromptPrefixBundle(root),
|
|
23
|
+
createProjectSession(root),
|
|
24
|
+
]);
|
|
25
|
+
host.basePromptPartitions = selectPromptPartitions(bundle);
|
|
26
|
+
return host;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
return host;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function selectPromptPartitions(bundle, moduleIds = []) {
|
|
33
|
+
const requested = new Set(moduleIds.map((id) => id.startsWith("module:") ? id : `module:${id}`));
|
|
34
|
+
const available = new Set(bundle.assembly.modulePartitionIds);
|
|
35
|
+
const missing = [...requested].filter((id) => !available.has(id)).sort();
|
|
36
|
+
if (missing.length > 0) throw new Error(`Unknown prompt module partition(s): ${missing.join(", ")}.`);
|
|
37
|
+
return bundle.partitions.filter((partition) =>
|
|
38
|
+
bundle.assembly.basePartitionIds.includes(partition.id) || requested.has(partition.id),
|
|
39
|
+
);
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "llmnav",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "A deterministic semantic navigation layer for LLM coding agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"llmnav": "bin/llmnav.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./src/index.d.ts",
|
|
12
|
+
"import": "./src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./spec": {
|
|
15
|
+
"types": "./src/spec.d.ts",
|
|
16
|
+
"import": "./src/spec.js"
|
|
17
|
+
},
|
|
18
|
+
"./examples/provider-neutral-host.mjs": {
|
|
19
|
+
"types": "./examples/provider-neutral-host.d.mts",
|
|
20
|
+
"import": "./examples/provider-neutral-host.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"bin",
|
|
26
|
+
"src",
|
|
27
|
+
"templates",
|
|
28
|
+
"schema",
|
|
29
|
+
"docs",
|
|
30
|
+
"examples/provider-neutral-host.mjs",
|
|
31
|
+
"examples/provider-neutral-host.d.mts",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md",
|
|
34
|
+
"ROADMAP.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test": "node --test",
|
|
42
|
+
"test:coverage": "node --test --experimental-test-coverage",
|
|
43
|
+
"lint": "node ./scripts/lint-repo.js",
|
|
44
|
+
"check": "npm run lint && npm test && node ./bin/llmnav.js check --root . && node ./bin/llmnav.js generate --root . --full --check",
|
|
45
|
+
"pack:check": "npm pack --dry-run",
|
|
46
|
+
"release:check": "node ./scripts/release-check.js",
|
|
47
|
+
"prepublishOnly": "npm run check && npm run release:check && npm run pack:check",
|
|
48
|
+
"release:tag": "node ./scripts/verify-tag.js",
|
|
49
|
+
"test:performance": "node --test tests/performance.test.js",
|
|
50
|
+
"benchmark:v0.2": "node ./benchmarks/run-v0.2.js",
|
|
51
|
+
"smoke:pack": "node ./scripts/pack-smoke.js"
|
|
52
|
+
},
|
|
53
|
+
"keywords": [
|
|
54
|
+
"llm",
|
|
55
|
+
"coding-agent",
|
|
56
|
+
"code-navigation",
|
|
57
|
+
"semantic-search",
|
|
58
|
+
"repository-map",
|
|
59
|
+
"ai",
|
|
60
|
+
"cli",
|
|
61
|
+
"lint"
|
|
62
|
+
],
|
|
63
|
+
"author": "제로디",
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "git+https://github.com/0disoft/llmnav.git"
|
|
68
|
+
},
|
|
69
|
+
"bugs": {
|
|
70
|
+
"url": "https://github.com/0disoft/llmnav/issues"
|
|
71
|
+
},
|
|
72
|
+
"homepage": "https://github.com/0disoft/llmnav#readme",
|
|
73
|
+
"sideEffects": false,
|
|
74
|
+
"types": "./src/index.d.ts",
|
|
75
|
+
"publishConfig": {
|
|
76
|
+
"access": "public",
|
|
77
|
+
"provenance": true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://llmnav.dev/schema/config.schema.json",
|
|
4
|
+
"title": "LLMNav configuration",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"version",
|
|
8
|
+
"repositoryId",
|
|
9
|
+
"sourceRoots",
|
|
10
|
+
"includeExtensions"
|
|
11
|
+
],
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"$schema": {
|
|
15
|
+
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
"version": {
|
|
18
|
+
"const": 1
|
|
19
|
+
},
|
|
20
|
+
"repositoryId": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^[a-z][a-z0-9-]{0,63}$"
|
|
23
|
+
},
|
|
24
|
+
"sourceRoots": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"minItems": 1,
|
|
27
|
+
"items": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"pattern": "^(?!/|[A-Za-z]:|.*(?:^|/)\\.\\.(?:/|$))(?:\\.|[^\\\\]+)$"
|
|
30
|
+
},
|
|
31
|
+
"uniqueItems": true
|
|
32
|
+
},
|
|
33
|
+
"includeExtensions": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"minItems": 1,
|
|
36
|
+
"items": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"pattern": "^\\."
|
|
39
|
+
},
|
|
40
|
+
"uniqueItems": true
|
|
41
|
+
},
|
|
42
|
+
"excludeDirectories": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": {
|
|
45
|
+
"type": "string"
|
|
46
|
+
},
|
|
47
|
+
"uniqueItems": true
|
|
48
|
+
},
|
|
49
|
+
"excludeFiles": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
},
|
|
54
|
+
"uniqueItems": true
|
|
55
|
+
},
|
|
56
|
+
"coverageRules": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"required": [
|
|
61
|
+
"match"
|
|
62
|
+
],
|
|
63
|
+
"additionalProperties": false,
|
|
64
|
+
"properties": {
|
|
65
|
+
"name": {
|
|
66
|
+
"type": "string"
|
|
67
|
+
},
|
|
68
|
+
"match": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"minItems": 1,
|
|
71
|
+
"items": {
|
|
72
|
+
"type": "string"
|
|
73
|
+
},
|
|
74
|
+
"uniqueItems": true
|
|
75
|
+
},
|
|
76
|
+
"scope": {
|
|
77
|
+
"enum": [
|
|
78
|
+
"file",
|
|
79
|
+
"module",
|
|
80
|
+
"symbol"
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"requiredFields": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"items": {
|
|
86
|
+
"enum": [
|
|
87
|
+
"id",
|
|
88
|
+
"role",
|
|
89
|
+
"owns",
|
|
90
|
+
"excludes",
|
|
91
|
+
"search",
|
|
92
|
+
"invariant",
|
|
93
|
+
"effect",
|
|
94
|
+
"risk",
|
|
95
|
+
"rel",
|
|
96
|
+
"stability"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"uniqueItems": true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"graph": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"additionalProperties": false,
|
|
107
|
+
"properties": {
|
|
108
|
+
"indexFiles": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"pattern": "^(?!/|[A-Za-z]:|.*(?:^|/)\\.\\.(?:/|$))[^\\\\]+$"
|
|
113
|
+
},
|
|
114
|
+
"uniqueItems": true
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"lint": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"additionalProperties": false,
|
|
121
|
+
"properties": {
|
|
122
|
+
"maxRoleLength": {
|
|
123
|
+
"type": "integer",
|
|
124
|
+
"minimum": 40
|
|
125
|
+
},
|
|
126
|
+
"maxSearchTerms": {
|
|
127
|
+
"type": "integer",
|
|
128
|
+
"minimum": 1
|
|
129
|
+
},
|
|
130
|
+
"minSearchTerms": {
|
|
131
|
+
"type": "integer",
|
|
132
|
+
"minimum": 0
|
|
133
|
+
},
|
|
134
|
+
"maxInvariants": {
|
|
135
|
+
"type": "integer",
|
|
136
|
+
"minimum": 0
|
|
137
|
+
},
|
|
138
|
+
"maxEffects": {
|
|
139
|
+
"type": "integer",
|
|
140
|
+
"minimum": 0
|
|
141
|
+
},
|
|
142
|
+
"maxRelations": {
|
|
143
|
+
"type": "integer",
|
|
144
|
+
"minimum": 0
|
|
145
|
+
},
|
|
146
|
+
"maxBlockBytes": {
|
|
147
|
+
"type": "object",
|
|
148
|
+
"properties": {
|
|
149
|
+
"file": {
|
|
150
|
+
"type": "integer",
|
|
151
|
+
"minimum": 100
|
|
152
|
+
},
|
|
153
|
+
"module": {
|
|
154
|
+
"type": "integer",
|
|
155
|
+
"minimum": 100
|
|
156
|
+
},
|
|
157
|
+
"symbol": {
|
|
158
|
+
"type": "integer",
|
|
159
|
+
"minimum": 100
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"maxSemanticRatio": {
|
|
164
|
+
"type": "number",
|
|
165
|
+
"minimum": 0,
|
|
166
|
+
"maximum": 1
|
|
167
|
+
},
|
|
168
|
+
"minimumSourceBytesForRatio": {
|
|
169
|
+
"type": "integer",
|
|
170
|
+
"minimum": 0
|
|
171
|
+
},
|
|
172
|
+
"searchTermSaturation": {
|
|
173
|
+
"type": "number",
|
|
174
|
+
"minimum": 0,
|
|
175
|
+
"maximum": 1
|
|
176
|
+
},
|
|
177
|
+
"minimumCardsForSaturation": {
|
|
178
|
+
"type": "integer",
|
|
179
|
+
"minimum": 1
|
|
180
|
+
},
|
|
181
|
+
"genericSearchTerms": {
|
|
182
|
+
"type": "array",
|
|
183
|
+
"items": {
|
|
184
|
+
"type": "string"
|
|
185
|
+
},
|
|
186
|
+
"uniqueItems": true
|
|
187
|
+
},
|
|
188
|
+
"vagueRoleWords": {
|
|
189
|
+
"type": "array",
|
|
190
|
+
"items": {
|
|
191
|
+
"type": "string"
|
|
192
|
+
},
|
|
193
|
+
"uniqueItems": true
|
|
194
|
+
},
|
|
195
|
+
"strictRisks": {
|
|
196
|
+
"type": "array",
|
|
197
|
+
"items": {
|
|
198
|
+
"type": "string"
|
|
199
|
+
},
|
|
200
|
+
"uniqueItems": true
|
|
201
|
+
},
|
|
202
|
+
"additionalEffects": {
|
|
203
|
+
"type": "array",
|
|
204
|
+
"items": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"pattern": "^[a-z][a-z0-9.-]*$"
|
|
207
|
+
},
|
|
208
|
+
"uniqueItems": true
|
|
209
|
+
},
|
|
210
|
+
"additionalRisks": {
|
|
211
|
+
"type": "array",
|
|
212
|
+
"items": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"pattern": "^[a-z][a-z0-9.-]*$"
|
|
215
|
+
},
|
|
216
|
+
"uniqueItems": true
|
|
217
|
+
},
|
|
218
|
+
"additionalRelations": {
|
|
219
|
+
"type": "array",
|
|
220
|
+
"items": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"pattern": "^[a-z][a-z0-9-]*$"
|
|
223
|
+
},
|
|
224
|
+
"uniqueItems": true
|
|
225
|
+
},
|
|
226
|
+
"requireCanonicalOrder": {
|
|
227
|
+
"type": "boolean"
|
|
228
|
+
},
|
|
229
|
+
"requireCanonicalFormatting": {
|
|
230
|
+
"type": "boolean"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
"generation": {
|
|
235
|
+
"type": "object",
|
|
236
|
+
"additionalProperties": false,
|
|
237
|
+
"properties": {
|
|
238
|
+
"cacheDirectory": {
|
|
239
|
+
"type": "string",
|
|
240
|
+
"pattern": "^\\.llmnav/(?!.*(?:^|/)\\.\\.(?:/|$))[^\\\\]+$"
|
|
241
|
+
},
|
|
242
|
+
"moduleDepth": {
|
|
243
|
+
"type": "integer",
|
|
244
|
+
"minimum": 1,
|
|
245
|
+
"maximum": 6
|
|
246
|
+
},
|
|
247
|
+
"searchShardSize": {
|
|
248
|
+
"type": "integer",
|
|
249
|
+
"minimum": 0
|
|
250
|
+
},
|
|
251
|
+
"repositoryCatalogStabilities": {
|
|
252
|
+
"type": "array",
|
|
253
|
+
"items": {
|
|
254
|
+
"enum": [
|
|
255
|
+
"architecture",
|
|
256
|
+
"contract",
|
|
257
|
+
"implementation"
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
"uniqueItems": true
|
|
261
|
+
},
|
|
262
|
+
"moduleCatalogStabilities": {
|
|
263
|
+
"type": "array",
|
|
264
|
+
"items": {
|
|
265
|
+
"enum": [
|
|
266
|
+
"architecture",
|
|
267
|
+
"contract",
|
|
268
|
+
"implementation"
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
"uniqueItems": true
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
"evaluation": {
|
|
276
|
+
"type": "object",
|
|
277
|
+
"additionalProperties": false,
|
|
278
|
+
"properties": {
|
|
279
|
+
"queryFile": {
|
|
280
|
+
"type": "string",
|
|
281
|
+
"pattern": "^(?!/|[A-Za-z]:|.*(?:^|/)\\.\\.(?:/|$))[^\\\\]+$"
|
|
282
|
+
},
|
|
283
|
+
"minimumRecallAt1": {
|
|
284
|
+
"type": "number",
|
|
285
|
+
"minimum": 0,
|
|
286
|
+
"maximum": 1
|
|
287
|
+
},
|
|
288
|
+
"minimumRecallAt5": {
|
|
289
|
+
"type": "number",
|
|
290
|
+
"minimum": 0,
|
|
291
|
+
"maximum": 1
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* llmnav/1 module
|
|
2
|
+
id=llmnav.agent.protocol
|
|
3
|
+
role=Execute bounded repository navigation operations through one provider-neutral result envelope.
|
|
4
|
+
owns=operation validation|operation dispatch|provider-neutral result envelope
|
|
5
|
+
excludes=provider SDK transport|repository discovery|source mutation
|
|
6
|
+
search=agent tool schema|provider neutral tools|tool dispatcher|agent operation protocol
|
|
7
|
+
rel=workflow>llmnav.search.query
|
|
8
|
+
rel=workflow>llmnav.rules.validate
|
|
9
|
+
stability=contract
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { loadGraphInputs } from "./graph-input.js";
|
|
13
|
+
import { scanProject } from "./project.js";
|
|
14
|
+
import { buildContext, queryProject, showProjectCard } from "./search.js";
|
|
15
|
+
import { countDiagnostics, validateProject } from "./validator.js";
|
|
16
|
+
import { getAgentToolDefinitions } from "./agent-tools.js";
|
|
17
|
+
|
|
18
|
+
export { AGENT_TOOL_SCHEMA_VERSION, getAgentToolDefinitions } from "./agent-tools.js";
|
|
19
|
+
export const AGENT_OPERATION_SCHEMA_VERSION = 1;
|
|
20
|
+
|
|
21
|
+
const DEFINITIONS = getAgentToolDefinitions();
|
|
22
|
+
|
|
23
|
+
const OPERATIONS = new Map([
|
|
24
|
+
["llmnav_query", "query"],
|
|
25
|
+
["llmnav_show", "show"],
|
|
26
|
+
["llmnav_context", "context"],
|
|
27
|
+
["llmnav_check", "check"],
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
export async function executeAgentOperation(root, name, input = {}, options = {}) {
|
|
31
|
+
const canonicalName = String(name);
|
|
32
|
+
const operation = OPERATIONS.get(canonicalName);
|
|
33
|
+
if (!operation) return failure("unknown", "LNVAP001", `Unknown agent operation ${JSON.stringify(name)}.`);
|
|
34
|
+
const definition = DEFINITIONS.find((item) => item.name === canonicalName);
|
|
35
|
+
const validationError = validateInput(definition.inputSchema, input);
|
|
36
|
+
if (validationError) return failure(operation, "LNVAP002", validationError);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
if (operation === "query") {
|
|
40
|
+
return success(operation, options.session
|
|
41
|
+
? options.session.query(input.task.trim(), { top: input.top ?? 5 })
|
|
42
|
+
: await queryProject(root, input.task.trim(), { top: input.top ?? 5 }));
|
|
43
|
+
}
|
|
44
|
+
if (operation === "show") {
|
|
45
|
+
const result = options.session
|
|
46
|
+
? options.session.show(input.id.trim())
|
|
47
|
+
: await showProjectCard(root, input.id.trim());
|
|
48
|
+
if (!result.card && !result.node) return failure(operation, "LNVAP404", `Unknown or inactive semantic ID ${input.id}.`, result);
|
|
49
|
+
return success(operation, result);
|
|
50
|
+
}
|
|
51
|
+
if (operation === "context") {
|
|
52
|
+
const contextOptions = {
|
|
53
|
+
depth: input.depth ?? 1,
|
|
54
|
+
budget: input.budget ?? 2500,
|
|
55
|
+
maxEdges: input.maxEdges ?? 24,
|
|
56
|
+
};
|
|
57
|
+
return success(operation, options.session
|
|
58
|
+
? options.session.context(input.id.trim(), contextOptions)
|
|
59
|
+
: await buildContext(root, input.id.trim(), contextOptions));
|
|
60
|
+
}
|
|
61
|
+
const project = await scanProject(root, { paths: input.paths ?? [] });
|
|
62
|
+
const graphInputs = await loadGraphInputs(root, project.config);
|
|
63
|
+
const diagnostics = [...validateProject(project), ...graphInputs.diagnostics].sort(compareDiagnostics);
|
|
64
|
+
const counts = countDiagnostics(diagnostics);
|
|
65
|
+
return {
|
|
66
|
+
schemaVersion: AGENT_OPERATION_SCHEMA_VERSION,
|
|
67
|
+
operation,
|
|
68
|
+
ok: counts.error === 0,
|
|
69
|
+
data: { counts, diagnostics, cardCount: project.records.length },
|
|
70
|
+
error: null,
|
|
71
|
+
};
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return failure(operation, "LNVAP500", error instanceof Error ? error.message : String(error));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function validateInput(schema, input) {
|
|
78
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return "Operation input must be an object.";
|
|
79
|
+
const keys = Object.keys(input).sort();
|
|
80
|
+
const unknown = keys.filter((key) => !Object.hasOwn(schema.properties, key));
|
|
81
|
+
if (unknown.length > 0) return `Unknown input field(s): ${unknown.join(", ")}.`;
|
|
82
|
+
const missing = schema.required.filter((key) => input[key] === undefined);
|
|
83
|
+
if (missing.length > 0) return `Missing required input field(s): ${missing.join(", ")}.`;
|
|
84
|
+
for (const key of keys) {
|
|
85
|
+
const error = validateProperty(key, input[key], schema.properties[key]);
|
|
86
|
+
if (error) return error;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function validateProperty(key, value, schema) {
|
|
92
|
+
if (schema.type === "string") {
|
|
93
|
+
if (typeof value !== "string" || value.trim().length < (schema.minLength ?? 0)) return `Input field ${key} must be a non-empty string.`;
|
|
94
|
+
} else if (schema.type === "integer") {
|
|
95
|
+
if (!Number.isInteger(value) || value < schema.minimum || value > schema.maximum) {
|
|
96
|
+
return `Input field ${key} must be an integer from ${schema.minimum} to ${schema.maximum}.`;
|
|
97
|
+
}
|
|
98
|
+
} else if (schema.type === "array") {
|
|
99
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.length < schema.items.minLength)) {
|
|
100
|
+
return `Input field ${key} must be an array of non-empty strings.`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function success(operation, data) {
|
|
107
|
+
return { schemaVersion: AGENT_OPERATION_SCHEMA_VERSION, operation, ok: true, data, error: null };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function failure(operation, code, message, data = null) {
|
|
111
|
+
return { schemaVersion: AGENT_OPERATION_SCHEMA_VERSION, operation, ok: false, data, error: { code, message } };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function compareDiagnostics(left, right) {
|
|
115
|
+
return left.file.localeCompare(right.file, "en") || left.line - right.line || left.column - right.column ||
|
|
116
|
+
left.code.localeCompare(right.code, "en") || left.message.localeCompare(right.message, "en");
|
|
117
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* llmnav/1 module
|
|
2
|
+
id=llmnav.agent.tool-schema
|
|
3
|
+
role=Publish stable provider-neutral tool definitions without loading repository execution code.
|
|
4
|
+
owns=agent tool names|tool input schemas|tool order
|
|
5
|
+
excludes=operation execution|provider SDK transport|repository scope
|
|
6
|
+
search=agent tool definitions|tool JSON schema|stable tool prefix|provider neutral schema
|
|
7
|
+
rel=workflow>llmnav.agent.protocol
|
|
8
|
+
stability=contract
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const AGENT_TOOL_SCHEMA_VERSION = 1;
|
|
12
|
+
|
|
13
|
+
const DEFINITIONS = [
|
|
14
|
+
tool("llmnav_query", "Find the most relevant semantic cards for a coding task.", {
|
|
15
|
+
task: stringProperty("Task language to search for."),
|
|
16
|
+
top: integerProperty("Maximum results.", 1, 100, 5),
|
|
17
|
+
}, ["task"]),
|
|
18
|
+
tool("llmnav_show", "Resolve one local or qualified workspace semantic ID.", {
|
|
19
|
+
id: stringProperty("Semantic ID or repository-qualified semantic ID."),
|
|
20
|
+
}, ["id"]),
|
|
21
|
+
tool("llmnav_context", "Build bounded semantic and graph context around one ID.", {
|
|
22
|
+
id: stringProperty("Semantic ID or repository-qualified semantic ID."),
|
|
23
|
+
depth: integerProperty("Maximum graph traversal depth.", 0, 8, 1),
|
|
24
|
+
budget: integerProperty("Approximate token budget.", 128, 100000, 2500),
|
|
25
|
+
maxEdges: integerProperty("Maximum graph edges to inspect and pack.", 0, 1000, 24),
|
|
26
|
+
}, ["id"]),
|
|
27
|
+
tool("llmnav_check", "Validate semantic cards and configured graph inputs without mutation.", {
|
|
28
|
+
paths: {
|
|
29
|
+
type: "array",
|
|
30
|
+
description: "Optional repository-relative paths to validate.",
|
|
31
|
+
items: { type: "string", minLength: 1 },
|
|
32
|
+
default: [],
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export function getAgentToolDefinitions() {
|
|
38
|
+
return structuredClone(DEFINITIONS);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function tool(name, description, properties, required = []) {
|
|
42
|
+
return {
|
|
43
|
+
schemaVersion: AGENT_TOOL_SCHEMA_VERSION,
|
|
44
|
+
name,
|
|
45
|
+
description,
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: "object",
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
properties,
|
|
50
|
+
required,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function stringProperty(description) {
|
|
56
|
+
return { type: "string", minLength: 1, description };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function integerProperty(description, minimum, maximum, defaultValue) {
|
|
60
|
+
return { type: "integer", minimum, maximum, default: defaultValue, description };
|
|
61
|
+
}
|