UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

148 lines (147 loc) • 5.71 kB
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; }