@sema-agent/core
Version:
Stateless, task-oriented AI agent core
148 lines (147 loc) • 5.71 kB
JavaScript
import { pathToFileURL } from "node:url";
export function pathToUri(p) {
if (p.startsWith("/")) {
return `file://${p.split("/").map(encodeURIComponent).join("/")}`;
}
const drive = /^([A-Za-z]):[\\/](.*)$/.exec(p);
if (drive)
return `file:///${drive[1]}:/${drive[2].split(/[\\/]/).map(encodeURIComponent).join("/")}`;
if (p.startsWith("\\\\"))
return `file://${p.slice(2).split(/[\\/]/).map(encodeURIComponent).join("/")}`;
return pathToFileURL(p).href;
}
export function uriToPath(uri) {
if (!uri.startsWith("file://"))
return uri;
let rest = uri.slice("file://".length);
if (rest.length > 0 && !rest.startsWith("/"))
rest = "//" + rest;
try {
rest = decodeURIComponent(rest);
}
catch {
}
const m = /^\/([A-Za-z]:)(\/.*)?$/.exec(rest);
if (m)
return m[1] + (m[2] ?? "/").replace(/\//g, "\\");
return rest;
}
export function callHierarchyMethod(op) {
return op === "incomingCalls" ? "callHierarchy/incomingCalls" : "callHierarchy/outgoingCalls";
}
export function buildRequest(op, params) {
const uri = pathToUri(params.filePath);
const position = { line: Math.max(0, (params.line ?? 1) - 1), character: params.character ?? 0 };
const td = { textDocument: { uri } };
switch (op) {
case "goToDefinition":
return { method: "textDocument/definition", params: { ...td, position } };
case "findReferences":
return { method: "textDocument/references", params: { ...td, position, context: { includeDeclaration: true } } };
case "hover":
return { method: "textDocument/hover", params: { ...td, position } };
case "goToImplementation":
return { method: "textDocument/implementation", params: { ...td, position } };
case "documentSymbol":
return { method: "textDocument/documentSymbol", params: td };
case "workspaceSymbol":
return { method: "workspace/symbol", params: { query: params.symbol ?? "" } };
case "prepareCallHierarchy":
case "incomingCalls":
case "outgoingCalls":
return { method: "textDocument/prepareCallHierarchy", params: { ...td, position } };
}
}
export function parseResult(op, raw, fallbackUri) {
switch (op) {
case "goToDefinition":
case "findReferences":
case "goToImplementation": {
const locations = toLocations(raw);
return locations.length ? { kind: "locations", locations } : { kind: "none" };
}
case "hover": {
const contents = hoverContents(raw);
return contents ? { kind: "hover", contents } : { kind: "none" };
}
case "documentSymbol":
case "workspaceSymbol":
case "prepareCallHierarchy": {
const symbols = toSymbols(raw, op === "documentSymbol" ? fallbackUri : undefined);
return symbols.length ? { kind: "symbols", symbols } : { kind: "none" };
}
case "incomingCalls":
case "outgoingCalls": {
const symbols = toCallSymbols(raw);
return symbols.length ? { kind: "symbols", symbols } : { kind: "none" };
}
}
}
function asArray(raw) {
if (raw == null)
return [];
return (Array.isArray(raw) ? raw : [raw]);
}
function locFrom(uri, range) {
const s = range?.start ?? { line: 0, character: 0 };
return { uri, line: s.line + 1, character: s.character };
}
function toLocations(raw) {
return asArray(raw)
.map((x) => {
if (typeof x?.uri === "string")
return locFrom(x.uri, x.range);
if (typeof x?.targetUri === "string")
return locFrom(x.targetUri, (x.targetSelectionRange ?? x.targetRange));
return undefined;
})
.filter((l) => l !== undefined);
}
function toSymbols(raw, fallbackUri) {
const out = [];
const walk = (x, inheritedUri) => {
const loc = x.location;
const uri = loc?.uri ?? (typeof x.uri === "string" ? x.uri : inheritedUri);
const range = (loc?.range ?? x.selectionRange ?? x.range);
if (typeof x.name === "string" && uri) {
const s = range?.start;
out.push({
name: x.name,
...(symbolKindName(x.kind) ? { kind: symbolKindName(x.kind) } : {}),
uri,
line: (s?.line ?? 0) + 1,
character: s?.character ?? 0,
...(s === undefined ? { positionUnknown: true } : {}),
});
}
for (const child of asArray(x.children))
walk(child, uri);
};
for (const x of asArray(raw))
walk(x, fallbackUri);
return out;
}
function toCallSymbols(raw) {
return toSymbols(asArray(raw).map((x) => x.from ?? x.to ?? x));
}
function hoverContents(raw) {
const c = raw?.contents;
if (c == null)
return "";
const one = (v) => {
if (typeof v === "string")
return v;
if (v && typeof v === "object" && typeof v.value === "string")
return v.value;
return "";
};
return (Array.isArray(c) ? c.map(one).filter(Boolean).join("\n\n") : one(c)).trim();
}
const SYMBOL_KINDS = [
"File", "Module", "Namespace", "Package", "Class", "Method", "Property", "Field", "Constructor", "Enum",
"Interface", "Function", "Variable", "Constant", "String", "Number", "Boolean", "Array", "Object", "Key",
"Null", "EnumMember", "Struct", "Event", "Operator", "TypeParameter",
];
function symbolKindName(kind) {
return typeof kind === "number" && kind >= 1 && kind <= SYMBOL_KINDS.length ? SYMBOL_KINDS[kind - 1] : undefined;
}