@mrclrchtr/supi-code-intelligence 4.1.0 → 4.3.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.
- package/README.md +4 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +2 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +5 -5
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/language.ts +2 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/operation-support.ts +19 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/syntax-node.ts +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-c-family.ts +230 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-html-sql.ts +186 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-jvm.ts +311 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-polyglot.ts +218 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-scripting.ts +307 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline.ts +9 -5
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +6 -2
- package/package.json +5 -5
- package/src/analysis/search/pattern.ts +2 -2
- package/src/analysis/target/symbol.ts +5 -2
- package/src/extension.ts +5 -1
- package/src/session/orientation/collect.ts +134 -180
- package/src/session/orientation/context-sections.ts +14 -48
- package/src/session/orientation-types.ts +14 -4
- package/src/session/orientation-workflow.ts +20 -48
- package/src/substrate/lsp/lifecycle.ts +55 -3
- package/src/substrate/lsp/state.ts +6 -0
- package/src/tool/graph/execute.ts +1 -1
- package/src/tool/graph/markdown.ts +222 -0
- package/src/tool/orientation/markdown.ts +66 -11
- package/src/tool/register.ts +10 -0
- package/src/ui/footer.ts +33 -8
- package/src/ui/markdown/overview-data.ts +3 -15
- package/src/ui/markdown/overview.ts +4 -10
- package/src/ui/markdown/types.ts +0 -1
- package/src/tool/graph/calls-md.ts +0 -41
- package/src/tool/graph/implementations-md.ts +0 -57
- package/src/tool/graph/markdown-base.ts +0 -88
- package/src/tool/graph/provider-location-md.ts +0 -9
- package/src/tool/graph/references-md.ts +0 -41
|
@@ -23,22 +23,14 @@ import type {
|
|
|
23
23
|
OrientationTarget,
|
|
24
24
|
} from "../../ui/markdown/types.ts";
|
|
25
25
|
import type {
|
|
26
|
-
|
|
26
|
+
OrientationItem,
|
|
27
27
|
OrientationResultData,
|
|
28
28
|
OrientationSectionData,
|
|
29
29
|
} from "../orientation-types.ts";
|
|
30
30
|
import { collectContextOrientationFacts } from "./context-facts.ts";
|
|
31
|
-
import { formatSectionNote } from "./context-sections.ts";
|
|
32
|
-
|
|
33
|
-
interface CollectedOrientationSection {
|
|
34
|
-
readonly key: OrientationSection;
|
|
35
|
-
readonly title: string;
|
|
36
|
-
readonly lines: readonly string[];
|
|
37
|
-
readonly metadata: OrientationSectionData;
|
|
38
|
-
}
|
|
39
31
|
|
|
40
32
|
interface TargetSectionCollection {
|
|
41
|
-
readonly
|
|
33
|
+
readonly items: OrientationItem[];
|
|
42
34
|
readonly hasStructuralEvidence: boolean;
|
|
43
35
|
readonly hasSemanticEvidence: boolean;
|
|
44
36
|
readonly status: "complete" | "partial" | "unavailable";
|
|
@@ -89,7 +81,7 @@ async function executeTargetOrientation(
|
|
|
89
81
|
const requestedSections = DEFAULT_TARGET_SECTIONS;
|
|
90
82
|
const limit = input.maxResults ?? 10;
|
|
91
83
|
const focusTarget = input.target ? formatFocusTarget(input.target, deps.cwd) : null;
|
|
92
|
-
const sections:
|
|
84
|
+
const sections: OrientationSectionData[] = [];
|
|
93
85
|
|
|
94
86
|
let hasStructural = false;
|
|
95
87
|
let hasSemantic = false;
|
|
@@ -115,16 +107,16 @@ async function executeTargetOrientation(
|
|
|
115
107
|
: "unavailable";
|
|
116
108
|
|
|
117
109
|
return {
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
title: "Code Orientation",
|
|
111
|
+
notes: [],
|
|
112
|
+
sections,
|
|
120
113
|
confidence,
|
|
121
114
|
focusTarget,
|
|
122
115
|
requestedSections,
|
|
123
116
|
renderedSections: sections.map((section) => section.key),
|
|
124
117
|
omittedCount: sections.reduce(
|
|
125
118
|
(total, section) =>
|
|
126
|
-
total +
|
|
127
|
-
section.metadata.evidenceLists.reduce((sum, list) => sum + (list.omittedCount ?? 0), 0),
|
|
119
|
+
total + section.evidenceLists.reduce((sum, list) => sum + (list.omittedCount ?? 0), 0),
|
|
128
120
|
0,
|
|
129
121
|
),
|
|
130
122
|
nextQueries: buildNextQueries(input.target, deps.cwd),
|
|
@@ -139,7 +131,7 @@ async function buildRequestedSection(options: {
|
|
|
139
131
|
limit: number;
|
|
140
132
|
treeContext: Awaited<ReturnType<typeof maybeGatherTreeContext>>;
|
|
141
133
|
}): Promise<{
|
|
142
|
-
section:
|
|
134
|
+
section: OrientationSectionData;
|
|
143
135
|
hasStructuralEvidence: boolean;
|
|
144
136
|
hasSemanticEvidence: boolean;
|
|
145
137
|
}> {
|
|
@@ -172,22 +164,22 @@ async function buildEnrichedDefsSection(
|
|
|
172
164
|
treeContext: Awaited<ReturnType<typeof maybeGatherTreeContext>>,
|
|
173
165
|
limit: number,
|
|
174
166
|
): Promise<TargetSectionCollection> {
|
|
175
|
-
const
|
|
167
|
+
const items = buildDefinitionItems(target, deps.cwd, treeContext);
|
|
176
168
|
const contextHasSemanticEvidence = Boolean(
|
|
177
169
|
treeContext?.hover || (treeContext?.definition?.length ?? 0) > 0,
|
|
178
170
|
);
|
|
179
|
-
const hasStructuralEvidence = hasRenderableItems(
|
|
171
|
+
const hasStructuralEvidence = hasRenderableItems(items);
|
|
180
172
|
|
|
181
173
|
if (!target) {
|
|
182
174
|
return unavailableTargetSection(
|
|
183
|
-
|
|
175
|
+
items,
|
|
184
176
|
hasStructuralEvidence,
|
|
185
177
|
"Definitions require a precise target.",
|
|
186
178
|
);
|
|
187
179
|
}
|
|
188
180
|
if (deps.lspRuntime.kind !== "ready") {
|
|
189
181
|
return {
|
|
190
|
-
|
|
182
|
+
items,
|
|
191
183
|
hasStructuralEvidence,
|
|
192
184
|
hasSemanticEvidence: contextHasSemanticEvidence,
|
|
193
185
|
status: contextHasSemanticEvidence ? "partial" : "unavailable",
|
|
@@ -197,13 +189,13 @@ async function buildEnrichedDefsSection(
|
|
|
197
189
|
}
|
|
198
190
|
|
|
199
191
|
const definitions = await collectDefinitionTargets(target, deps, limit);
|
|
200
|
-
if (definitions.
|
|
201
|
-
if (
|
|
202
|
-
|
|
192
|
+
if (definitions.items.length > 0) {
|
|
193
|
+
if (items.length > 0) items.push({ kind: "blank" });
|
|
194
|
+
items.push({ kind: "paragraph", text: "**Definition:**" }, ...definitions.items);
|
|
203
195
|
}
|
|
204
196
|
const hasSemanticEvidence = contextHasSemanticEvidence || definitions.hasSemanticEvidence;
|
|
205
197
|
return {
|
|
206
|
-
|
|
198
|
+
items,
|
|
207
199
|
hasStructuralEvidence,
|
|
208
200
|
hasSemanticEvidence,
|
|
209
201
|
status:
|
|
@@ -220,12 +212,12 @@ async function collectDefinitionTargets(
|
|
|
220
212
|
): Promise<
|
|
221
213
|
Pick<
|
|
222
214
|
TargetSectionCollection,
|
|
223
|
-
"
|
|
215
|
+
"items" | "hasSemanticEvidence" | "status" | "reason" | "evidenceLists"
|
|
224
216
|
>
|
|
225
217
|
> {
|
|
226
218
|
if (!deps.provider?.definition) {
|
|
227
219
|
return {
|
|
228
|
-
|
|
220
|
+
items: [],
|
|
229
221
|
hasSemanticEvidence: false,
|
|
230
222
|
status: "unavailable",
|
|
231
223
|
reason: "Definition provider unavailable.",
|
|
@@ -239,7 +231,7 @@ async function collectDefinitionTargets(
|
|
|
239
231
|
});
|
|
240
232
|
if (result.kind === "unavailable") {
|
|
241
233
|
return {
|
|
242
|
-
|
|
234
|
+
items: [],
|
|
243
235
|
hasSemanticEvidence: false,
|
|
244
236
|
status: "unavailable",
|
|
245
237
|
reason: result.reason,
|
|
@@ -265,7 +257,7 @@ async function collectDefinitionTargets(
|
|
|
265
257
|
maxResults: limit,
|
|
266
258
|
});
|
|
267
259
|
return {
|
|
268
|
-
|
|
260
|
+
items: evidence.items.map((location) => ({ kind: "paragraph", text: location })),
|
|
269
261
|
hasSemanticEvidence: true,
|
|
270
262
|
status: result.kind === "partial" ? "partial" : "complete",
|
|
271
263
|
reason: result.kind === "partial" ? result.reason : null,
|
|
@@ -273,7 +265,7 @@ async function collectDefinitionTargets(
|
|
|
273
265
|
};
|
|
274
266
|
} catch (error) {
|
|
275
267
|
return {
|
|
276
|
-
|
|
268
|
+
items: [],
|
|
277
269
|
hasSemanticEvidence: false,
|
|
278
270
|
status: "unavailable",
|
|
279
271
|
reason: `Definition provider failed: ${String(error)}`,
|
|
@@ -293,7 +285,10 @@ async function buildDiagnosticsSection(
|
|
|
293
285
|
if (deps.lspRuntime.kind !== "ready") {
|
|
294
286
|
return unavailableTargetSection(
|
|
295
287
|
[
|
|
296
|
-
|
|
288
|
+
{
|
|
289
|
+
kind: "paragraph",
|
|
290
|
+
text: "LSP not available — diagnostics require a live language server. Use `code_health` to check server status.",
|
|
291
|
+
},
|
|
297
292
|
],
|
|
298
293
|
false,
|
|
299
294
|
"Diagnostics require a live language server.",
|
|
@@ -305,7 +300,7 @@ async function buildDiagnosticsSection(
|
|
|
305
300
|
const result = await deps.lspRuntime.runtime.fileDiagnostics(targetFile, 4);
|
|
306
301
|
if (result.kind === "unavailable") {
|
|
307
302
|
return unavailableTargetSection(
|
|
308
|
-
[`Diagnostics unavailable for this target — ${result.reason}`],
|
|
303
|
+
[{ kind: "paragraph", text: `Diagnostics unavailable for this target — ${result.reason}` }],
|
|
309
304
|
false,
|
|
310
305
|
result.reason,
|
|
311
306
|
);
|
|
@@ -327,10 +322,10 @@ async function buildDiagnosticsSection(
|
|
|
327
322
|
maxResults: limit,
|
|
328
323
|
});
|
|
329
324
|
return {
|
|
330
|
-
|
|
325
|
+
items:
|
|
331
326
|
evidence.items.length === 0
|
|
332
|
-
? ["No diagnostics found near this target."]
|
|
333
|
-
: evidence.items.map((diagnostic) =>
|
|
327
|
+
? [{ kind: "paragraph", text: "No diagnostics found near this target." }]
|
|
328
|
+
: evidence.items.map((diagnostic) => formatDiagnosticItem(diagnostic)),
|
|
334
329
|
hasStructuralEvidence: false,
|
|
335
330
|
hasSemanticEvidence: true,
|
|
336
331
|
status: result.kind === "partial" ? "partial" : "complete",
|
|
@@ -339,24 +334,27 @@ async function buildDiagnosticsSection(
|
|
|
339
334
|
};
|
|
340
335
|
} catch (error) {
|
|
341
336
|
return unavailableTargetSection(
|
|
342
|
-
["Diagnostics failed to load."],
|
|
337
|
+
[{ kind: "paragraph", text: "Diagnostics failed to load." }],
|
|
343
338
|
false,
|
|
344
339
|
`Diagnostics failed to load: ${String(error)}`,
|
|
345
340
|
);
|
|
346
341
|
}
|
|
347
342
|
}
|
|
348
343
|
|
|
349
|
-
function
|
|
344
|
+
function formatDiagnosticItem(diagnostic: Diagnostic): OrientationItem {
|
|
350
345
|
const severity = (diagnostic.severity ?? 1) === 1 ? "ERROR" : "WARN";
|
|
351
|
-
return
|
|
346
|
+
return {
|
|
347
|
+
kind: "list-item",
|
|
348
|
+
text: `**${severity}** (L${(diagnostic.range.start.line ?? 0) + 1}): ${diagnosticMessageString(diagnostic)}`,
|
|
349
|
+
};
|
|
352
350
|
}
|
|
353
351
|
|
|
354
352
|
function completedDocsSection(
|
|
355
|
-
|
|
353
|
+
items: OrientationItem[],
|
|
356
354
|
evidenceLists: readonly EvidenceListMetadata[],
|
|
357
355
|
): TargetSectionCollection {
|
|
358
356
|
return {
|
|
359
|
-
|
|
357
|
+
items,
|
|
360
358
|
hasStructuralEvidence: false,
|
|
361
359
|
hasSemanticEvidence: false,
|
|
362
360
|
status: "complete",
|
|
@@ -366,12 +364,12 @@ function completedDocsSection(
|
|
|
366
364
|
}
|
|
367
365
|
|
|
368
366
|
function unavailableTargetSection(
|
|
369
|
-
|
|
367
|
+
items: OrientationItem[],
|
|
370
368
|
hasStructuralEvidence: boolean,
|
|
371
369
|
reason: string,
|
|
372
370
|
): TargetSectionCollection {
|
|
373
371
|
return {
|
|
374
|
-
|
|
372
|
+
items,
|
|
375
373
|
hasStructuralEvidence,
|
|
376
374
|
hasSemanticEvidence: false,
|
|
377
375
|
status: "unavailable",
|
|
@@ -388,7 +386,7 @@ async function buildDocsSection(
|
|
|
388
386
|
): Promise<TargetSectionCollection> {
|
|
389
387
|
if (!target) {
|
|
390
388
|
return unavailableTargetSection(
|
|
391
|
-
["Docs unavailable without a precise target."],
|
|
389
|
+
[{ kind: "paragraph", text: "Docs unavailable without a precise target." }],
|
|
392
390
|
false,
|
|
393
391
|
"Docs require a precise target.",
|
|
394
392
|
);
|
|
@@ -397,7 +395,7 @@ async function buildDocsSection(
|
|
|
397
395
|
const targetFile = path.resolve(deps.cwd, target.file);
|
|
398
396
|
if (!existsSync(targetFile)) {
|
|
399
397
|
return unavailableTargetSection(
|
|
400
|
-
["Docs unavailable — target file not found."],
|
|
398
|
+
[{ kind: "paragraph", text: "Docs unavailable — target file not found." }],
|
|
401
399
|
false,
|
|
402
400
|
"Target file not found.",
|
|
403
401
|
);
|
|
@@ -443,7 +441,10 @@ async function buildDocsSection(
|
|
|
443
441
|
}
|
|
444
442
|
|
|
445
443
|
if (jsdocStart === -1 || jsdocEnd === -1) {
|
|
446
|
-
return completedDocsSection(
|
|
444
|
+
return completedDocsSection(
|
|
445
|
+
[{ kind: "paragraph", text: "No JSDoc/TSDoc comment found for this symbol." }],
|
|
446
|
+
[],
|
|
447
|
+
);
|
|
447
448
|
}
|
|
448
449
|
|
|
449
450
|
const docLines = lines
|
|
@@ -457,7 +458,10 @@ async function buildDocsSection(
|
|
|
457
458
|
.filter((line) => line.trim() !== "");
|
|
458
459
|
|
|
459
460
|
if (docLines.length === 0) {
|
|
460
|
-
return completedDocsSection(
|
|
461
|
+
return completedDocsSection(
|
|
462
|
+
[{ kind: "paragraph", text: "No JSDoc/TSDoc comment found for this symbol." }],
|
|
463
|
+
[],
|
|
464
|
+
);
|
|
461
465
|
}
|
|
462
466
|
|
|
463
467
|
const evidence = createEvidenceList({
|
|
@@ -465,10 +469,13 @@ async function buildDocsSection(
|
|
|
465
469
|
items: docLines,
|
|
466
470
|
maxResults: limit,
|
|
467
471
|
});
|
|
468
|
-
return completedDocsSection(
|
|
472
|
+
return completedDocsSection(
|
|
473
|
+
[{ kind: "code", language: "ts", lines: evidence.items }],
|
|
474
|
+
[evidence.metadata],
|
|
475
|
+
);
|
|
469
476
|
} catch (error) {
|
|
470
477
|
return unavailableTargetSection(
|
|
471
|
-
["Docs extraction failed."],
|
|
478
|
+
[{ kind: "paragraph", text: "Docs extraction failed." }],
|
|
472
479
|
false,
|
|
473
480
|
`Docs extraction failed: ${String(error)}`,
|
|
474
481
|
);
|
|
@@ -484,66 +491,106 @@ async function maybeGatherTreeContext(
|
|
|
484
491
|
return gatherTreeSitterContext(deps.provider, relPath, target.line, target.character);
|
|
485
492
|
}
|
|
486
493
|
|
|
487
|
-
function
|
|
494
|
+
function buildDefinitionItems(
|
|
488
495
|
target: OrientationTarget | null | undefined,
|
|
489
496
|
cwd: string,
|
|
490
497
|
treeContext: Awaited<ReturnType<typeof maybeGatherTreeContext>>,
|
|
491
|
-
):
|
|
492
|
-
if (!target) return ["No precise target context found."];
|
|
498
|
+
): OrientationItem[] {
|
|
499
|
+
if (!target) return [{ kind: "paragraph", text: "No precise target context found." }];
|
|
493
500
|
|
|
494
|
-
const
|
|
501
|
+
const items: OrientationItem[] = [
|
|
502
|
+
{ kind: "list-item", text: `Focus: \`${formatFocusTarget(target, cwd)}\`` },
|
|
503
|
+
];
|
|
495
504
|
if (target.name) {
|
|
496
|
-
|
|
505
|
+
items.push({
|
|
506
|
+
kind: "list-item",
|
|
507
|
+
text: `Symbol: \`${target.name}\`${target.kind ? ` (${target.kind})` : ""}`,
|
|
508
|
+
});
|
|
497
509
|
}
|
|
498
510
|
if (treeContext?.nodeInfo?.type) {
|
|
499
|
-
|
|
511
|
+
items.push({ kind: "list-item", text: `Node: \`${treeContext.nodeInfo.type}\`` });
|
|
500
512
|
}
|
|
501
513
|
if (treeContext?.hover?.contents) {
|
|
502
|
-
|
|
514
|
+
items.push(...hoverItems(treeContext.hover.contents));
|
|
503
515
|
}
|
|
504
|
-
return
|
|
516
|
+
return items;
|
|
505
517
|
}
|
|
506
518
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const maxHoverChars = 600;
|
|
510
|
-
if (trimmed.startsWith("```")) {
|
|
511
|
-
return formatFencedHover(trimmed, maxHoverChars);
|
|
512
|
-
}
|
|
513
|
-
if (trimmed.length <= maxHoverChars) return [`- Hover: ${trimmed}`];
|
|
519
|
+
const MAX_HOVER_CHARS = 600;
|
|
520
|
+
const HOVER_TRUNCATED_NOTE = "_(truncated, use `code_inspect` for full type)_";
|
|
514
521
|
|
|
515
|
-
|
|
516
|
-
|
|
522
|
+
function hoverItems(contents: string): OrientationItem[] {
|
|
523
|
+
const trimmed = contents.trim();
|
|
524
|
+
if (!trimmed.startsWith("```")) {
|
|
525
|
+
if (trimmed.length <= MAX_HOVER_CHARS)
|
|
526
|
+
return [{ kind: "list-item", text: `Hover: ${trimmed}` }];
|
|
517
527
|
return [
|
|
518
|
-
|
|
519
|
-
"
|
|
528
|
+
{ kind: "list-item", text: `Hover: ${clipHoverText(trimmed)}...` },
|
|
529
|
+
{ kind: "paragraph", text: HOVER_TRUNCATED_NOTE },
|
|
520
530
|
];
|
|
521
531
|
}
|
|
532
|
+
const { lines, truncated } = clipHoverLines(trimmed);
|
|
533
|
+
const items: OrientationItem[] = [
|
|
534
|
+
{ kind: "list-item", text: "Hover:" },
|
|
535
|
+
...hoverBodyItems(lines),
|
|
536
|
+
];
|
|
537
|
+
if (truncated) items.push({ kind: "paragraph", text: HOVER_TRUNCATED_NOTE });
|
|
538
|
+
return items;
|
|
539
|
+
}
|
|
522
540
|
|
|
541
|
+
function clipHoverText(trimmed: string): string {
|
|
542
|
+
const lines = trimmed.split("\n");
|
|
543
|
+
if (lines.length === 1) return trimmed.slice(0, MAX_HOVER_CHARS);
|
|
523
544
|
let acc = "";
|
|
524
|
-
for (const line of
|
|
525
|
-
if (acc.length + line.length + 1 >
|
|
545
|
+
for (const line of lines) {
|
|
546
|
+
if (acc.length + line.length + 1 > MAX_HOVER_CHARS && acc.length > 0) break;
|
|
526
547
|
acc += (acc ? "\n" : "") + line;
|
|
527
548
|
}
|
|
528
|
-
return
|
|
549
|
+
return acc;
|
|
529
550
|
}
|
|
530
551
|
|
|
531
|
-
|
|
532
|
-
function formatFencedHover(contents: string, maxChars: number): string[] {
|
|
552
|
+
function clipHoverLines(contents: string): { lines: string[]; truncated: boolean } {
|
|
533
553
|
const sourceLines = contents.split("\n");
|
|
534
|
-
if (contents.length <=
|
|
535
|
-
|
|
554
|
+
if (contents.length <= MAX_HOVER_CHARS) return { lines: sourceLines, truncated: false };
|
|
536
555
|
const lines = [sourceLines[0]];
|
|
537
556
|
let length = sourceLines[0].length;
|
|
538
|
-
let closed = false;
|
|
539
557
|
for (const line of sourceLines.slice(1)) {
|
|
540
|
-
if (length + line.length + 1 >
|
|
558
|
+
if (length + line.length + 1 > MAX_HOVER_CHARS) break;
|
|
541
559
|
lines.push(line);
|
|
542
560
|
length += line.length + 1;
|
|
543
|
-
if (line.startsWith("```")) closed = true;
|
|
544
561
|
}
|
|
545
|
-
|
|
546
|
-
|
|
562
|
+
return { lines, truncated: true };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** Parse provider hover Markdown (fenced code + prose) into rendered items. */
|
|
566
|
+
function hoverBodyItems(lines: readonly string[]): OrientationItem[] {
|
|
567
|
+
const items: OrientationItem[] = [];
|
|
568
|
+
let codeLanguage: string | null = null;
|
|
569
|
+
let codeLines: string[] = [];
|
|
570
|
+
const closeCode = (): void => {
|
|
571
|
+
if (codeLanguage === null) return;
|
|
572
|
+
items.push({
|
|
573
|
+
kind: "code",
|
|
574
|
+
language: codeLanguage.length > 0 ? codeLanguage : null,
|
|
575
|
+
lines: codeLines,
|
|
576
|
+
});
|
|
577
|
+
codeLanguage = null;
|
|
578
|
+
codeLines = [];
|
|
579
|
+
};
|
|
580
|
+
for (const line of lines) {
|
|
581
|
+
if (line.startsWith("```")) {
|
|
582
|
+
if (codeLanguage === null) codeLanguage = line.slice(3).trim();
|
|
583
|
+
else closeCode();
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
if (codeLanguage !== null) {
|
|
587
|
+
codeLines.push(line);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
items.push(line.trim().length === 0 ? { kind: "blank" } : { kind: "paragraph", text: line });
|
|
591
|
+
}
|
|
592
|
+
closeCode();
|
|
593
|
+
return items;
|
|
547
594
|
}
|
|
548
595
|
|
|
549
596
|
function formatFocusTarget(target: OrientationTarget, cwd: string): string {
|
|
@@ -595,7 +642,7 @@ function targetSectionResult(
|
|
|
595
642
|
collection: TargetSectionCollection,
|
|
596
643
|
provenance: OrientationSectionData["provenance"],
|
|
597
644
|
): {
|
|
598
|
-
section:
|
|
645
|
+
section: OrientationSectionData;
|
|
599
646
|
hasStructuralEvidence: boolean;
|
|
600
647
|
hasSemanticEvidence: boolean;
|
|
601
648
|
} {
|
|
@@ -608,111 +655,18 @@ function targetSectionResult(
|
|
|
608
655
|
section: {
|
|
609
656
|
key,
|
|
610
657
|
title: SECTION_TITLES[key],
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
confidence,
|
|
618
|
-
provenance,
|
|
619
|
-
evidenceLists: collection.evidenceLists,
|
|
620
|
-
},
|
|
658
|
+
status: collection.status,
|
|
659
|
+
reason: collection.reason,
|
|
660
|
+
confidence,
|
|
661
|
+
provenance,
|
|
662
|
+
evidenceLists: collection.evidenceLists,
|
|
663
|
+
items: collection.items,
|
|
621
664
|
},
|
|
622
665
|
hasStructuralEvidence: collection.hasStructuralEvidence,
|
|
623
666
|
hasSemanticEvidence: collection.hasSemanticEvidence,
|
|
624
667
|
};
|
|
625
668
|
}
|
|
626
669
|
|
|
627
|
-
function
|
|
628
|
-
|
|
629
|
-
sections: readonly CollectedOrientationSection[],
|
|
630
|
-
): OrientationBlock[] {
|
|
631
|
-
const blocks: OrientationBlock[] = [
|
|
632
|
-
{ kind: "heading", level: 1, text: "Code Orientation" },
|
|
633
|
-
{ kind: "blank" },
|
|
634
|
-
];
|
|
635
|
-
if (focusTarget) {
|
|
636
|
-
blocks.push(
|
|
637
|
-
{ kind: "heading", level: 2, text: "Focus" },
|
|
638
|
-
{ kind: "list-item", text: `\`${focusTarget}\`` },
|
|
639
|
-
{ kind: "blank" },
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
for (const section of sections) {
|
|
643
|
-
blocks.push(
|
|
644
|
-
{ kind: "heading", level: 2, text: section.title },
|
|
645
|
-
{ kind: "paragraph", text: formatSectionNote(section.metadata) },
|
|
646
|
-
);
|
|
647
|
-
blocks.push(...sectionBlocks(section.lines.join("\n")), { kind: "blank" });
|
|
648
|
-
}
|
|
649
|
-
return blocks;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
interface SectionBlockState {
|
|
653
|
-
blocks: OrientationBlock[];
|
|
654
|
-
codeLanguage: string | null;
|
|
655
|
-
codeLines: string[];
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/** Convert target-section fragments into typed blocks. Context Orientation never crosses a markdown seam. */
|
|
659
|
-
function sectionBlocks(document: string): OrientationBlock[] {
|
|
660
|
-
const state: SectionBlockState = { blocks: [], codeLanguage: null, codeLines: [] };
|
|
661
|
-
for (const line of document.split("\n")) appendSectionLine(state, line);
|
|
662
|
-
closeCodeBlock(state);
|
|
663
|
-
return state.blocks;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
function appendSectionLine(state: SectionBlockState, line: string): void {
|
|
667
|
-
if (line.startsWith("```")) {
|
|
668
|
-
toggleCodeBlock(state, line.slice(3).trim());
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
if (state.codeLanguage !== null) {
|
|
672
|
-
state.codeLines.push(line);
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
appendProseBlock(state.blocks, line);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
function toggleCodeBlock(state: SectionBlockState, language: string): void {
|
|
679
|
-
if (state.codeLanguage === null) {
|
|
680
|
-
state.codeLanguage = language;
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
closeCodeBlock(state);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
function closeCodeBlock(state: SectionBlockState): void {
|
|
687
|
-
if (state.codeLanguage === null) return;
|
|
688
|
-
state.blocks.push({
|
|
689
|
-
kind: "code",
|
|
690
|
-
language: state.codeLanguage.length > 0 ? state.codeLanguage : null,
|
|
691
|
-
lines: state.codeLines,
|
|
692
|
-
});
|
|
693
|
-
state.codeLanguage = null;
|
|
694
|
-
state.codeLines = [];
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
function appendProseBlock(blocks: OrientationBlock[], line: string): void {
|
|
698
|
-
const heading = /^(#{1,3})\s+(.*)$/.exec(line);
|
|
699
|
-
if (heading) {
|
|
700
|
-
blocks.push({
|
|
701
|
-
kind: "heading",
|
|
702
|
-
level: heading[1].length as 1 | 2 | 3,
|
|
703
|
-
text: heading[2],
|
|
704
|
-
});
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
if (line.startsWith("- ")) {
|
|
708
|
-
blocks.push({ kind: "list-item", text: line.slice(2) });
|
|
709
|
-
} else if (line.trim().length === 0) {
|
|
710
|
-
blocks.push({ kind: "blank" });
|
|
711
|
-
} else {
|
|
712
|
-
blocks.push({ kind: "paragraph", text: line });
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
function hasRenderableItems(lines: string[]): boolean {
|
|
717
|
-
return lines.some((line) => line.trim().startsWith("- "));
|
|
670
|
+
function hasRenderableItems(items: readonly OrientationItem[]): boolean {
|
|
671
|
+
return items.some((item) => item.kind === "list-item");
|
|
718
672
|
}
|
|
@@ -7,7 +7,6 @@ import type { EvidenceListMetadata, EvidencePartialReason } from "../../analysis
|
|
|
7
7
|
import { createEvidenceList } from "../../analysis/evidence.ts";
|
|
8
8
|
import type { CodeProvider } from "../../analysis/provider.ts";
|
|
9
9
|
import type {
|
|
10
|
-
OrientationBlock,
|
|
11
10
|
OrientationProvenance,
|
|
12
11
|
OrientationResultData,
|
|
13
12
|
OrientationSectionData,
|
|
@@ -15,8 +14,9 @@ import type {
|
|
|
15
14
|
|
|
16
15
|
/** Mutable collection state kept private to context-Oriented fact assembly. */
|
|
17
16
|
export interface ContextOrientationBuilder {
|
|
18
|
-
readonly
|
|
17
|
+
readonly title: string;
|
|
19
18
|
readonly sections: OrientationSectionData[];
|
|
19
|
+
readonly notes: string[];
|
|
20
20
|
readonly maxResults: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -34,16 +34,17 @@ export interface ListSectionInput<T> {
|
|
|
34
34
|
readonly partialReason?: EvidencePartialReason;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Start a context-Oriented document with one factual focus
|
|
37
|
+
/** Start a context-Oriented document with one factual focus title. */
|
|
38
38
|
export function createBuilder(maxResults: number, title: string): ContextOrientationBuilder {
|
|
39
39
|
return {
|
|
40
|
-
|
|
40
|
+
title,
|
|
41
41
|
sections: [],
|
|
42
|
+
notes: [],
|
|
42
43
|
maxResults,
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
/** Add one bounded evidence list and matching
|
|
47
|
+
/** Add one bounded evidence list and matching section facts. */
|
|
47
48
|
export function appendList<T>(
|
|
48
49
|
builder: ContextOrientationBuilder,
|
|
49
50
|
input: ListSectionInput<T>,
|
|
@@ -60,7 +61,7 @@ export function appendList<T>(
|
|
|
60
61
|
partialReason: input.partialReason ?? "filesystem-error",
|
|
61
62
|
}
|
|
62
63
|
: list.metadata;
|
|
63
|
-
|
|
64
|
+
builder.sections.push({
|
|
64
65
|
key: input.key,
|
|
65
66
|
title: input.title,
|
|
66
67
|
status: input.status ?? "complete",
|
|
@@ -68,16 +69,8 @@ export function appendList<T>(
|
|
|
68
69
|
confidence: input.confidence,
|
|
69
70
|
provenance: input.provenance,
|
|
70
71
|
evidenceLists: [{ ...metadata, key: input.key }],
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
builder.blocks.push(
|
|
74
|
-
{ kind: "heading", level: 2, text: input.title },
|
|
75
|
-
{ kind: "paragraph", text: formatSectionNote(section) },
|
|
76
|
-
);
|
|
77
|
-
for (const item of list.items) {
|
|
78
|
-
builder.blocks.push({ kind: "list-item", text: input.render(item) });
|
|
79
|
-
}
|
|
80
|
-
builder.blocks.push({ kind: "blank" });
|
|
72
|
+
items: list.items.map((item) => ({ kind: "list-item", text: input.render(item) })),
|
|
73
|
+
});
|
|
81
74
|
}
|
|
82
75
|
|
|
83
76
|
/** Add an unavailable section without manufacturing an absence claim. */
|
|
@@ -90,7 +83,7 @@ export function appendUnavailable(
|
|
|
90
83
|
provenance: readonly OrientationProvenance[];
|
|
91
84
|
},
|
|
92
85
|
): void {
|
|
93
|
-
|
|
86
|
+
builder.sections.push({
|
|
94
87
|
key: input.key,
|
|
95
88
|
title: input.title,
|
|
96
89
|
status: "unavailable",
|
|
@@ -98,13 +91,8 @@ export function appendUnavailable(
|
|
|
98
91
|
confidence: "unavailable",
|
|
99
92
|
provenance: input.provenance,
|
|
100
93
|
evidenceLists: [],
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
builder.blocks.push(
|
|
104
|
-
{ kind: "heading", level: 2, text: input.title },
|
|
105
|
-
{ kind: "paragraph", text: formatSectionNote(section) },
|
|
106
|
-
{ kind: "blank" },
|
|
107
|
-
);
|
|
94
|
+
items: [],
|
|
95
|
+
});
|
|
108
96
|
}
|
|
109
97
|
|
|
110
98
|
/** Collect direct regular-file and directory names from one filesystem level. */
|
|
@@ -294,7 +282,8 @@ export function resultData(
|
|
|
294
282
|
0,
|
|
295
283
|
);
|
|
296
284
|
return {
|
|
297
|
-
|
|
285
|
+
title: builder.title,
|
|
286
|
+
notes: builder.notes,
|
|
298
287
|
sections: builder.sections,
|
|
299
288
|
confidence: highestConfidence(builder.sections),
|
|
300
289
|
focusTarget: focus,
|
|
@@ -322,29 +311,6 @@ export function formatManifestValue(value: unknown): string {
|
|
|
322
311
|
return `\`${serialized ?? String(value)}\``;
|
|
323
312
|
}
|
|
324
313
|
|
|
325
|
-
/** Render the same section status/provenance metadata stored in result details. */
|
|
326
|
-
export function formatSectionNote(section: OrientationSectionData): string {
|
|
327
|
-
const facts = section.evidenceLists.map(evidenceSummary).join("; ");
|
|
328
|
-
const provenance = section.provenance.map(formatProvenance).join(", ");
|
|
329
|
-
return `_(status: ${section.status}; provenance: ${provenance || "none"}${section.reason ? `; ${section.reason}` : ""}${facts ? `; ${facts}` : ""})_`;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function evidenceSummary(metadata: EvidenceListMetadata): string {
|
|
333
|
-
if (metadata.totalCount === null) {
|
|
334
|
-
const omitted = metadata.omittedCount ? `; ${metadata.omittedCount} collected omitted` : "";
|
|
335
|
-
return `showing ${metadata.shownCount}${omitted}; more may exist — ${metadata.partialReason ?? "partial"}`;
|
|
336
|
-
}
|
|
337
|
-
if ((metadata.omittedCount ?? 0) > 0) {
|
|
338
|
-
return `showing ${metadata.shownCount} of ${metadata.totalCount}; ${metadata.omittedCount} omitted`;
|
|
339
|
-
}
|
|
340
|
-
return `${metadata.totalCount} observed`;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function formatProvenance(provenance: OrientationProvenance): string {
|
|
344
|
-
const detail = provenance.detail ? ` \`${provenance.detail}\`` : "";
|
|
345
|
-
return `${provenance.source}${provenance.capability ? ` (${provenance.capability})` : ""}${detail}`;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
314
|
function highestConfidence(sections: readonly OrientationSectionData[]): ConfidenceMode {
|
|
349
315
|
if (sections.some((section) => section.confidence === "semantic")) return "semantic";
|
|
350
316
|
if (sections.some((section) => section.confidence === "structural")) return "structural";
|