dominds 0.1.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/LICENSE +157 -0
- package/README.md +250 -0
- package/README.zh.md +161 -0
- package/dist/access-control.js +253 -0
- package/dist/cli/create.js +263 -0
- package/dist/cli/read.js +84 -0
- package/dist/cli/tui.js +199 -0
- package/dist/cli/webui.js +169 -0
- package/dist/cli.js +227 -0
- package/dist/dialog-factory.js +53 -0
- package/dist/dialog-global-registry.js +68 -0
- package/dist/dialog-instance-registry.js +78 -0
- package/dist/dialog-run-state.js +198 -0
- package/dist/dialog.js +1024 -0
- package/dist/evt-registry.js +103 -0
- package/dist/index.js +8 -0
- package/dist/llm/client.js +69 -0
- package/dist/llm/defaults.yaml +386 -0
- package/dist/llm/driver.js +3214 -0
- package/dist/llm/gen/anthropic.js +611 -0
- package/dist/llm/gen/codex.js +375 -0
- package/dist/llm/gen/mock.js +326 -0
- package/dist/llm/gen/openai.js +470 -0
- package/dist/llm/gen/registry.js +26 -0
- package/dist/llm/gen.js +2 -0
- package/dist/llm/tools-projection.js +37 -0
- package/dist/log.js +228 -0
- package/dist/mcp/config.js +230 -0
- package/dist/mcp/sdk-client.js +129 -0
- package/dist/mcp/server-runtime.js +57 -0
- package/dist/mcp/stdio-client.js +280 -0
- package/dist/mcp/supervisor.js +979 -0
- package/dist/mcp/tool-names.js +109 -0
- package/dist/minds/builtin/cmdr/persona.md +3 -0
- package/dist/minds/builtin/dijiang/knowledge.md +287 -0
- package/dist/minds/builtin/dijiang/persona.md +7 -0
- package/dist/minds/builtin/fuxi/persona.en.md +59 -0
- package/dist/minds/builtin/fuxi/persona.zh.md +49 -0
- package/dist/minds/builtin/pangu/persona.en.md +78 -0
- package/dist/minds/builtin/pangu/persona.zh.md +71 -0
- package/dist/minds/load.js +617 -0
- package/dist/minds/minds-i18n.js +131 -0
- package/dist/minds/system-prompt.js +281 -0
- package/dist/persistence.js +3128 -0
- package/dist/problems.js +109 -0
- package/dist/server/api-routes.js +1031 -0
- package/dist/server/auth.js +180 -0
- package/dist/server/mime-types.js +32 -0
- package/dist/server/prompts-routes.js +543 -0
- package/dist/server/server-core.js +235 -0
- package/dist/server/setup-routes.js +697 -0
- package/dist/server/static-server.js +132 -0
- package/dist/server/websocket-handler.js +1011 -0
- package/dist/server.js +164 -0
- package/dist/shared/async-fifo-mutex.js +36 -0
- package/dist/shared/diligence.js +20 -0
- package/dist/shared/dotenv.js +144 -0
- package/dist/shared/evt.js +195 -0
- package/dist/shared/i18n/driver-messages.js +267 -0
- package/dist/shared/i18n/text.js +9 -0
- package/dist/shared/i18n/tool-result-messages.js +51 -0
- package/dist/shared/rtws-cli.js +73 -0
- package/dist/shared/runtime-language.js +47 -0
- package/dist/shared/team-mgmt-manual.js +116 -0
- package/dist/shared/types/context-health.js +2 -0
- package/dist/shared/types/dialog.js +11 -0
- package/dist/shared/types/i18n.js +2 -0
- package/dist/shared/types/index.js +26 -0
- package/dist/shared/types/language.js +40 -0
- package/dist/shared/types/problems.js +2 -0
- package/dist/shared/types/prompts.js +2 -0
- package/dist/shared/types/q4h.js +7 -0
- package/dist/shared/types/run-state.js +8 -0
- package/dist/shared/types/setup.js +2 -0
- package/dist/shared/types/storage.js +10 -0
- package/dist/shared/types/tellask.js +8 -0
- package/dist/shared/types/tools-registry.js +2 -0
- package/dist/shared/types/wire.js +12 -0
- package/dist/shared/utils/fmt.js +9 -0
- package/dist/shared/utils/html.js +20 -0
- package/dist/shared/utils/id.js +18 -0
- package/dist/shared/utils/inter-dialog-format.js +101 -0
- package/dist/shared/utils/time.js +13 -0
- package/dist/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/static/assets/_baseUniq-Crfl3d5Y.js +661 -0
- package/dist/static/assets/_baseUniq-Crfl3d5Y.js.map +1 -0
- package/dist/static/assets/arc-CbA_x9GD.js +132 -0
- package/dist/static/assets/arc-CbA_x9GD.js.map +1 -0
- package/dist/static/assets/architectureDiagram-VXUJARFQ-lcFS8ZQJ.js +8685 -0
- package/dist/static/assets/architectureDiagram-VXUJARFQ-lcFS8ZQJ.js.map +1 -0
- package/dist/static/assets/blockDiagram-VD42YOAC-B3Q36qRc.js +3608 -0
- package/dist/static/assets/blockDiagram-VD42YOAC-B3Q36qRc.js.map +1 -0
- package/dist/static/assets/c4Diagram-YG6GDRKO-Mt-aq3VH.js +2482 -0
- package/dist/static/assets/c4Diagram-YG6GDRKO-Mt-aq3VH.js.map +1 -0
- package/dist/static/assets/channel-BVr1Yke-.js +8 -0
- package/dist/static/assets/channel-BVr1Yke-.js.map +1 -0
- package/dist/static/assets/chunk-4BX2VUAB-qCIn5Iic.js +17 -0
- package/dist/static/assets/chunk-4BX2VUAB-qCIn5Iic.js.map +1 -0
- package/dist/static/assets/chunk-55IACEB6-q172NeCV.js +14 -0
- package/dist/static/assets/chunk-55IACEB6-q172NeCV.js.map +1 -0
- package/dist/static/assets/chunk-B4BG7PRW-CMJmtYzq.js +1827 -0
- package/dist/static/assets/chunk-B4BG7PRW-CMJmtYzq.js.map +1 -0
- package/dist/static/assets/chunk-DI55MBZ5-DiuwwZPL.js +1916 -0
- package/dist/static/assets/chunk-DI55MBZ5-DiuwwZPL.js.map +1 -0
- package/dist/static/assets/chunk-FMBD7UC4-06sqZTTn.js +20 -0
- package/dist/static/assets/chunk-FMBD7UC4-06sqZTTn.js.map +1 -0
- package/dist/static/assets/chunk-QN33PNHL-CnpBNkpP.js +25 -0
- package/dist/static/assets/chunk-QN33PNHL-CnpBNkpP.js.map +1 -0
- package/dist/static/assets/chunk-QZHKN3VN-CNgjMR-e.js +18 -0
- package/dist/static/assets/chunk-QZHKN3VN-CNgjMR-e.js.map +1 -0
- package/dist/static/assets/chunk-TZMSLE5B-BxtzW6--.js +109 -0
- package/dist/static/assets/chunk-TZMSLE5B-BxtzW6--.js.map +1 -0
- package/dist/static/assets/classDiagram-2ON5EDUG-29huvmn-.js +23 -0
- package/dist/static/assets/classDiagram-2ON5EDUG-29huvmn-.js.map +1 -0
- package/dist/static/assets/classDiagram-v2-WZHVMYZB-29huvmn-.js +23 -0
- package/dist/static/assets/classDiagram-v2-WZHVMYZB-29huvmn-.js.map +1 -0
- package/dist/static/assets/clone-D2OgLSSn.js +9 -0
- package/dist/static/assets/clone-D2OgLSSn.js.map +1 -0
- package/dist/static/assets/cose-bilkent-S5V4N54A-BNegDCxl.js +4943 -0
- package/dist/static/assets/cose-bilkent-S5V4N54A-BNegDCxl.js.map +1 -0
- package/dist/static/assets/cytoscape.esm-Bm8DJGmZ.js +30240 -0
- package/dist/static/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -0
- package/dist/static/assets/dagre-6UL2VRFP-f1XrTRSn.js +695 -0
- package/dist/static/assets/dagre-6UL2VRFP-f1XrTRSn.js.map +1 -0
- package/dist/static/assets/defaultLocale-DVr69WTU.js +207 -0
- package/dist/static/assets/defaultLocale-DVr69WTU.js.map +1 -0
- package/dist/static/assets/diagram-PSM6KHXK-8w1WbeDi.js +849 -0
- package/dist/static/assets/diagram-PSM6KHXK-8w1WbeDi.js.map +1 -0
- package/dist/static/assets/diagram-QEK2KX5R-CF4wtMmR.js +303 -0
- package/dist/static/assets/diagram-QEK2KX5R-CF4wtMmR.js.map +1 -0
- package/dist/static/assets/diagram-S2PKOQOG-8p3Avgn2.js +213 -0
- package/dist/static/assets/diagram-S2PKOQOG-8p3Avgn2.js.map +1 -0
- package/dist/static/assets/erDiagram-Q2GNP2WA-BMKLxlM9.js +1159 -0
- package/dist/static/assets/erDiagram-Q2GNP2WA-BMKLxlM9.js.map +1 -0
- package/dist/static/assets/favicon-Cmg5RbCj.svg +8 -0
- package/dist/static/assets/flowDiagram-NV44I4VS-CgEuPNK2.js +2332 -0
- package/dist/static/assets/flowDiagram-NV44I4VS-CgEuPNK2.js.map +1 -0
- package/dist/static/assets/ganttDiagram-JELNMOA3-bJkDCf-9.js +3681 -0
- package/dist/static/assets/ganttDiagram-JELNMOA3-bJkDCf-9.js.map +1 -0
- package/dist/static/assets/gitGraphDiagram-NY62KEGX-4QE9kesp.js +1206 -0
- package/dist/static/assets/gitGraphDiagram-NY62KEGX-4QE9kesp.js.map +1 -0
- package/dist/static/assets/graph-CS0Pmm7c.js +597 -0
- package/dist/static/assets/graph-CS0Pmm7c.js.map +1 -0
- package/dist/static/assets/index-BS6HnGzC.js +112303 -0
- package/dist/static/assets/index-BS6HnGzC.js.map +1 -0
- package/dist/static/assets/index-DaIsSzC_.css +483 -0
- package/dist/static/assets/infoDiagram-WHAUD3N6-ypBcKfUs.js +34 -0
- package/dist/static/assets/infoDiagram-WHAUD3N6-ypBcKfUs.js.map +1 -0
- package/dist/static/assets/init-ZxktEp_H.js +17 -0
- package/dist/static/assets/init-ZxktEp_H.js.map +1 -0
- package/dist/static/assets/journeyDiagram-XKPGCS4Q-QnrxDowJ.js +1255 -0
- package/dist/static/assets/journeyDiagram-XKPGCS4Q-QnrxDowJ.js.map +1 -0
- package/dist/static/assets/kanban-definition-3W4ZIXB7-CfvEc4z5.js +1048 -0
- package/dist/static/assets/kanban-definition-3W4ZIXB7-CfvEc4z5.js.map +1 -0
- package/dist/static/assets/layout-8TGxpm23.js +2218 -0
- package/dist/static/assets/layout-8TGxpm23.js.map +1 -0
- package/dist/static/assets/linear-BATBPQQv.js +341 -0
- package/dist/static/assets/linear-BATBPQQv.js.map +1 -0
- package/dist/static/assets/min-B3oVH3AC.js +42 -0
- package/dist/static/assets/min-B3oVH3AC.js.map +1 -0
- package/dist/static/assets/mindmap-definition-VGOIOE7T-L7VLwwF8.js +1127 -0
- package/dist/static/assets/mindmap-definition-VGOIOE7T-L7VLwwF8.js.map +1 -0
- package/dist/static/assets/ordinal-CxptdPJm.js +77 -0
- package/dist/static/assets/ordinal-CxptdPJm.js.map +1 -0
- package/dist/static/assets/pieDiagram-ADFJNKIX-CFW3zIhM.js +241 -0
- package/dist/static/assets/pieDiagram-ADFJNKIX-CFW3zIhM.js.map +1 -0
- package/dist/static/assets/quadrantDiagram-AYHSOK5B-B7ssen3E.js +1338 -0
- package/dist/static/assets/quadrantDiagram-AYHSOK5B-B7ssen3E.js.map +1 -0
- package/dist/static/assets/requirementDiagram-UZGBJVZJ-D0v5BArv.js +1162 -0
- package/dist/static/assets/requirementDiagram-UZGBJVZJ-D0v5BArv.js.map +1 -0
- package/dist/static/assets/sankeyDiagram-TZEHDZUN-B7slncJe.js +1195 -0
- package/dist/static/assets/sankeyDiagram-TZEHDZUN-B7slncJe.js.map +1 -0
- package/dist/static/assets/sequenceDiagram-WL72ISMW-oXU2lRh_.js +3875 -0
- package/dist/static/assets/sequenceDiagram-WL72ISMW-oXU2lRh_.js.map +1 -0
- package/dist/static/assets/stateDiagram-FKZM4ZOC-CFYsEd0x.js +452 -0
- package/dist/static/assets/stateDiagram-FKZM4ZOC-CFYsEd0x.js.map +1 -0
- package/dist/static/assets/stateDiagram-v2-4FDKWEC3-C0UWaNA7.js +22 -0
- package/dist/static/assets/stateDiagram-v2-4FDKWEC3-C0UWaNA7.js.map +1 -0
- package/dist/static/assets/timeline-definition-IT6M3QCI-C3KODUrh.js +1223 -0
- package/dist/static/assets/timeline-definition-IT6M3QCI-C3KODUrh.js.map +1 -0
- package/dist/static/assets/treemap-KMMF4GRG-DAGDLhj2.js +18753 -0
- package/dist/static/assets/treemap-KMMF4GRG-DAGDLhj2.js.map +1 -0
- package/dist/static/assets/xychartDiagram-PRI3JC2R-C0J9iwTO.js +1888 -0
- package/dist/static/assets/xychartDiagram-PRI3JC2R-C0J9iwTO.js.map +1 -0
- package/dist/static/index.html +71 -0
- package/dist/static/testing/dom-observation-utils.js +425 -0
- package/dist/static/testing/e2e-test-helper.js +3119 -0
- package/dist/team.js +1160 -0
- package/dist/tellask.js +431 -0
- package/dist/tool.js +150 -0
- package/dist/tools/apply-patch.js +542 -0
- package/dist/tools/builtins.js +196 -0
- package/dist/tools/context-health.js +177 -0
- package/dist/tools/ctrl.js +478 -0
- package/dist/tools/diag.js +583 -0
- package/dist/tools/env.js +184 -0
- package/dist/tools/fs.js +818 -0
- package/dist/tools/mcp.js +138 -0
- package/dist/tools/mem.js +349 -0
- package/dist/tools/os.js +751 -0
- package/dist/tools/prompts/team_mgmt.en.md +70 -0
- package/dist/tools/prompts/team_mgmt.zh.md +70 -0
- package/dist/tools/prompts/ws_mod.en.md +86 -0
- package/dist/tools/prompts/ws_mod.zh.md +87 -0
- package/dist/tools/registry-snapshot.js +31 -0
- package/dist/tools/registry.js +121 -0
- package/dist/tools/ripgrep.js +678 -0
- package/dist/tools/team-mgmt.js +3300 -0
- package/dist/tools/txt.js +3178 -0
- package/dist/utils/id.js +72 -0
- package/dist/utils/task-doc.js +236 -0
- package/dist/utils/task-package.js +522 -0
- package/dist/utils/taskdoc-search.js +280 -0
- package/dist/utils/taskdoc.js +400 -0
- package/package.json +69 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ripgrepSearchTool = exports.ripgrepFixedTool = exports.ripgrepSnippetsTool = exports.ripgrepCountTool = exports.ripgrepFilesTool = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Module: tools/ripgrep
|
|
9
|
+
*
|
|
10
|
+
* Workspace search tools backed by `rg` (ripgrep), with low-noise YAML output.
|
|
11
|
+
*/
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
function yamlQuote(value) {
|
|
16
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
17
|
+
}
|
|
18
|
+
function formatYamlCodeBlock(yaml) {
|
|
19
|
+
return `\`\`\`yaml\n${yaml}\n\`\`\``;
|
|
20
|
+
}
|
|
21
|
+
function yamlFlowStringArray(values) {
|
|
22
|
+
if (values.length === 0)
|
|
23
|
+
return '[]';
|
|
24
|
+
return `[${values.map(yamlQuote).join(', ')}]`;
|
|
25
|
+
}
|
|
26
|
+
function requireNonEmptyStringArg(args, key) {
|
|
27
|
+
const value = args[key];
|
|
28
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
29
|
+
throw new Error(`Invalid arguments: \`${key}\` must be a non-empty string`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
function optionalStringArg(args, key) {
|
|
34
|
+
const value = args[key];
|
|
35
|
+
if (value === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (typeof value !== 'string')
|
|
38
|
+
throw new Error(`Invalid arguments: \`${key}\` must be a string`);
|
|
39
|
+
const trimmed = value.trim();
|
|
40
|
+
if (trimmed === '')
|
|
41
|
+
return undefined;
|
|
42
|
+
return trimmed;
|
|
43
|
+
}
|
|
44
|
+
function optionalBooleanArg(args, key) {
|
|
45
|
+
const value = args[key];
|
|
46
|
+
if (value === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (typeof value !== 'boolean')
|
|
49
|
+
throw new Error(`Invalid arguments: \`${key}\` must be a boolean`);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
function optionalPositiveIntegerArg(args, key) {
|
|
53
|
+
const value = args[key];
|
|
54
|
+
if (value === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
57
|
+
throw new Error(`Invalid arguments: \`${key}\` must be an integer`);
|
|
58
|
+
}
|
|
59
|
+
if (value < 0)
|
|
60
|
+
throw new Error(`Invalid arguments: \`${key}\` must be >= 0`);
|
|
61
|
+
if (value === 0)
|
|
62
|
+
return undefined; // 0 is a sentinel for "default" under Codex required-all.
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
function optionalNonNegativeIntegerArg(args, key) {
|
|
66
|
+
const value = args[key];
|
|
67
|
+
if (value === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
70
|
+
throw new Error(`Invalid arguments: \`${key}\` must be an integer`);
|
|
71
|
+
}
|
|
72
|
+
if (value < 0)
|
|
73
|
+
throw new Error(`Invalid arguments: \`${key}\` must be >= 0`);
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
function optionalStringArrayArg(args, key) {
|
|
77
|
+
const value = args[key];
|
|
78
|
+
if (value === undefined)
|
|
79
|
+
return undefined;
|
|
80
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) {
|
|
81
|
+
throw new Error(`Invalid arguments: \`${key}\` must be an array of strings`);
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
function dirPatternToRipgrepGlob(pattern) {
|
|
86
|
+
const normalized = pattern.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '') || '.';
|
|
87
|
+
if (normalized === '.' || normalized === '')
|
|
88
|
+
return undefined;
|
|
89
|
+
if (normalized.includes('*'))
|
|
90
|
+
return normalized;
|
|
91
|
+
if (normalized.endsWith('/**'))
|
|
92
|
+
return normalized;
|
|
93
|
+
return `${normalized}/**`;
|
|
94
|
+
}
|
|
95
|
+
function buildAccessControlGlobs(member) {
|
|
96
|
+
const include = [];
|
|
97
|
+
const exclude = [];
|
|
98
|
+
const whitelist = member.read_dirs ?? [];
|
|
99
|
+
for (const pat of whitelist) {
|
|
100
|
+
const glob = dirPatternToRipgrepGlob(pat);
|
|
101
|
+
if (glob)
|
|
102
|
+
include.push(glob);
|
|
103
|
+
}
|
|
104
|
+
const blacklist = member.no_read_dirs ?? [];
|
|
105
|
+
for (const pat of blacklist) {
|
|
106
|
+
const glob = dirPatternToRipgrepGlob(pat);
|
|
107
|
+
if (glob)
|
|
108
|
+
exclude.push(glob);
|
|
109
|
+
}
|
|
110
|
+
// Task Docs are encapsulated and forbidden to all general file tools.
|
|
111
|
+
exclude.push('**/*.tsk');
|
|
112
|
+
exclude.push('**/*.tsk/**');
|
|
113
|
+
return { include, exclude };
|
|
114
|
+
}
|
|
115
|
+
function defaultBaseOptions() {
|
|
116
|
+
return {
|
|
117
|
+
globs: [],
|
|
118
|
+
caseMode: 'smart',
|
|
119
|
+
fixedStrings: false,
|
|
120
|
+
includeHidden: false,
|
|
121
|
+
followSymlinks: false,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function parseRipgrepCaseModeArg(args, defaultValue) {
|
|
125
|
+
const raw = optionalStringArg(args, 'case');
|
|
126
|
+
if (raw === undefined)
|
|
127
|
+
return defaultValue;
|
|
128
|
+
if (raw === 'smart' || raw === 'sensitive' || raw === 'insensitive')
|
|
129
|
+
return raw;
|
|
130
|
+
throw new Error("Invalid arguments: `case` must be one of: 'smart', 'sensitive', 'insensitive'");
|
|
131
|
+
}
|
|
132
|
+
function baseRgArgs(options, member) {
|
|
133
|
+
const args = ['--no-messages', '--color=never'];
|
|
134
|
+
if (options.includeHidden)
|
|
135
|
+
args.push('--hidden');
|
|
136
|
+
if (options.followSymlinks)
|
|
137
|
+
args.push('--follow');
|
|
138
|
+
if (options.caseMode === 'smart')
|
|
139
|
+
args.push('--smart-case');
|
|
140
|
+
if (options.caseMode === 'sensitive')
|
|
141
|
+
args.push('--case-sensitive');
|
|
142
|
+
if (options.caseMode === 'insensitive')
|
|
143
|
+
args.push('--ignore-case');
|
|
144
|
+
if (options.fixedStrings)
|
|
145
|
+
args.push('--fixed-strings');
|
|
146
|
+
const access = buildAccessControlGlobs(member);
|
|
147
|
+
for (const inc of access.include) {
|
|
148
|
+
args.push('--glob', inc);
|
|
149
|
+
}
|
|
150
|
+
for (const exc of access.exclude) {
|
|
151
|
+
args.push('--glob', `!${exc}`);
|
|
152
|
+
}
|
|
153
|
+
for (const glob of options.globs) {
|
|
154
|
+
args.push('--glob', glob);
|
|
155
|
+
}
|
|
156
|
+
return args;
|
|
157
|
+
}
|
|
158
|
+
async function runRgLines(args) {
|
|
159
|
+
return await new Promise((resolve, reject) => {
|
|
160
|
+
const child = (0, child_process_1.spawn)('rg', args, { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'pipe'] });
|
|
161
|
+
const stdoutChunks = [];
|
|
162
|
+
const stderrChunks = [];
|
|
163
|
+
child.stdout?.on('data', (d) => stdoutChunks.push(d));
|
|
164
|
+
child.stderr?.on('data', (d) => stderrChunks.push(d));
|
|
165
|
+
child.on('error', (err) => reject(err));
|
|
166
|
+
child.on('close', (_code) => {
|
|
167
|
+
const stdoutText = Buffer.concat(stdoutChunks).toString('utf8');
|
|
168
|
+
const stderrText = Buffer.concat(stderrChunks).toString('utf8');
|
|
169
|
+
const stdoutLines = stdoutText.split('\n').filter((l) => l !== '');
|
|
170
|
+
resolve({ stdoutLines, stderrText });
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async function loadFileLines(relPath) {
|
|
175
|
+
const abs = path_1.default.resolve(process.cwd(), relPath);
|
|
176
|
+
const text = await promises_1.default.readFile(abs, 'utf8');
|
|
177
|
+
const parts = text.split('\n');
|
|
178
|
+
if (parts.length > 0 && parts[parts.length - 1] === '')
|
|
179
|
+
parts.pop();
|
|
180
|
+
return parts;
|
|
181
|
+
}
|
|
182
|
+
async function runRipgrepFiles(caller, pattern, searchPath, options) {
|
|
183
|
+
const args = [...baseRgArgs(options, caller), '--files-with-matches', '--', pattern, searchPath];
|
|
184
|
+
try {
|
|
185
|
+
const { stdoutLines } = await runRgLines(args);
|
|
186
|
+
let totalFiles = 0;
|
|
187
|
+
const results = [];
|
|
188
|
+
for (const line of stdoutLines) {
|
|
189
|
+
totalFiles++;
|
|
190
|
+
if (results.length < options.maxFiles)
|
|
191
|
+
results.push({ path: line });
|
|
192
|
+
}
|
|
193
|
+
const truncated = totalFiles > options.maxFiles;
|
|
194
|
+
const summary = totalFiles === 0
|
|
195
|
+
? 'No matches.'
|
|
196
|
+
: truncated
|
|
197
|
+
? `Found ${totalFiles} files; showing first ${options.maxFiles} (truncated=true).`
|
|
198
|
+
: `Found ${totalFiles} files.`;
|
|
199
|
+
const yaml = [
|
|
200
|
+
`status: ok`,
|
|
201
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
202
|
+
`mode: files`,
|
|
203
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
204
|
+
...(options.globs.length > 0 ? [`globs: ${yamlFlowStringArray(options.globs)}`] : []),
|
|
205
|
+
`case: ${options.caseMode}`,
|
|
206
|
+
`fixed_strings: ${options.fixedStrings}`,
|
|
207
|
+
`regex: ${!options.fixedStrings}`,
|
|
208
|
+
`truncated: ${truncated}`,
|
|
209
|
+
`limits:`,
|
|
210
|
+
` max_files: ${options.maxFiles}`,
|
|
211
|
+
`totals:`,
|
|
212
|
+
` files_matched: ${totalFiles}`,
|
|
213
|
+
`summary: ${yamlQuote(summary)}`,
|
|
214
|
+
`results:`,
|
|
215
|
+
...results.map((r) => ` - path: ${yamlQuote(r.path)}`),
|
|
216
|
+
].join('\n');
|
|
217
|
+
return formatYamlCodeBlock(yaml);
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
return formatYamlCodeBlock([
|
|
221
|
+
`status: error`,
|
|
222
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
223
|
+
`mode: files`,
|
|
224
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
225
|
+
`error: FAILED`,
|
|
226
|
+
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
227
|
+
].join('\n'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
exports.ripgrepFilesTool = {
|
|
231
|
+
type: 'func',
|
|
232
|
+
name: 'ripgrep_files',
|
|
233
|
+
description: 'Search file paths containing a pattern (rg-backed).',
|
|
234
|
+
parameters: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
additionalProperties: false,
|
|
237
|
+
properties: {
|
|
238
|
+
pattern: { type: 'string' },
|
|
239
|
+
path: { type: 'string' },
|
|
240
|
+
globs: { type: 'array', items: { type: 'string' } },
|
|
241
|
+
case: { type: 'string' },
|
|
242
|
+
fixed_strings: { type: 'boolean' },
|
|
243
|
+
max_files: { type: 'integer' },
|
|
244
|
+
include_hidden: { type: 'boolean' },
|
|
245
|
+
follow_symlinks: { type: 'boolean' },
|
|
246
|
+
},
|
|
247
|
+
required: ['pattern'],
|
|
248
|
+
},
|
|
249
|
+
argsValidation: 'dominds',
|
|
250
|
+
call: async (_dlg, caller, args) => {
|
|
251
|
+
const pattern = requireNonEmptyStringArg(args, 'pattern');
|
|
252
|
+
const searchPath = optionalStringArg(args, 'path') ?? '.';
|
|
253
|
+
const globs = optionalStringArrayArg(args, 'globs') ?? [];
|
|
254
|
+
const caseMode = parseRipgrepCaseModeArg(args, 'smart');
|
|
255
|
+
const fixedStrings = optionalBooleanArg(args, 'fixed_strings') ?? false;
|
|
256
|
+
const includeHidden = optionalBooleanArg(args, 'include_hidden') ?? false;
|
|
257
|
+
const followSymlinks = optionalBooleanArg(args, 'follow_symlinks') ?? false;
|
|
258
|
+
const maxFiles = optionalPositiveIntegerArg(args, 'max_files') ?? 50;
|
|
259
|
+
const options = {
|
|
260
|
+
globs,
|
|
261
|
+
caseMode,
|
|
262
|
+
fixedStrings,
|
|
263
|
+
includeHidden,
|
|
264
|
+
followSymlinks,
|
|
265
|
+
maxFiles,
|
|
266
|
+
};
|
|
267
|
+
return await runRipgrepFiles(caller, pattern, searchPath, options);
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
async function runRipgrepCount(caller, pattern, searchPath, options) {
|
|
271
|
+
const args = [...baseRgArgs(options, caller), '--count-matches', '--', pattern, searchPath];
|
|
272
|
+
try {
|
|
273
|
+
const { stdoutLines } = await runRgLines(args);
|
|
274
|
+
let totalFiles = 0;
|
|
275
|
+
let totalMatches = 0;
|
|
276
|
+
const results = [];
|
|
277
|
+
for (const line of stdoutLines) {
|
|
278
|
+
const idx = line.lastIndexOf(':');
|
|
279
|
+
if (idx <= 0)
|
|
280
|
+
continue;
|
|
281
|
+
const p = line.slice(0, idx);
|
|
282
|
+
const rawCount = line.slice(idx + 1);
|
|
283
|
+
const count = Number.parseInt(rawCount, 10);
|
|
284
|
+
if (!Number.isFinite(count))
|
|
285
|
+
continue;
|
|
286
|
+
totalFiles++;
|
|
287
|
+
totalMatches += count;
|
|
288
|
+
if (results.length < options.maxFiles)
|
|
289
|
+
results.push({ path: p, count });
|
|
290
|
+
}
|
|
291
|
+
const truncated = totalFiles > options.maxFiles;
|
|
292
|
+
const summary = totalMatches === 0
|
|
293
|
+
? 'No matches.'
|
|
294
|
+
: truncated
|
|
295
|
+
? `Counted ${totalMatches} matches in ${totalFiles} files; showing first ${options.maxFiles} files (truncated=true).`
|
|
296
|
+
: `Counted ${totalMatches} matches in ${totalFiles} files.`;
|
|
297
|
+
const yaml = [
|
|
298
|
+
`status: ok`,
|
|
299
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
300
|
+
`mode: count`,
|
|
301
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
302
|
+
...(options.globs.length > 0 ? [`globs: ${yamlFlowStringArray(options.globs)}`] : []),
|
|
303
|
+
`case: ${options.caseMode}`,
|
|
304
|
+
`fixed_strings: ${options.fixedStrings}`,
|
|
305
|
+
`regex: ${!options.fixedStrings}`,
|
|
306
|
+
`truncated: ${truncated}`,
|
|
307
|
+
`limits:`,
|
|
308
|
+
` max_files: ${options.maxFiles}`,
|
|
309
|
+
`totals:`,
|
|
310
|
+
` files_matched: ${totalFiles}`,
|
|
311
|
+
` matches: ${totalMatches}`,
|
|
312
|
+
`summary: ${yamlQuote(summary)}`,
|
|
313
|
+
`results:`,
|
|
314
|
+
...results.map((r) => ` - path: ${yamlQuote(r.path)}\n count: ${r.count}`),
|
|
315
|
+
].join('\n');
|
|
316
|
+
return formatYamlCodeBlock(yaml);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
return formatYamlCodeBlock([
|
|
320
|
+
`status: error`,
|
|
321
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
322
|
+
`mode: count`,
|
|
323
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
324
|
+
`error: FAILED`,
|
|
325
|
+
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
326
|
+
].join('\n'));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
exports.ripgrepCountTool = {
|
|
330
|
+
type: 'func',
|
|
331
|
+
name: 'ripgrep_count',
|
|
332
|
+
description: 'Count matches per file (rg-backed).',
|
|
333
|
+
parameters: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
additionalProperties: false,
|
|
336
|
+
properties: {
|
|
337
|
+
pattern: { type: 'string' },
|
|
338
|
+
path: { type: 'string' },
|
|
339
|
+
globs: { type: 'array', items: { type: 'string' } },
|
|
340
|
+
case: { type: 'string' },
|
|
341
|
+
fixed_strings: { type: 'boolean' },
|
|
342
|
+
max_files: { type: 'integer' },
|
|
343
|
+
include_hidden: { type: 'boolean' },
|
|
344
|
+
follow_symlinks: { type: 'boolean' },
|
|
345
|
+
},
|
|
346
|
+
required: ['pattern'],
|
|
347
|
+
},
|
|
348
|
+
argsValidation: 'dominds',
|
|
349
|
+
call: async (_dlg, caller, args) => {
|
|
350
|
+
const pattern = requireNonEmptyStringArg(args, 'pattern');
|
|
351
|
+
const searchPath = optionalStringArg(args, 'path') ?? '.';
|
|
352
|
+
const globs = optionalStringArrayArg(args, 'globs') ?? [];
|
|
353
|
+
const caseMode = parseRipgrepCaseModeArg(args, 'smart');
|
|
354
|
+
const fixedStrings = optionalBooleanArg(args, 'fixed_strings') ?? false;
|
|
355
|
+
const includeHidden = optionalBooleanArg(args, 'include_hidden') ?? false;
|
|
356
|
+
const followSymlinks = optionalBooleanArg(args, 'follow_symlinks') ?? false;
|
|
357
|
+
const maxFiles = optionalPositiveIntegerArg(args, 'max_files') ?? 200;
|
|
358
|
+
const options = {
|
|
359
|
+
globs,
|
|
360
|
+
caseMode,
|
|
361
|
+
fixedStrings,
|
|
362
|
+
includeHidden,
|
|
363
|
+
followSymlinks,
|
|
364
|
+
maxFiles,
|
|
365
|
+
};
|
|
366
|
+
return await runRipgrepCount(caller, pattern, searchPath, options);
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
async function runRipgrepSnippets(caller, pattern, searchPath, options) {
|
|
370
|
+
const args = [...baseRgArgs(options, caller), '--vimgrep', '--', pattern, searchPath];
|
|
371
|
+
try {
|
|
372
|
+
const { stdoutLines } = await runRgLines(args);
|
|
373
|
+
let totalMatches = 0;
|
|
374
|
+
const fileSet = new Set();
|
|
375
|
+
const results = [];
|
|
376
|
+
const fileCache = new Map();
|
|
377
|
+
for (const line of stdoutLines) {
|
|
378
|
+
totalMatches++;
|
|
379
|
+
const first = line.indexOf(':');
|
|
380
|
+
if (first <= 0)
|
|
381
|
+
continue;
|
|
382
|
+
const second = line.indexOf(':', first + 1);
|
|
383
|
+
if (second <= first)
|
|
384
|
+
continue;
|
|
385
|
+
const third = line.indexOf(':', second + 1);
|
|
386
|
+
if (third <= second)
|
|
387
|
+
continue;
|
|
388
|
+
const filePath = line.slice(0, first);
|
|
389
|
+
const lineStr = line.slice(first + 1, second);
|
|
390
|
+
const colStr = line.slice(second + 1, third);
|
|
391
|
+
const text = line.slice(third + 1);
|
|
392
|
+
const ln = Number.parseInt(lineStr, 10);
|
|
393
|
+
const col = Number.parseInt(colStr, 10);
|
|
394
|
+
if (!Number.isFinite(ln) || !Number.isFinite(col))
|
|
395
|
+
continue;
|
|
396
|
+
fileSet.add(filePath);
|
|
397
|
+
if (results.length >= options.maxResults)
|
|
398
|
+
continue;
|
|
399
|
+
let lines = fileCache.get(filePath);
|
|
400
|
+
if (!lines) {
|
|
401
|
+
lines = await loadFileLines(filePath).catch(() => []);
|
|
402
|
+
fileCache.set(filePath, lines);
|
|
403
|
+
}
|
|
404
|
+
const idx0 = ln - 1;
|
|
405
|
+
const before = lines.slice(Math.max(0, idx0 - options.contextBefore), idx0);
|
|
406
|
+
const after = lines.slice(idx0 + 1, idx0 + 1 + options.contextAfter);
|
|
407
|
+
results.push({ path: filePath, line: ln, col, match: text, before, after });
|
|
408
|
+
}
|
|
409
|
+
const truncated = totalMatches > options.maxResults;
|
|
410
|
+
const summary = totalMatches === 0
|
|
411
|
+
? 'No matches.'
|
|
412
|
+
: truncated
|
|
413
|
+
? `Showing first ${options.maxResults} of ${totalMatches} matches (truncated=true).`
|
|
414
|
+
: `Found ${totalMatches} matches.`;
|
|
415
|
+
const yaml = [
|
|
416
|
+
`status: ok`,
|
|
417
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
418
|
+
`mode: snippets`,
|
|
419
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
420
|
+
...(options.globs.length > 0 ? [`globs: ${yamlFlowStringArray(options.globs)}`] : []),
|
|
421
|
+
`case: ${options.caseMode}`,
|
|
422
|
+
`fixed_strings: ${options.fixedStrings}`,
|
|
423
|
+
`regex: ${!options.fixedStrings}`,
|
|
424
|
+
`truncated: ${truncated}`,
|
|
425
|
+
`limits:`,
|
|
426
|
+
` max_results: ${options.maxResults}`,
|
|
427
|
+
`totals:`,
|
|
428
|
+
` files_matched: ${fileSet.size}`,
|
|
429
|
+
` matches: ${totalMatches}`,
|
|
430
|
+
`summary: ${yamlQuote(summary)}`,
|
|
431
|
+
`results:`,
|
|
432
|
+
...results.map((r) => [
|
|
433
|
+
` - path: ${yamlQuote(r.path)}`,
|
|
434
|
+
` line: ${r.line}`,
|
|
435
|
+
` col: ${r.col}`,
|
|
436
|
+
` match: ${yamlQuote(r.match)}`,
|
|
437
|
+
` before: ${yamlFlowStringArray(r.before)}`,
|
|
438
|
+
` after: ${yamlFlowStringArray(r.after)}`,
|
|
439
|
+
].join('\n')),
|
|
440
|
+
].join('\n');
|
|
441
|
+
return formatYamlCodeBlock(yaml);
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
return formatYamlCodeBlock([
|
|
445
|
+
`status: error`,
|
|
446
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
447
|
+
`mode: snippets`,
|
|
448
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
449
|
+
`error: FAILED`,
|
|
450
|
+
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
451
|
+
].join('\n'));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
exports.ripgrepSnippetsTool = {
|
|
455
|
+
type: 'func',
|
|
456
|
+
name: 'ripgrep_snippets',
|
|
457
|
+
description: 'Search snippets with line/col (rg-backed).',
|
|
458
|
+
parameters: {
|
|
459
|
+
type: 'object',
|
|
460
|
+
additionalProperties: false,
|
|
461
|
+
properties: {
|
|
462
|
+
pattern: { type: 'string' },
|
|
463
|
+
path: { type: 'string' },
|
|
464
|
+
globs: { type: 'array', items: { type: 'string' } },
|
|
465
|
+
case: { type: 'string' },
|
|
466
|
+
fixed_strings: { type: 'boolean' },
|
|
467
|
+
context_before: { type: 'integer' },
|
|
468
|
+
context_after: { type: 'integer' },
|
|
469
|
+
max_results: { type: 'integer' },
|
|
470
|
+
include_hidden: { type: 'boolean' },
|
|
471
|
+
follow_symlinks: { type: 'boolean' },
|
|
472
|
+
},
|
|
473
|
+
required: ['pattern'],
|
|
474
|
+
},
|
|
475
|
+
argsValidation: 'dominds',
|
|
476
|
+
call: async (_dlg, caller, args) => {
|
|
477
|
+
const pattern = requireNonEmptyStringArg(args, 'pattern');
|
|
478
|
+
const searchPath = optionalStringArg(args, 'path') ?? '.';
|
|
479
|
+
const globs = optionalStringArrayArg(args, 'globs') ?? [];
|
|
480
|
+
const caseMode = parseRipgrepCaseModeArg(args, 'smart');
|
|
481
|
+
const fixedStrings = optionalBooleanArg(args, 'fixed_strings') ?? false;
|
|
482
|
+
const includeHidden = optionalBooleanArg(args, 'include_hidden') ?? false;
|
|
483
|
+
const followSymlinks = optionalBooleanArg(args, 'follow_symlinks') ?? false;
|
|
484
|
+
const maxResults = optionalPositiveIntegerArg(args, 'max_results') ?? 50;
|
|
485
|
+
const contextBefore = optionalNonNegativeIntegerArg(args, 'context_before') ?? 1;
|
|
486
|
+
const contextAfter = optionalNonNegativeIntegerArg(args, 'context_after') ?? 1;
|
|
487
|
+
const options = {
|
|
488
|
+
globs,
|
|
489
|
+
caseMode,
|
|
490
|
+
fixedStrings,
|
|
491
|
+
includeHidden,
|
|
492
|
+
followSymlinks,
|
|
493
|
+
maxResults,
|
|
494
|
+
contextBefore,
|
|
495
|
+
contextAfter,
|
|
496
|
+
};
|
|
497
|
+
return await runRipgrepSnippets(caller, pattern, searchPath, options);
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
exports.ripgrepFixedTool = {
|
|
501
|
+
type: 'func',
|
|
502
|
+
name: 'ripgrep_fixed',
|
|
503
|
+
description: 'Fixed-string ripgrep convenience tool (routes to ripgrep_files/ripgrep_snippets/ripgrep_count with fixed_strings=true).',
|
|
504
|
+
parameters: {
|
|
505
|
+
type: 'object',
|
|
506
|
+
additionalProperties: false,
|
|
507
|
+
properties: {
|
|
508
|
+
literal: { type: 'string' },
|
|
509
|
+
path: { type: 'string' },
|
|
510
|
+
mode: { type: 'string' },
|
|
511
|
+
globs: { type: 'array', items: { type: 'string' } },
|
|
512
|
+
case: { type: 'string' },
|
|
513
|
+
max_files: { type: 'integer' },
|
|
514
|
+
max_results: { type: 'integer' },
|
|
515
|
+
context_before: { type: 'integer' },
|
|
516
|
+
context_after: { type: 'integer' },
|
|
517
|
+
include_hidden: { type: 'boolean' },
|
|
518
|
+
follow_symlinks: { type: 'boolean' },
|
|
519
|
+
},
|
|
520
|
+
required: ['literal'],
|
|
521
|
+
},
|
|
522
|
+
argsValidation: 'dominds',
|
|
523
|
+
call: async (_dlg, caller, args) => {
|
|
524
|
+
const literal = requireNonEmptyStringArg(args, 'literal');
|
|
525
|
+
const searchPath = optionalStringArg(args, 'path') ?? '.';
|
|
526
|
+
const modeRaw = optionalStringArg(args, 'mode') ?? 'snippets';
|
|
527
|
+
if (modeRaw !== 'files' && modeRaw !== 'snippets' && modeRaw !== 'count') {
|
|
528
|
+
throw new Error("Invalid arguments: `mode` must be one of: 'files', 'snippets', 'count'");
|
|
529
|
+
}
|
|
530
|
+
const globs = optionalStringArrayArg(args, 'globs') ?? [];
|
|
531
|
+
const caseMode = parseRipgrepCaseModeArg(args, 'smart');
|
|
532
|
+
const includeHidden = optionalBooleanArg(args, 'include_hidden') ?? false;
|
|
533
|
+
const followSymlinks = optionalBooleanArg(args, 'follow_symlinks') ?? false;
|
|
534
|
+
const base = {
|
|
535
|
+
globs,
|
|
536
|
+
caseMode,
|
|
537
|
+
fixedStrings: true,
|
|
538
|
+
includeHidden,
|
|
539
|
+
followSymlinks,
|
|
540
|
+
};
|
|
541
|
+
if (modeRaw === 'files') {
|
|
542
|
+
const maxFiles = optionalPositiveIntegerArg(args, 'max_files') ?? 50;
|
|
543
|
+
const options = { ...base, maxFiles };
|
|
544
|
+
return await runRipgrepFiles(caller, literal, searchPath, options);
|
|
545
|
+
}
|
|
546
|
+
if (modeRaw === 'count') {
|
|
547
|
+
const maxFiles = optionalPositiveIntegerArg(args, 'max_files') ?? 200;
|
|
548
|
+
const options = { ...base, maxFiles };
|
|
549
|
+
return await runRipgrepCount(caller, literal, searchPath, options);
|
|
550
|
+
}
|
|
551
|
+
const maxResults = optionalPositiveIntegerArg(args, 'max_results') ?? 50;
|
|
552
|
+
const contextBefore = optionalNonNegativeIntegerArg(args, 'context_before') ?? 1;
|
|
553
|
+
const contextAfter = optionalNonNegativeIntegerArg(args, 'context_after') ?? 1;
|
|
554
|
+
const options = { ...base, maxResults, contextBefore, contextAfter };
|
|
555
|
+
return await runRipgrepSnippets(caller, literal, searchPath, options);
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
const DISALLOWED_RG_ARGS = new Set(['--pre', '--pre-glob']);
|
|
559
|
+
async function runRipgrepSearch(caller, pattern, searchPath, rawRgArgs) {
|
|
560
|
+
for (const tok of rawRgArgs) {
|
|
561
|
+
if (DISALLOWED_RG_ARGS.has(tok)) {
|
|
562
|
+
return formatYamlCodeBlock(`status: error\nerror: DISALLOWED_ARG\nsummary: ${yamlQuote(`Disallowed rg arg: ${tok}`)}`);
|
|
563
|
+
}
|
|
564
|
+
if (tok === '--json' ||
|
|
565
|
+
tok === '--count' ||
|
|
566
|
+
tok === '--count-matches' ||
|
|
567
|
+
tok === '--files-with-matches' ||
|
|
568
|
+
tok === '--files') {
|
|
569
|
+
return formatYamlCodeBlock(`status: error\nerror: DISALLOWED_ARG\nsummary: ${yamlQuote(`Disallowed rg output arg: ${tok}`)}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const options = {
|
|
573
|
+
...defaultBaseOptions(),
|
|
574
|
+
fixedStrings: false,
|
|
575
|
+
maxResults: 50,
|
|
576
|
+
contextBefore: 1,
|
|
577
|
+
contextAfter: 1,
|
|
578
|
+
};
|
|
579
|
+
const rgArgs = [
|
|
580
|
+
...baseRgArgs(options, caller),
|
|
581
|
+
...rawRgArgs,
|
|
582
|
+
'--vimgrep',
|
|
583
|
+
'--',
|
|
584
|
+
pattern,
|
|
585
|
+
searchPath,
|
|
586
|
+
];
|
|
587
|
+
try {
|
|
588
|
+
const { stdoutLines } = await runRgLines(rgArgs);
|
|
589
|
+
let totalMatches = 0;
|
|
590
|
+
const fileSet = new Set();
|
|
591
|
+
const results = [];
|
|
592
|
+
for (const line of stdoutLines) {
|
|
593
|
+
totalMatches++;
|
|
594
|
+
const first = line.indexOf(':');
|
|
595
|
+
if (first <= 0)
|
|
596
|
+
continue;
|
|
597
|
+
const secondColon = line.indexOf(':', first + 1);
|
|
598
|
+
if (secondColon <= first)
|
|
599
|
+
continue;
|
|
600
|
+
const thirdColon = line.indexOf(':', secondColon + 1);
|
|
601
|
+
if (thirdColon <= secondColon)
|
|
602
|
+
continue;
|
|
603
|
+
const filePath = line.slice(0, first);
|
|
604
|
+
const lineStr = line.slice(first + 1, secondColon);
|
|
605
|
+
const colStr = line.slice(secondColon + 1, thirdColon);
|
|
606
|
+
const text = line.slice(thirdColon + 1);
|
|
607
|
+
const ln = Number.parseInt(lineStr, 10);
|
|
608
|
+
const col = Number.parseInt(colStr, 10);
|
|
609
|
+
if (!Number.isFinite(ln) || !Number.isFinite(col))
|
|
610
|
+
continue;
|
|
611
|
+
fileSet.add(filePath);
|
|
612
|
+
if (results.length < options.maxResults)
|
|
613
|
+
results.push({ path: filePath, line: ln, col, match: text });
|
|
614
|
+
}
|
|
615
|
+
const truncated = totalMatches > options.maxResults;
|
|
616
|
+
const summary = totalMatches === 0
|
|
617
|
+
? 'No matches.'
|
|
618
|
+
: truncated
|
|
619
|
+
? `Showing first ${options.maxResults} of ${totalMatches} matches (truncated=true).`
|
|
620
|
+
: `Found ${totalMatches} matches.`;
|
|
621
|
+
const yaml = [
|
|
622
|
+
`status: ok`,
|
|
623
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
624
|
+
`mode: snippets`,
|
|
625
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
626
|
+
`case: smart`,
|
|
627
|
+
`fixed_strings: false`,
|
|
628
|
+
`regex: true`,
|
|
629
|
+
`truncated: ${truncated}`,
|
|
630
|
+
`limits:`,
|
|
631
|
+
` max_results: ${options.maxResults}`,
|
|
632
|
+
`totals:`,
|
|
633
|
+
` files_matched: ${fileSet.size}`,
|
|
634
|
+
` matches: ${totalMatches}`,
|
|
635
|
+
`summary: ${yamlQuote(summary)}`,
|
|
636
|
+
`results:`,
|
|
637
|
+
...results.map((r) => [
|
|
638
|
+
` - path: ${yamlQuote(r.path)}`,
|
|
639
|
+
` line: ${r.line}`,
|
|
640
|
+
` col: ${r.col}`,
|
|
641
|
+
` match: ${yamlQuote(r.match)}`,
|
|
642
|
+
].join('\n')),
|
|
643
|
+
].join('\n');
|
|
644
|
+
return formatYamlCodeBlock(yaml);
|
|
645
|
+
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
return formatYamlCodeBlock([
|
|
648
|
+
`status: error`,
|
|
649
|
+
`pattern: ${yamlQuote(pattern)}`,
|
|
650
|
+
`mode: snippets`,
|
|
651
|
+
`path: ${yamlQuote(searchPath)}`,
|
|
652
|
+
`error: FAILED`,
|
|
653
|
+
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
654
|
+
].join('\n'));
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
exports.ripgrepSearchTool = {
|
|
658
|
+
type: 'func',
|
|
659
|
+
name: 'ripgrep_search',
|
|
660
|
+
description: 'Escape hatch: run rg-style search (snippets output, with a limited allowlist). Output is normalized to YAML snippets mode.',
|
|
661
|
+
parameters: {
|
|
662
|
+
type: 'object',
|
|
663
|
+
additionalProperties: false,
|
|
664
|
+
properties: {
|
|
665
|
+
pattern: { type: 'string' },
|
|
666
|
+
path: { type: 'string' },
|
|
667
|
+
rg_args: { type: 'array', items: { type: 'string' } },
|
|
668
|
+
},
|
|
669
|
+
required: ['pattern'],
|
|
670
|
+
},
|
|
671
|
+
argsValidation: 'dominds',
|
|
672
|
+
call: async (_dlg, caller, args) => {
|
|
673
|
+
const pattern = requireNonEmptyStringArg(args, 'pattern');
|
|
674
|
+
const searchPath = optionalStringArg(args, 'path') ?? '.';
|
|
675
|
+
const rgArgs = optionalStringArrayArg(args, 'rg_args') ?? [];
|
|
676
|
+
return await runRipgrepSearch(caller, pattern, searchPath, rgArgs);
|
|
677
|
+
},
|
|
678
|
+
};
|