{"version":3,"file":"external-tools.d.ts","sourceRoot":"","sources":["../../src/core/external-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAgC,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnH,0DAA0D;AAC1D,MAAM,MAAM,WAAW;AACtB,4CAA4C;AAC1C,SAAS;AACX,2DAA2D;GACzD,WAAW;AACb,gFAAgF;GAC9E,QAAQ,CAAC;AAEZ,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,kEAAkE;IAClE,GAAG,EAAE,MAAM,EAAE,CAAC;IACd;;;;OAIG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iEAAiE;IACjE,YAAY,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,eAAe,EA+DpD,CAAC;AAEF,MAAM,WAAW,kBAAmB,SAAQ,eAAe,EAAE,iBAAiB;IAC7E,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAY9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,kBAAkB,EAAE,CAY5D;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAMtG","sourcesContent":["/**\n * The external (Rust) binaries hoocode can use, and what each one is worth.\n *\n * hoocode is self-sufficient without any of them: `SearchCodebase` and @-file\n * autocomplete fall back to pure-JS implementations, and the features that have\n * no fallback (web, voice) are off or inert rather than broken. These binaries are an\n * *expansion* layer, which is exactly why they were invisible - nothing failed\n * loudly enough to tell anyone they existed.\n *\n * This module is the single description of that layer. The `/settings` pane\n * renders it; the same table says which settings rows are gated on a binary, so\n * a row that cannot do anything today can say so instead of lying.\n */\n\nimport { getToolStatus, isOfflineMode, type ManagedTool, type ManagedToolStatus } from \"../utils/tools-manager.js\";\n\n/** How hoocode gets a binary it does not already have. */\nexport type Acquisition =\n\t/** Fetched in the background at startup. */\n\t| \"startup\"\n\t/** Fetched the first time the feature is actually used. */\n\t| \"on-demand\"\n\t/** Never fetched implicitly; install it yourself or point the env var at it. */\n\t| \"manual\";\n\nexport interface ExternalToolDoc {\n\ttool: ManagedTool;\n\t/** Row label in the pane. */\n\tlabel: string;\n\t/** What the binary does for hoocode, in one line. */\n\tsummary: string;\n\t/** What it turns on, feature by feature. */\n\tenables: string[];\n\t/** What hoocode does instead when it is missing. Never \"nothing works\". */\n\tfallback: string;\n\tacquisition: Acquisition;\n\t/** Env vars that change how this binary is resolved or driven. */\n\tenv: string[];\n\t/**\n\t * `/settings` row ids whose effect depends on this binary. A row listed here\n\t * is annotated (not hidden) while the binary is missing: hiding it would\n\t * recreate the discoverability hole this whole surface exists to close.\n\t */\n\tdependentRows: string[];\n\t/** settings.json keys this binary gates, for docs and search. */\n\tsettingsKeys: string[];\n}\n\n/**\n * Ordered so the two that silently make everything faster come first, then the\n * three that add capability hoocode does not otherwise have.\n */\nexport const EXTERNAL_TOOLS: readonly ExternalToolDoc[] = [\n\t{\n\t\ttool: \"rg\",\n\t\tlabel: \"ripgrep (rg)\",\n\t\tsummary: \"Fast path for content search.\",\n\t\tenables: [\"the lexical half of search runs rg instead of the JS scanner\"],\n\t\tfallback:\n\t\t\t\"A pure-JS scanner produces the same match shape, so results are identical - it is materially slower on large trees and respects fewer ignore-file edge cases.\",\n\t\tacquisition: \"startup\",\n\t\tenv: [\"HOOCODE_RG_BINARY\", \"HOOCODE_NATIVE_SEARCH=1 forces the JS path even when rg is present\"],\n\t\tdependentRows: [],\n\t\tsettingsKeys: [],\n\t},\n\t{\n\t\ttool: \"fd\",\n\t\tlabel: \"fd\",\n\t\tsummary: \"Fast path for filename search.\",\n\t\tenables: [\"@-file autocomplete lists paths with fd instead of the JS directory walker\"],\n\t\tfallback:\n\t\t\t\"A JS walker produces the same result shape - slower on large trees, and glob/ignore handling is the JS approximation rather than fd's.\",\n\t\tacquisition: \"startup\",\n\t\tenv: [\"HOOCODE_FD_BINARY\", \"HOOCODE_NATIVE_SEARCH=1 forces the JS path even when fd is present\"],\n\t\tdependentRows: [],\n\t\tsettingsKeys: [],\n\t},\n\t{\n\t\ttool: \"embsearch\",\n\t\tlabel: \"embsearch (semantic index)\",\n\t\tsummary: \"Local embedding index. The only source of semantic ranking in hoocode.\",\n\t\tenables: [\n\t\t\t\"search fuses semantic hits with its lexical hits\",\n\t\t\t\"MCP/capability deferral ranks tools by meaning rather than keyword\",\n\t\t],\n\t\tfallback:\n\t\t\t\"search is lexical-only and capability lookup ranks lexically. Nothing errors; queries phrased by intent rather than by token simply rank worse. Requires the ONNX build - the mock build is rejected on purpose, because it would rank at random while looking healthy.\",\n\t\tacquisition: \"on-demand\",\n\t\tenv: [\"HOOCODE_EMBSEARCH_BINARY\"],\n\t\tdependentRows: [\"group:embsearch\"],\n\t\tsettingsKeys: [\"enableSemanticIndex\", \"embsearchBinaryPath\", \"embsearchThresholdBytes\"],\n\t},\n\t{\n\t\ttool: \"webtools\",\n\t\tlabel: \"webtools (webfetch/websearch)\",\n\t\tsummary: \"The network layer. Without it hoocode has no way to reach the internet.\",\n\t\tenables: [\"the webfetch tool\", \"the websearch tool\"],\n\t\tfallback:\n\t\t\t\"Both tools return an error when called. The web tool group is off by default, so a missing binary is invisible until you turn the group on.\",\n\t\tacquisition: \"on-demand\",\n\t\tenv: [\"HOOCODE_WEBTOOLS_BINARY\", \"HOOCODE_WEBTOOLS_TIMEOUT\"],\n\t\tdependentRows: [\"group:web\", \"webtools-timeout-secs\"],\n\t\tsettingsKeys: [\"enableWebTools\", \"webtools.timeoutSecs\"],\n\t},\n\t{\n\t\ttool: \"voicetools\",\n\t\tlabel: \"voicetools (voice input)\",\n\t\tsummary: \"Microphone capture and transcription for the TUI.\",\n\t\tenables: [\"push-to-talk voice input in the editor\"],\n\t\tfallback: \"Voice capture reports an error and never starts. Typing is unaffected.\",\n\t\tacquisition: \"on-demand\",\n\t\tenv: [\"VOICETOOLS_BIN\", \"HOOCODE_VOICETOOLS_BINARY\", \"VOICETOOLS_SILENCE_MS\"],\n\t\tdependentRows: [\"voice-silence-ms\"],\n\t\tsettingsKeys: [\"voice.silenceMs\"],\n\t},\n];\n\nexport interface ExternalToolStatus extends ExternalToolDoc, ManagedToolStatus {\n\tinstalled: boolean;\n\t/**\n\t * Whether hoocode would fetch it if the feature were used right now. False in\n\t * offline mode, and on Android where the published Linux builds do not run.\n\t */\n\tdownloadable: boolean;\n}\n\n/** Short, fixed-width-ish status word for the pane's value column. */\nexport function statusLabel(status: ExternalToolStatus): string {\n\tif (!status.installed) return status.downloadable ? \"not installed\" : \"unavailable\";\n\tswitch (status.source) {\n\t\tcase \"override\":\n\t\t\treturn \"env override\";\n\t\tcase \"managed\":\n\t\t\treturn \"installed\";\n\t\tcase \"path\":\n\t\t\treturn \"system\";\n\t\tdefault:\n\t\t\treturn \"installed\";\n\t}\n}\n\n/**\n * Resolve every external tool. Never downloads: this is what the pane shows\n * *before* the user opts into anything.\n */\nexport function describeExternalTools(): ExternalToolStatus[] {\n\tconst offline = isOfflineMode();\n\tconst android = process.platform === \"android\";\n\treturn EXTERNAL_TOOLS.map((doc) => {\n\t\tconst status = getToolStatus(doc.tool);\n\t\treturn {\n\t\t\t...doc,\n\t\t\t...status,\n\t\t\tinstalled: status.path !== null,\n\t\t\tdownloadable: !offline && !android && doc.acquisition !== \"manual\",\n\t\t};\n\t});\n}\n\n/** Index of pane-row id -> the tool it needs, for annotating gated rows. */\nexport function buildRowGates(statuses: readonly ExternalToolStatus[]): Map<string, ExternalToolStatus> {\n\tconst gates = new Map<string, ExternalToolStatus>();\n\tfor (const status of statuses) {\n\t\tfor (const rowId of status.dependentRows) gates.set(rowId, status);\n\t}\n\treturn gates;\n}\n"]}