oscar64-mcp-docs 1.0.7 → 1.0.9
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/dist/stdio.js +207 -303
- package/package.json +1 -1
package/dist/stdio.js
CHANGED
|
@@ -394,67 +394,30 @@ var toolErrorSchema = z.object({
|
|
|
394
394
|
})
|
|
395
395
|
).optional().describe("Optional follow-up tool calls that can recover the workflow.")
|
|
396
396
|
});
|
|
397
|
-
var classificationEvidenceSchema = z.object({
|
|
398
|
-
label: z.string().describe("Label supported by this evidence item."),
|
|
399
|
-
facet: z.enum(["primary_track", "domain", "hardware", "technique", "abstraction", "artifact"]).describe("Facet channel that this evidence contributes to."),
|
|
400
|
-
reason: z.string().describe("Human-readable explanation for why this rule matched."),
|
|
401
|
-
weight: z.number().describe("Weighted contribution from this evidence match."),
|
|
402
|
-
matched_on: z.string().describe("Stable rule identifier that produced the evidence."),
|
|
403
|
-
source_field: z.enum(["title", "rel_path", "text", "files", "asset_refs", "derived"]).describe("Artifact field where the match was detected."),
|
|
404
|
-
matched_text: z.string().describe("Concrete snippet/text token that matched the rule."),
|
|
405
|
-
direct: z.boolean().describe("True when this is a direct artifact signal (API include/symbol) instead of a weak heuristic mention.")
|
|
406
|
-
});
|
|
407
|
-
var classificationDetailsSchema = z.object({
|
|
408
|
-
primary_track: z.string().describe("Selected primary track after evidence gating and fallback handling."),
|
|
409
|
-
primary_track_status: z.enum(["asserted", "neutral_fallback"]).describe("Whether the primary track is directly asserted or forced to a neutral fallback."),
|
|
410
|
-
facets: z.object({
|
|
411
|
-
domain: z.array(z.string()).describe("Functional domains covered by this result."),
|
|
412
|
-
hardware: z.array(z.string()).describe("Hardware domains referenced by this result."),
|
|
413
|
-
technique: z.array(z.string()).describe("Implementation techniques shown in this result."),
|
|
414
|
-
abstraction: z.array(z.string()).describe("Abstraction styles represented in this result."),
|
|
415
|
-
artifact: z.array(z.string()).describe("Artifact kinds used or produced in this result."),
|
|
416
|
-
systems: z.array(systemFamilySchema).describe("Detected target systems for this result; empty means shared/common."),
|
|
417
|
-
scope: z.enum(["tutorial", "sample", "manual"]).describe("Content scope of this result.")
|
|
418
|
-
}),
|
|
419
|
-
confidence: z.number().describe("Calibrated numeric confidence for the primary track decision."),
|
|
420
|
-
confidence_bucket: z.enum(["low", "medium", "high"]).describe("Confidence band intended for policy decisions and quality monitoring."),
|
|
421
|
-
evidence: z.array(classificationEvidenceSchema).describe("Inspectable evidence records used to derive primary track and facets.")
|
|
422
|
-
});
|
|
423
397
|
var classificationSummarySchema = z.object({
|
|
424
398
|
track: z.string().describe("Primary track summary value for routing and filtering."),
|
|
425
399
|
track_status: z.enum(["asserted", "neutral_fallback"]).describe("Whether the summary track was asserted or neutralized by fallback policy."),
|
|
426
|
-
confidence_bucket: z.enum(["low", "medium", "high"]).describe("Coarse confidence bucket for this classification summary.")
|
|
427
|
-
confidence: z.number().describe("Calibrated numeric confidence for summary consumers."),
|
|
428
|
-
domains: z.array(z.string()).describe("Top-level functional domains admitted after evidence gating."),
|
|
429
|
-
hardware: z.array(z.string()).describe("Relevant hardware domains admitted by evidence."),
|
|
430
|
-
techniques: z.array(z.string()).describe("Top techniques surfaced for this result."),
|
|
431
|
-
systems: z.array(systemFamilySchema).describe("Detected target systems for this result; empty means shared/common."),
|
|
432
|
-
scope: z.enum(["tutorial", "sample", "manual"]).describe("Content scope of this result.")
|
|
400
|
+
confidence_bucket: z.enum(["low", "medium", "high"]).describe("Coarse confidence bucket for this classification summary.")
|
|
433
401
|
});
|
|
434
402
|
var searchPreviewSchema = z.object({
|
|
435
403
|
summary: z.string().describe("Compact preview summary for quick relevance checks."),
|
|
436
404
|
signature: z.string().optional().describe("Declaration-like line extracted from content when available."),
|
|
437
|
-
include_path: z.string().optional().describe("Header include path context when relevant.")
|
|
438
|
-
declaration_context: z.string().optional().describe("Nearby declaration context that helps disambiguate similar APIs.")
|
|
405
|
+
include_path: z.string().optional().describe("Header include path context when relevant.")
|
|
439
406
|
});
|
|
440
407
|
var searchHitSchema = z.object({
|
|
441
|
-
source: z.enum(["manual", "code"]).describe("Where the result came from."),
|
|
442
408
|
result_type: z.enum(["topics", "tutorials", "samples", "headers"]).describe("Artifact type for this result."),
|
|
443
409
|
uri: z.string().describe("URI to pass into `read_uri` for full content."),
|
|
444
410
|
title: z.string().describe("Short title for the matched result."),
|
|
445
411
|
preview: searchPreviewSchema.describe("Structured preview fields for relevance evaluation."),
|
|
446
|
-
score: z.number().describe("Relative relevance score; higher means better match."),
|
|
447
412
|
referenced_files: z.array(z.string()).optional().describe("Referenced `code://...` URIs that can be read with `read_uri` (for example from #embed or #include)."),
|
|
448
|
-
classification_summary: classificationSummarySchema.describe("Compact classification metadata always returned.")
|
|
449
|
-
classification_details: classificationDetailsSchema.optional().describe("Detailed classification, included when `include_details=true`.")
|
|
413
|
+
classification_summary: classificationSummarySchema.describe("Compact classification metadata always returned.")
|
|
450
414
|
});
|
|
451
415
|
var searchInputSchema = z.object({
|
|
452
416
|
query: z.string().min(1).describe("Query text, symbol, API name, or error phrase to search for."),
|
|
453
417
|
limit: z.number().int().min(1).max(80).default(20).describe("Maximum number of results to return."),
|
|
454
418
|
type: searchTypeSchema.default("all").describe("Filter results by artifact type. Defaults to `all`."),
|
|
455
|
-
system: systemFilterSchema.default("c64").describe("Target system filter. Defaults to `c64`; use `all` for cross-system search.")
|
|
456
|
-
|
|
457
|
-
});
|
|
419
|
+
system: systemFilterSchema.default("c64").describe("Target system filter. Defaults to `c64`; use `all` for cross-system search.")
|
|
420
|
+
}).strict();
|
|
458
421
|
var searchDataSchema = z.object({
|
|
459
422
|
results: z.array(searchHitSchema).describe("Ranked search hits.")
|
|
460
423
|
});
|
|
@@ -580,11 +543,68 @@ var TUTORIAL_BAND_RULES = [
|
|
|
580
543
|
{ min: 5e3, max: 5099, primaryTrack: "bitmap_graphics", domain: "graphics" }
|
|
581
544
|
];
|
|
582
545
|
var CLASSIFICATION_RULES = [
|
|
546
|
+
{
|
|
547
|
+
id: "video_memory_api",
|
|
548
|
+
appliesTo: ["tutorial", "sample", "manual"],
|
|
549
|
+
tests: [
|
|
550
|
+
{
|
|
551
|
+
pattern: "#include\\s*<(?:c64|c128|vic20|plus4|x16|pet|atari|mega65|nes)/(?:vic|vdc|ted)\\.h>",
|
|
552
|
+
flags: "i",
|
|
553
|
+
reason: "includes display memory/control API header",
|
|
554
|
+
weight: 3,
|
|
555
|
+
direct: true
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
pattern: "\\b(vic_setbank|vic_waitframe|vic_waitline|vdc_[a-z0-9_]+|ted_[a-z0-9_]+|screenmem|d018)\\b",
|
|
559
|
+
flags: "i",
|
|
560
|
+
reason: "uses display memory/control API symbols",
|
|
561
|
+
weight: 2.5,
|
|
562
|
+
direct: true
|
|
563
|
+
}
|
|
564
|
+
],
|
|
565
|
+
emits: [
|
|
566
|
+
{ kind: "primary_track", label: "memory_mapping" },
|
|
567
|
+
{ kind: "domain", label: "memory" },
|
|
568
|
+
{ kind: "hardware", label: "vic_ii" },
|
|
569
|
+
{ kind: "technique", label: "bank_switching" }
|
|
570
|
+
]
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
id: "charwin_api",
|
|
574
|
+
appliesTo: ["tutorial", "sample", "manual"],
|
|
575
|
+
tests: [
|
|
576
|
+
{
|
|
577
|
+
pattern: "#include\\s*<(?:c64|c128|vic20|plus4|x16|pet|atari|mega65|nes)/charwin\\.h>",
|
|
578
|
+
flags: "i",
|
|
579
|
+
reason: "includes character window API header",
|
|
580
|
+
weight: 3,
|
|
581
|
+
direct: true
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
pattern: "\\bcwin_[a-z0-9_]+\\b",
|
|
585
|
+
flags: "i",
|
|
586
|
+
reason: "uses char window API symbols",
|
|
587
|
+
weight: 2.5,
|
|
588
|
+
direct: true
|
|
589
|
+
}
|
|
590
|
+
],
|
|
591
|
+
emits: [
|
|
592
|
+
{ kind: "primary_track", label: "fundamentals" },
|
|
593
|
+
{ kind: "domain", label: "display_textmode" },
|
|
594
|
+
{ kind: "artifact", label: "text_ui" }
|
|
595
|
+
]
|
|
596
|
+
},
|
|
583
597
|
{
|
|
584
598
|
id: "rasterirq_api",
|
|
585
599
|
appliesTo: ["tutorial", "sample", "manual"],
|
|
586
600
|
tests: [
|
|
587
|
-
{
|
|
601
|
+
{
|
|
602
|
+
pattern: "#include\\s*<(?:c64|c128|vic20|plus4|x16|pet|atari|mega65|nes)/rasterirq\\.h>",
|
|
603
|
+
flags: "i",
|
|
604
|
+
reason: "includes raster IRQ API",
|
|
605
|
+
weight: 3,
|
|
606
|
+
direct: true
|
|
607
|
+
},
|
|
588
608
|
{ pattern: "\\brirq_[a-z0-9_]+\\b", flags: "i", reason: "uses rirq_* routines", weight: 2, direct: true },
|
|
589
609
|
{ pattern: "\\braster\\s*irq\\b|\\birq\\b", flags: "i", reason: "mentions IRQ timing", weight: 1 }
|
|
590
610
|
],
|
|
@@ -1820,30 +1840,10 @@ var readUriTool = createTool2({
|
|
|
1820
1840
|
// src/mcp/tools/search.tool.ts
|
|
1821
1841
|
import { createTool as createTool3 } from "@mastra/core/tools";
|
|
1822
1842
|
import path9 from "path";
|
|
1823
|
-
var INTENT_SYNONYMS = {
|
|
1824
|
-
screenmem: ["screen", "memory", "screen memory"],
|
|
1825
|
-
charset: ["character set", "font", "d018"],
|
|
1826
|
-
d018: ["charset", "screen memory", "vic"],
|
|
1827
|
-
bank: ["banking", "vic_setbank", "memmap", "bank switch"],
|
|
1828
|
-
banking: ["bank", "vic_setbank", "memmap"],
|
|
1829
|
-
rasterirq: ["raster irq", "rirq", "interrupt"],
|
|
1830
|
-
sprite: ["sprites", "spr_", "vic"],
|
|
1831
|
-
sprites: ["sprite", "spr_", "vic"],
|
|
1832
|
-
charwin: ["cwin", "window", "text window"],
|
|
1833
|
-
joystick: ["joy", "input", "cia"],
|
|
1834
|
-
memmap: ["memory map", "banking", "mmap_"],
|
|
1835
|
-
vic: ["vic_ii", "d018", "screen memory"]
|
|
1836
|
-
};
|
|
1837
1843
|
function parseCodeUri(uri) {
|
|
1838
|
-
if (uri.startsWith("code://oscar/")) {
|
|
1839
|
-
|
|
1840
|
-
}
|
|
1841
|
-
if (uri.startsWith("code://sample/")) {
|
|
1842
|
-
return { scope: "sample", relPath: uri.replace("code://sample/", "") };
|
|
1843
|
-
}
|
|
1844
|
-
if (uri.startsWith("code://tutorial/")) {
|
|
1845
|
-
return { scope: "tutorial", relPath: uri.replace("code://tutorial/", "") };
|
|
1846
|
-
}
|
|
1844
|
+
if (uri.startsWith("code://oscar/")) return { scope: "oscar", relPath: uri.replace("code://oscar/", "") };
|
|
1845
|
+
if (uri.startsWith("code://sample/")) return { scope: "sample", relPath: uri.replace("code://sample/", "") };
|
|
1846
|
+
if (uri.startsWith("code://tutorial/")) return { scope: "tutorial", relPath: uri.replace("code://tutorial/", "") };
|
|
1847
1847
|
return null;
|
|
1848
1848
|
}
|
|
1849
1849
|
async function resolveReferencedUris(state, sourceUri, refs) {
|
|
@@ -1894,6 +1894,12 @@ async function resolveReferencedUris(state, sourceUri, refs) {
|
|
|
1894
1894
|
function escapeRegExp(value) {
|
|
1895
1895
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1896
1896
|
}
|
|
1897
|
+
function tokenizeQuery(query) {
|
|
1898
|
+
return query.toLowerCase().split(/[^a-z0-9_]+/).map((token) => token.trim()).filter((token) => token.length >= 3);
|
|
1899
|
+
}
|
|
1900
|
+
function isExactSymbolQuery(query) {
|
|
1901
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(query.trim());
|
|
1902
|
+
}
|
|
1897
1903
|
function looksLikeDeclarationLine(line) {
|
|
1898
1904
|
const trimmed = line.trim();
|
|
1899
1905
|
if (!trimmed) return false;
|
|
@@ -1913,286 +1919,172 @@ function inferResultType(result) {
|
|
|
1913
1919
|
if (uri.startsWith("code://oscar/include/") && uri.toLowerCase().endsWith(".h")) return "headers";
|
|
1914
1920
|
return "samples";
|
|
1915
1921
|
}
|
|
1916
|
-
function
|
|
1922
|
+
function extractSignatureFromBody(body, query) {
|
|
1923
|
+
if (!body || !query.trim()) return void 0;
|
|
1924
|
+
const symbol = query.trim().toLowerCase();
|
|
1925
|
+
const exactSymbol = isExactSymbolQuery(query) ? new RegExp(`\\b${escapeRegExp(symbol)}\\b`) : null;
|
|
1926
|
+
const queryTokens = tokenizeQuery(query);
|
|
1927
|
+
const lines = body.split(/\r?\n/);
|
|
1928
|
+
for (const line of lines) {
|
|
1929
|
+
const trimmed = line.trim();
|
|
1930
|
+
if (!trimmed || !looksLikeDeclarationLine(trimmed)) continue;
|
|
1931
|
+
const lower = trimmed.toLowerCase();
|
|
1932
|
+
if (exactSymbol && exactSymbol.test(lower)) return trimmed;
|
|
1933
|
+
if (queryTokens.some((token) => lower.includes(token))) return trimmed;
|
|
1934
|
+
}
|
|
1935
|
+
return void 0;
|
|
1936
|
+
}
|
|
1937
|
+
function buildPreview(result, query, resultType) {
|
|
1917
1938
|
const uri = String(result?.uri ?? "");
|
|
1918
1939
|
const body = String(result?.body ?? "");
|
|
1919
1940
|
const fallbackSummary = makeExcerpt(String(result?.snippet ?? ""), 700);
|
|
1920
1941
|
const basePreview = typeof result?.preview === "object" && result.preview ? result.preview : { summary: fallbackSummary };
|
|
1921
|
-
|
|
1922
|
-
let declarationContext = typeof basePreview.declarationContext === "string" && basePreview.declarationContext.trim().length > 0 ? basePreview.declarationContext.trim() : void 0;
|
|
1923
|
-
if (body && query.trim()) {
|
|
1924
|
-
const queryWord = query.trim().toLowerCase();
|
|
1925
|
-
const matcher = new RegExp(`\\b${escapeRegExp(queryWord)}\\b`);
|
|
1926
|
-
const lines = body.split(/\r?\n/);
|
|
1927
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
1928
|
-
const line = String(lines[i] ?? "");
|
|
1929
|
-
const lower = line.toLowerCase();
|
|
1930
|
-
if (!lower.includes(queryWord)) continue;
|
|
1931
|
-
if (!matcher.test(lower) || !looksLikeDeclarationLine(line)) continue;
|
|
1932
|
-
signature = signature ?? line.trim();
|
|
1933
|
-
const prev = lines.slice(Math.max(0, i - 1), i).find((v) => String(v).trim().length > 0)?.trim();
|
|
1934
|
-
const next = lines.slice(i + 1, i + 3).find((v) => String(v).trim().length > 0)?.trim();
|
|
1935
|
-
declarationContext = declarationContext ?? [prev, line.trim(), next].filter(Boolean).join(" | ");
|
|
1936
|
-
break;
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1942
|
+
const signature = (typeof basePreview.signature === "string" && basePreview.signature.trim().length > 0 ? basePreview.signature.trim() : void 0) ?? extractSignatureFromBody(body, query);
|
|
1939
1943
|
const includePathFromUri = uri.startsWith("code://oscar/include/") ? uri.replace("code://oscar/include/", "") : void 0;
|
|
1940
|
-
const includePath = typeof basePreview.includePath === "string" && basePreview.includePath.trim().length > 0 ? basePreview.includePath.trim() : includePathFromUri;
|
|
1944
|
+
const includePath = resultType === "headers" ? typeof basePreview.includePath === "string" && basePreview.includePath.trim().length > 0 ? basePreview.includePath.trim() : includePathFromUri : void 0;
|
|
1941
1945
|
const summaryCandidate = typeof basePreview.summary === "string" && basePreview.summary.trim().length > 0 ? basePreview.summary : fallbackSummary;
|
|
1942
1946
|
return {
|
|
1943
1947
|
summary: summaryCandidate || makeExcerpt(body || String(result?.title ?? ""), 700),
|
|
1944
1948
|
...signature ? { signature } : {},
|
|
1945
|
-
...includePath ? { include_path: includePath } : {}
|
|
1946
|
-
...declarationContext ? { declaration_context: declarationContext } : {}
|
|
1949
|
+
...includePath ? { include_path: includePath } : {}
|
|
1947
1950
|
};
|
|
1948
1951
|
}
|
|
1949
|
-
function
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
const body = String(result?.body ?? "");
|
|
1958
|
-
if (body) {
|
|
1959
|
-
const exactWord = new RegExp(`\\b${escapeRegExp(symbolLower)}\\b`);
|
|
1960
|
-
const lines = body.split(/\r?\n/);
|
|
1961
|
-
for (const line of lines) {
|
|
1962
|
-
const lower = line.toLowerCase();
|
|
1963
|
-
if (!lower.includes(symbolLower)) continue;
|
|
1964
|
-
if (exactWord.test(lower)) {
|
|
1965
|
-
boost = Math.max(boost, looksLikeDeclarationLine(line) ? 70 : 28);
|
|
1966
|
-
break;
|
|
1967
|
-
}
|
|
1968
|
-
boost = Math.max(boost, 10);
|
|
1952
|
+
function normalizeSearchResults(raw) {
|
|
1953
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1954
|
+
for (const result of raw) {
|
|
1955
|
+
const uri = String(result?.uri ?? "");
|
|
1956
|
+
if (!uri) continue;
|
|
1957
|
+
const prev = seen.get(uri);
|
|
1958
|
+
if (!prev || Number(result?.score ?? 0) > Number(prev?.score ?? 0)) {
|
|
1959
|
+
seen.set(uri, result);
|
|
1969
1960
|
}
|
|
1970
1961
|
}
|
|
1971
|
-
|
|
1972
|
-
if (boost > 0 && uri.startsWith("code://oscar/include/")) boost += 12;
|
|
1973
|
-
return boost;
|
|
1974
|
-
}
|
|
1975
|
-
function isExactSymbolQuery(query) {
|
|
1976
|
-
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(query.trim());
|
|
1962
|
+
return [...seen.values()];
|
|
1977
1963
|
}
|
|
1978
|
-
function
|
|
1979
|
-
|
|
1964
|
+
function toClassificationSummary(classification) {
|
|
1965
|
+
const confidence = Number(classification?.confidence ?? 0.25);
|
|
1966
|
+
const confidenceBucket = classification?.confidenceBucket === "high" || classification?.confidenceBucket === "medium" || classification?.confidenceBucket === "low" ? classification.confidenceBucket : confidence >= 0.8 ? "high" : confidence >= 0.55 ? "medium" : "low";
|
|
1967
|
+
const scope = classification?.facets?.scope === "manual" ? "manual" : "code";
|
|
1968
|
+
const neutralTrack = scope === "manual" ? "compiler_language" : "fundamentals";
|
|
1969
|
+
const track = String(classification?.primaryTrack ?? neutralTrack);
|
|
1970
|
+
const status = classification?.primaryTrackStatus === "asserted" || classification?.primaryTrackStatus === "neutral_fallback" ? classification.primaryTrackStatus : confidence >= 0.62 ? "asserted" : "neutral_fallback";
|
|
1971
|
+
return {
|
|
1972
|
+
track,
|
|
1973
|
+
track_status: status,
|
|
1974
|
+
confidence_bucket: confidenceBucket
|
|
1975
|
+
};
|
|
1980
1976
|
}
|
|
1981
|
-
function
|
|
1982
|
-
|
|
1977
|
+
function extractSystems(classification) {
|
|
1978
|
+
if (!Array.isArray(classification?.facets?.systems)) return [];
|
|
1983
1979
|
const out = /* @__PURE__ */ new Set();
|
|
1984
|
-
for (const
|
|
1985
|
-
|
|
1986
|
-
|
|
1980
|
+
for (const item of classification.facets.systems) {
|
|
1981
|
+
const normalized = normalizeSystemToken(String(item ?? ""));
|
|
1982
|
+
if (normalized) out.add(normalized);
|
|
1987
1983
|
}
|
|
1988
|
-
if (tokens.includes("vic") && tokens.includes("bank")) out.add("vic_setbank");
|
|
1989
|
-
if (tokens.includes("screenmem")) out.add("d018");
|
|
1990
1984
|
return [...out];
|
|
1991
1985
|
}
|
|
1992
|
-
function
|
|
1993
|
-
const
|
|
1994
|
-
if (
|
|
1995
|
-
return terms.join(" ");
|
|
1996
|
-
}
|
|
1997
|
-
function hasApiIntent(query) {
|
|
1998
|
-
const tokens = tokenizeQuery(query);
|
|
1999
|
-
return tokens.some(
|
|
2000
|
-
(token) => ["vic", "d018", "bank", "banking", "charset", "screenmem", "memmap", "charwin", "rasterirq", "sprite"].includes(
|
|
2001
|
-
token
|
|
2002
|
-
)
|
|
2003
|
-
);
|
|
2004
|
-
}
|
|
2005
|
-
function hasImplementationIntent(query) {
|
|
2006
|
-
const tokens = tokenizeQuery(query);
|
|
2007
|
-
return tokens.some((token) => ["setup", "init", "move", "print", "poll", "wait", "split"].includes(token));
|
|
2008
|
-
}
|
|
2009
|
-
function isActionableResult(result) {
|
|
2010
|
-
const uri = String(result?.uri ?? "");
|
|
2011
|
-
if (uri.startsWith("code://oscar/include/")) return uri.toLowerCase().endsWith(".h");
|
|
2012
|
-
const signature = String(result?.preview?.signature ?? "");
|
|
2013
|
-
if (signature.trim().length > 0) return true;
|
|
2014
|
-
return uri.startsWith("code://tutorial/") || uri.startsWith("code://sample/");
|
|
2015
|
-
}
|
|
2016
|
-
function isLowConfidenceResultSet(rawResults, query) {
|
|
2017
|
-
if (rawResults.length === 0) return true;
|
|
2018
|
-
const top = Number(rawResults[0]?.score ?? 0);
|
|
2019
|
-
const second = Number(rawResults[1]?.score ?? 0);
|
|
2020
|
-
if (top < 0.6) return true;
|
|
2021
|
-
if (second > 0 && top / second < 1.12) return true;
|
|
2022
|
-
if (hasApiIntent(query) && !rawResults.slice(0, 3).some((entry) => isActionableResult(entry))) return true;
|
|
2023
|
-
return false;
|
|
2024
|
-
}
|
|
2025
|
-
function computeIntentBoost(result, query) {
|
|
2026
|
-
const uri = String(result?.uri ?? "");
|
|
1986
|
+
function hasQuerySignal(result, query) {
|
|
1987
|
+
const queryTokens = tokenizeQuery(query);
|
|
1988
|
+
if (queryTokens.length === 0) return false;
|
|
2027
1989
|
const title = String(result?.title ?? "").toLowerCase();
|
|
1990
|
+
const snippet = String(result?.snippet ?? "").toLowerCase();
|
|
2028
1991
|
const body = String(result?.body ?? "").toLowerCase();
|
|
2029
|
-
|
|
2030
|
-
if (hasApiIntent(query) && uri.startsWith("code://oscar/include/")) boost += 14;
|
|
2031
|
-
if (hasImplementationIntent(query) && (uri.startsWith("code://tutorial/") || uri.startsWith("code://sample/"))) {
|
|
2032
|
-
boost += 8;
|
|
2033
|
-
}
|
|
2034
|
-
const expandedTerms = expandQueryTerms(query);
|
|
2035
|
-
for (const term of expandedTerms) {
|
|
2036
|
-
if (term.length < 3) continue;
|
|
2037
|
-
if (title.includes(term)) boost += 1.2;
|
|
2038
|
-
else if (body.includes(term)) boost += 0.5;
|
|
2039
|
-
}
|
|
2040
|
-
if (uri.includes("vic.h") && /\b(vic|d018|screenmem|charset|bank)\b/i.test(query)) boost += 10;
|
|
2041
|
-
if (uri.includes("memmap.h") && /\b(memmap|bank|banking|rom|ram)\b/i.test(query)) boost += 9;
|
|
2042
|
-
if (uri.toLowerCase().includes("screenmem") && /\b(screenmem|screen memory|charset|d018)\b/i.test(query)) boost += 11;
|
|
2043
|
-
return boost;
|
|
1992
|
+
return queryTokens.some((token) => title.includes(token) || snippet.includes(token) || body.includes(token));
|
|
2044
1993
|
}
|
|
2045
|
-
function
|
|
2046
|
-
|
|
2047
|
-
if (
|
|
2048
|
-
if (
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
}
|
|
1994
|
+
function hasClearCodingValue(hit) {
|
|
1995
|
+
if (typeof hit.preview.signature === "string" && hit.preview.signature.trim().length > 0) return true;
|
|
1996
|
+
if (hit.resultType === "headers" && typeof hit.preview.include_path === "string" && hit.preview.include_path.length > 0) return true;
|
|
1997
|
+
if ((hit.referencedUris?.length ?? 0) > 0) return true;
|
|
1998
|
+
if ((hit.resultType === "tutorials" || hit.resultType === "samples" || hit.resultType === "topics") && String(hit.preview.summary ?? "").trim().length > 0) {
|
|
1999
|
+
return true;
|
|
2000
|
+
}
|
|
2001
|
+
if (hit.hasQuerySignal && (hit.resultType === "tutorials" || hit.resultType === "samples" || hit.resultType === "topics")) {
|
|
2002
|
+
return true;
|
|
2003
|
+
}
|
|
2004
|
+
return false;
|
|
2055
2005
|
}
|
|
2056
|
-
function
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2006
|
+
function computeRankingBoost(hit) {
|
|
2007
|
+
let boost = 0;
|
|
2008
|
+
if (hit.resultType === "headers") boost += 20;
|
|
2009
|
+
if (hit.resultType === "headers" && hit.classificationSummary.track_status === "asserted") boost += 12;
|
|
2010
|
+
if (hit.resultType === "tutorials" || hit.resultType === "samples") boost += 6;
|
|
2011
|
+
if (hit.preview.signature) boost += 16;
|
|
2012
|
+
if ((hit.referencedUris?.length ?? 0) > 0) boost += 8;
|
|
2013
|
+
if (hit.hasQuerySignal) boost += 5;
|
|
2014
|
+
const symbol = hit.query.trim().toLowerCase();
|
|
2015
|
+
if (isExactSymbolQuery(hit.query)) {
|
|
2016
|
+
const signature = String(hit.preview.signature ?? "").toLowerCase();
|
|
2017
|
+
if (signature.includes(symbol)) boost += 42;
|
|
2018
|
+
if (hit.resultType === "headers" && hit.uri.toLowerCase().includes(`${symbol}.h`)) boost += 18;
|
|
2019
|
+
}
|
|
2020
|
+
if (hit.classificationSummary.track === "fundamentals" && hit.classificationSummary.confidence_bucket === "low") {
|
|
2021
|
+
boost -= 8;
|
|
2065
2022
|
}
|
|
2066
|
-
return
|
|
2023
|
+
return boost;
|
|
2067
2024
|
}
|
|
2068
2025
|
async function executeSearch(context) {
|
|
2069
|
-
const { query, limit
|
|
2026
|
+
const { query, limit } = context;
|
|
2070
2027
|
const requestedType = context.type ?? "all";
|
|
2071
2028
|
const system = context.system ?? "c64";
|
|
2072
2029
|
const state = await getStateSnapshot();
|
|
2073
|
-
const
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
{
|
|
2089
|
-
label: "fundamentals",
|
|
2090
|
-
facet: "primary_track",
|
|
2091
|
-
reason: "Fallback classification due to missing source metadata",
|
|
2092
|
-
weight: 3,
|
|
2093
|
-
matched_on: "fallback",
|
|
2094
|
-
source_field: "derived",
|
|
2095
|
-
matched_text: "fallback",
|
|
2096
|
-
direct: true
|
|
2097
|
-
}
|
|
2098
|
-
]
|
|
2099
|
-
});
|
|
2100
|
-
const toDetails = (classification) => {
|
|
2101
|
-
const fallback = toFallbackClassification();
|
|
2102
|
-
const normalizeSystems = (systems) => {
|
|
2103
|
-
if (!Array.isArray(systems)) return [];
|
|
2104
|
-
const out = /* @__PURE__ */ new Set();
|
|
2105
|
-
for (const entry of systems) {
|
|
2106
|
-
const normalized = normalizeSystemToken(String(entry ?? ""));
|
|
2107
|
-
if (normalized) out.add(normalized);
|
|
2108
|
-
}
|
|
2109
|
-
return [...out];
|
|
2110
|
-
};
|
|
2111
|
-
if (!classification || typeof classification !== "object") return fallback;
|
|
2112
|
-
return normalizePrimaryTrackForConfidence({
|
|
2113
|
-
primary_track: String(classification.primaryTrack ?? fallback.primary_track),
|
|
2114
|
-
primary_track_status: classification.primaryTrackStatus === "asserted" || classification.primaryTrackStatus === "neutral_fallback" ? classification.primaryTrackStatus : fallback.primary_track_status,
|
|
2115
|
-
facets: {
|
|
2116
|
-
domain: Array.isArray(classification.facets?.domain) ? classification.facets.domain.map(String) : [],
|
|
2117
|
-
hardware: Array.isArray(classification.facets?.hardware) ? classification.facets.hardware.map(String) : [],
|
|
2118
|
-
technique: Array.isArray(classification.facets?.technique) ? classification.facets.technique.map(String) : [],
|
|
2119
|
-
abstraction: Array.isArray(classification.facets?.abstraction) ? classification.facets.abstraction.map(String) : [],
|
|
2120
|
-
artifact: Array.isArray(classification.facets?.artifact) ? classification.facets.artifact.map(String) : [],
|
|
2121
|
-
systems: normalizeSystems(classification.facets?.systems),
|
|
2122
|
-
scope: classification.facets?.scope === "tutorial" || classification.facets?.scope === "sample" || classification.facets?.scope === "manual" ? classification.facets.scope : fallback.facets.scope
|
|
2123
|
-
},
|
|
2124
|
-
confidence: Number(classification.confidence ?? fallback.confidence),
|
|
2125
|
-
confidence_bucket: classification.confidenceBucket === "low" || classification.confidenceBucket === "medium" || classification.confidenceBucket === "high" ? classification.confidenceBucket : fallback.confidence_bucket,
|
|
2126
|
-
evidence: Array.isArray(classification.evidence) ? classification.evidence.map((item) => ({
|
|
2127
|
-
label: String(item?.label ?? ""),
|
|
2128
|
-
facet: item?.facet === "primary_track" || item?.facet === "domain" || item?.facet === "hardware" || item?.facet === "technique" || item?.facet === "abstraction" || item?.facet === "artifact" ? item.facet : "primary_track",
|
|
2129
|
-
reason: String(item?.reason ?? ""),
|
|
2130
|
-
weight: Number(item?.weight ?? 0),
|
|
2131
|
-
matched_on: String(item?.matchedOn ?? item?.matched_on ?? ""),
|
|
2132
|
-
source_field: item?.sourceField === "title" || item?.sourceField === "rel_path" || item?.sourceField === "text" || item?.sourceField === "files" || item?.sourceField === "asset_refs" || item?.sourceField === "derived" ? item.sourceField : item?.source_field === "title" || item?.source_field === "rel_path" || item?.source_field === "text" || item?.source_field === "files" || item?.source_field === "asset_refs" || item?.source_field === "derived" ? item.source_field : "derived",
|
|
2133
|
-
matched_text: String(item?.matchedText ?? item?.matched_text ?? ""),
|
|
2134
|
-
direct: Boolean(item?.direct)
|
|
2135
|
-
})) : []
|
|
2136
|
-
});
|
|
2137
|
-
};
|
|
2138
|
-
const toSummary = (details) => ({
|
|
2139
|
-
track: details.primary_track,
|
|
2140
|
-
track_status: details.primary_track_status,
|
|
2141
|
-
confidence_bucket: details.confidence_bucket,
|
|
2142
|
-
confidence: details.confidence,
|
|
2143
|
-
domains: details.facets.domain,
|
|
2144
|
-
hardware: details.facets.hardware,
|
|
2145
|
-
techniques: details.facets.technique,
|
|
2146
|
-
systems: details.facets.systems,
|
|
2147
|
-
scope: details.facets.scope
|
|
2148
|
-
});
|
|
2149
|
-
const primaryResults = state.searchIndex.search(query, {
|
|
2150
|
-
combineWith: inferCombineMode(query),
|
|
2151
|
-
prefix: true,
|
|
2152
|
-
fuzzy: 0.12
|
|
2153
|
-
});
|
|
2154
|
-
const symbolQuery = isExactSymbolQuery(query);
|
|
2155
|
-
const expandedQuery = buildExpandedQuery(query);
|
|
2156
|
-
const useFallback = !symbolQuery && (isLowConfidenceResultSet(primaryResults, query) || expandedQuery !== query);
|
|
2157
|
-
const expandedResults = useFallback ? state.searchIndex.search(expandedQuery, {
|
|
2158
|
-
combineWith: "OR",
|
|
2159
|
-
prefix: true,
|
|
2160
|
-
fuzzy: 0.16
|
|
2161
|
-
}) : [];
|
|
2162
|
-
const rawResults = normalizeSearchResults([
|
|
2163
|
-
...primaryResults,
|
|
2164
|
-
...expandedResults.map((item) => ({
|
|
2165
|
-
...item,
|
|
2166
|
-
score: Number(item?.score ?? 0) * 0.82
|
|
2167
|
-
}))
|
|
2168
|
-
]);
|
|
2030
|
+
const rawResults = normalizeSearchResults(
|
|
2031
|
+
state.searchIndex.search(query, {
|
|
2032
|
+
combineWith: inferCombineMode(query),
|
|
2033
|
+
prefix: true,
|
|
2034
|
+
fuzzy: 0.12
|
|
2035
|
+
})
|
|
2036
|
+
);
|
|
2037
|
+
const fallbackResults = rawResults.length === 0 ? normalizeSearchResults(
|
|
2038
|
+
state.searchIndex.search(query, {
|
|
2039
|
+
combineWith: "OR",
|
|
2040
|
+
prefix: true,
|
|
2041
|
+
fuzzy: 0.16
|
|
2042
|
+
})
|
|
2043
|
+
) : [];
|
|
2044
|
+
const mergedResults = normalizeSearchResults([...rawResults, ...fallbackResults]);
|
|
2169
2045
|
const mapped = await Promise.all(
|
|
2170
|
-
|
|
2171
|
-
const details = toDetails(result.classification);
|
|
2172
|
-
const referencedUris = await resolveReferencedUris(state, String(result.uri ?? ""), result.referencedFiles);
|
|
2046
|
+
mergedResults.map(async (result) => {
|
|
2173
2047
|
const resultType = inferResultType(result);
|
|
2174
|
-
const
|
|
2048
|
+
const preview = buildPreview(result, query, resultType);
|
|
2049
|
+
const referencedUris = await resolveReferencedUris(state, String(result.uri ?? ""), result.referencedFiles);
|
|
2050
|
+
const classificationSummary = toClassificationSummary(result.classification);
|
|
2051
|
+
const systems = extractSystems(result.classification);
|
|
2052
|
+
const hasSignal = hasQuerySignal(result, query);
|
|
2053
|
+
const baseScore = Number(result?.score ?? 0);
|
|
2054
|
+
const rankScore = baseScore + computeRankingBoost({
|
|
2055
|
+
uri: String(result.uri ?? ""),
|
|
2056
|
+
resultType,
|
|
2057
|
+
preview,
|
|
2058
|
+
query,
|
|
2059
|
+
referencedUris,
|
|
2060
|
+
classificationSummary,
|
|
2061
|
+
hasQuerySignal: hasSignal
|
|
2062
|
+
});
|
|
2175
2063
|
return {
|
|
2176
|
-
|
|
2064
|
+
rankScore,
|
|
2065
|
+
systems,
|
|
2066
|
+
keep: hasClearCodingValue({
|
|
2067
|
+
resultType,
|
|
2068
|
+
preview,
|
|
2069
|
+
referencedUris,
|
|
2070
|
+
hasQuerySignal: hasSignal
|
|
2071
|
+
}),
|
|
2177
2072
|
hit: {
|
|
2178
|
-
source: result.source === "manual" ? "manual" : "code",
|
|
2179
2073
|
result_type: resultType,
|
|
2180
2074
|
uri: String(result.uri ?? ""),
|
|
2181
2075
|
title: String(result.title ?? ""),
|
|
2182
|
-
preview
|
|
2183
|
-
score: (result.score ?? 0) + rerankBoost,
|
|
2076
|
+
preview,
|
|
2184
2077
|
...referencedUris ? { referenced_files: referencedUris } : {},
|
|
2185
|
-
classification_summary:
|
|
2186
|
-
...include_details ? { classification_details: details } : {}
|
|
2078
|
+
classification_summary: classificationSummary
|
|
2187
2079
|
}
|
|
2188
2080
|
};
|
|
2189
2081
|
})
|
|
2190
2082
|
);
|
|
2191
|
-
const hits = mapped.filter((entry) => {
|
|
2083
|
+
const hits = mapped.filter((entry) => entry.keep).filter((entry) => {
|
|
2192
2084
|
const uri = String(entry.hit.uri ?? "");
|
|
2193
2085
|
if (uri.startsWith("code://oscar/include/") && !uri.toLowerCase().endsWith(".h")) return false;
|
|
2194
2086
|
return true;
|
|
2195
|
-
}).filter((entry) => requestedType === "all" || entry.hit.result_type === requestedType).filter((entry) => matchesSystemFilter(entry.
|
|
2087
|
+
}).filter((entry) => requestedType === "all" || entry.hit.result_type === requestedType).filter((entry) => matchesSystemFilter(entry.systems, system)).sort((a, b) => b.rankScore - a.rankScore || a.hit.uri.localeCompare(b.hit.uri)).slice(0, limit);
|
|
2196
2088
|
return {
|
|
2197
2089
|
ok: true,
|
|
2198
2090
|
data: {
|
|
@@ -2206,7 +2098,19 @@ var searchTool = createTool3({
|
|
|
2206
2098
|
inputSchema: searchInputSchema,
|
|
2207
2099
|
outputSchema: searchOutputSchema,
|
|
2208
2100
|
execute: async (input) => {
|
|
2209
|
-
const
|
|
2101
|
+
const rawInput = input?.context ?? input;
|
|
2102
|
+
if (Object.prototype.hasOwnProperty.call(rawInput, "include_details")) {
|
|
2103
|
+
return {
|
|
2104
|
+
ok: false,
|
|
2105
|
+
error: {
|
|
2106
|
+
code: "INVALID_INPUT",
|
|
2107
|
+
message: "include_details is no longer supported.",
|
|
2108
|
+
hint: "Remove include_details and use classification_summary only.",
|
|
2109
|
+
recoverable: true
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
const context = rawInput;
|
|
2210
2114
|
try {
|
|
2211
2115
|
return await executeSearch(context);
|
|
2212
2116
|
} catch (error) {
|