@npm-ken/pi-tool-discovery 1.0.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/AGENTS.md.pause +9 -0
- package/README.md +10 -0
- package/bun.lock +299 -0
- package/docs/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +45 -0
- package/package.json +21 -0
- package/src/config.ts +60 -0
- package/src/core.ts +64 -0
- package/src/discoverable-tools.ts +59 -0
- package/src/index.ts +95 -0
- package/test/config.test.ts +52 -0
- package/test/core.test.ts +48 -0
- package/test/discoverable-tools.test.ts +40 -0
- package/test/extension.test.ts +100 -0
- package/tsconfig.json +12 -0
package/AGENTS.md.pause
ADDED
package/README.md
ADDED
package/bun.lock
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 2,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "pi-tool-discovery",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@earendil-works/pi-coding-agent": "^0.84.3",
|
|
9
|
+
"@iarna/toml": "^2.2.5",
|
|
10
|
+
"typebox": "^1.3.7",
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@types/bun": "latest",
|
|
14
|
+
"typescript": "^5.9.3",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
"packages": {
|
|
19
|
+
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.91.1", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw=="],
|
|
20
|
+
|
|
21
|
+
"@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
|
|
22
|
+
|
|
23
|
+
"@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
|
|
24
|
+
|
|
25
|
+
"@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="],
|
|
26
|
+
|
|
27
|
+
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
|
|
28
|
+
|
|
29
|
+
"@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/eventstream-handler-node": "^3.972.16", "@aws-sdk/middleware-eventstream": "^3.972.12", "@aws-sdk/middleware-websocket": "^3.972.19", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ=="],
|
|
30
|
+
|
|
31
|
+
"@aws-sdk/core": ["@aws-sdk/core@3.977.9", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@aws-sdk/xml-builder": "^3.972.40", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.33.3", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA=="],
|
|
32
|
+
|
|
33
|
+
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.70", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw=="],
|
|
34
|
+
|
|
35
|
+
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.72", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/node-http-handler": "^4.11.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w=="],
|
|
36
|
+
|
|
37
|
+
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.15", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/credential-provider-env": "^3.972.70", "@aws-sdk/credential-provider-http": "^3.972.72", "@aws-sdk/credential-provider-login": "^3.972.77", "@aws-sdk/credential-provider-process": "^3.972.70", "@aws-sdk/credential-provider-sso": "^3.973.14", "@aws-sdk/credential-provider-web-identity": "^3.972.76", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg=="],
|
|
38
|
+
|
|
39
|
+
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.77", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ=="],
|
|
40
|
+
|
|
41
|
+
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.81", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.70", "@aws-sdk/credential-provider-http": "^3.972.72", "@aws-sdk/credential-provider-ini": "^3.973.15", "@aws-sdk/credential-provider-process": "^3.972.70", "@aws-sdk/credential-provider-sso": "^3.973.14", "@aws-sdk/credential-provider-web-identity": "^3.972.76", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Rml+WitoFvXmv6JZ18U/xGdGDGGvB/mOin0ya0lTnTrdC0Z1lrVxTYh7iNklZBcvcRMrs4DoEf6xy1KWyrLQQw=="],
|
|
42
|
+
|
|
43
|
+
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.70", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q=="],
|
|
44
|
+
|
|
45
|
+
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.14", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/token-providers": "3.1116.0", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA=="],
|
|
46
|
+
|
|
47
|
+
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.76", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA=="],
|
|
48
|
+
|
|
49
|
+
"@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.972.34", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-cTeVzpu1xEAkryTZBYhGwnQ6gOGyp8ZYZvmn0Sg/nI/ABmy/CRHHxPDJDUi9PxwxUtGGaatvfRUB3FCgT/rSWw=="],
|
|
50
|
+
|
|
51
|
+
"@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.972.29", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-dlRzHCgyB8W6hLuDC5pcT5q+ziPt00n4QGgGBE17ucLVU4zMa6lsbuUdQ2Pm75Z5VA8GF+R/+SgrRcaTdIzSIQ=="],
|
|
52
|
+
|
|
53
|
+
"@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.972.52", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-vsPPM+nMbKJlUCFU+eoGZbdxdxDIAX9LbpjSXaR5Ufpmqgp8TdYQnoExhLu4T3umW/JIIPny1ydbhWidZZYokQ=="],
|
|
54
|
+
|
|
55
|
+
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.44", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/signature-v4-multi-region": "^3.996.46", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/node-http-handler": "^4.11.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw=="],
|
|
56
|
+
|
|
57
|
+
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.46", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ=="],
|
|
58
|
+
|
|
59
|
+
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="],
|
|
60
|
+
|
|
61
|
+
"@aws-sdk/types": ["@aws-sdk/types@3.974.5", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ=="],
|
|
62
|
+
|
|
63
|
+
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.10", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ycwH6Zd2GhuSqdXX9ihbCjeGTB6xOJs+O3+Jb8/zDG9978XU80qs75dfkPJRMNKe5MvBZPuNeFpd4JZKPoUF4g=="],
|
|
64
|
+
|
|
65
|
+
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.40", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA=="],
|
|
66
|
+
|
|
67
|
+
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="],
|
|
68
|
+
|
|
69
|
+
"@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="],
|
|
70
|
+
|
|
71
|
+
"@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@0.84.3", "", { "dependencies": { "@earendil-works/pi-ai": "^0.84.3", "@earendil-works/pi-telemetry": "^0.84.3", "diff": "8.0.4", "ignore": "7.0.5", "typebox": "1.3.7", "yaml": "2.9.0" } }, "sha512-VURr+xBRl3RxYcw3kT9Pn3yfi6LbRoCJgHF7h1mAblMjtLNV/MfG/RyF0uJizBAM886AEakSiw3j9c/aSngppg=="],
|
|
72
|
+
|
|
73
|
+
"@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.84.3", "", { "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@earendil-works/pi-telemetry": "^0.84.3", "@google/genai": "1.52.0", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", "openai": "6.40.0", "partial-json": "0.1.7", "typebox": "1.3.7" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-M0YUV8vNO3y2WwWSyY8ijKJV5W4gkSUixuvk+Z00ZBjsyMfsdXfITsHEwP1UIf09YRWXT6oGn0GlCamt+P32XQ=="],
|
|
74
|
+
|
|
75
|
+
"@earendil-works/pi-client": ["@earendil-works/pi-client@0.84.3", "", { "dependencies": { "@earendil-works/pi-protocol": "^0.84.3" } }, "sha512-zfErYane+390W0xpBJ/FWCp6aktPpkpcIcXUeZiAziWLoxE80ZNQALRyOSa/gGS5V+1OkNnMYxRxbzN0zUvnOA=="],
|
|
76
|
+
|
|
77
|
+
"@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@0.84.3", "", { "dependencies": { "@earendil-works/pi-agent-core": "^0.84.3", "@earendil-works/pi-ai": "^0.84.3", "@earendil-works/pi-client": "^0.84.3", "@earendil-works/pi-protocol": "^0.84.3", "@earendil-works/pi-tui": "^0.84.3", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", "diff": "8.0.4", "grok-mermaid": "0.2.2", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.5", "jiti": "2.7.0", "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", "typebox": "1.3.7", "undici": "8.9.0", "yaml": "2.9.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "pi": "dist/bundle/cli.js" } }, "sha512-Yr2p9PubrbFZmYEPYI+C8KmZP9xlFuLDnAG64RtU0ZDgrdiXYWa+y7WGyJO5OlqPliOkVCMd9IzVszO3/t0D0w=="],
|
|
78
|
+
|
|
79
|
+
"@earendil-works/pi-protocol": ["@earendil-works/pi-protocol@0.84.3", "", { "dependencies": { "typebox": "1.3.7" } }, "sha512-9a4g6WhLOvRqvsIOFaWxg/2gdrbY4Thclwj5ipLUPAWChfsDJ/8XdPc2sRhSOkD6EsxpEFJz3xppcfwI6EcZDg=="],
|
|
80
|
+
|
|
81
|
+
"@earendil-works/pi-telemetry": ["@earendil-works/pi-telemetry@0.84.3", "", {}, "sha512-sgEkWoKrvSGaKn+YfLLFZmn+/A7B/w62eLwTD57nI+C9to8ITlFFVbgC2OtwvPnT3NFGHdCd53qhBEMIlptD1g=="],
|
|
82
|
+
|
|
83
|
+
"@earendil-works/pi-tui": ["@earendil-works/pi-tui@0.84.3", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "18.0.5" } }, "sha512-fS6OEQKEEALnKa6Uw8LcgZZ+9CWck7f3MQSCETQp6leUgIFwMEDtKmOUnL9nsYm+RIPmy7OmplVxYRbV6hiaFg=="],
|
|
84
|
+
|
|
85
|
+
"@google/genai": ["@google/genai@1.52.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q=="],
|
|
86
|
+
|
|
87
|
+
"@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="],
|
|
88
|
+
|
|
89
|
+
"@mariozechner/clipboard": ["@mariozechner/clipboard@0.3.9", "", { "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.9", "@mariozechner/clipboard-darwin-universal": "0.3.9", "@mariozechner/clipboard-darwin-x64": "0.3.9", "@mariozechner/clipboard-linux-arm64-gnu": "0.3.9", "@mariozechner/clipboard-linux-arm64-musl": "0.3.9", "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-musl": "0.3.9", "@mariozechner/clipboard-win32-arm64-msvc": "0.3.9", "@mariozechner/clipboard-win32-x64-msvc": "0.3.9" } }, "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA=="],
|
|
90
|
+
|
|
91
|
+
"@mariozechner/clipboard-darwin-arm64": ["@mariozechner/clipboard-darwin-arm64@0.3.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ=="],
|
|
92
|
+
|
|
93
|
+
"@mariozechner/clipboard-darwin-universal": ["@mariozechner/clipboard-darwin-universal@0.3.9", "", { "os": "darwin" }, "sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ=="],
|
|
94
|
+
|
|
95
|
+
"@mariozechner/clipboard-darwin-x64": ["@mariozechner/clipboard-darwin-x64@0.3.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg=="],
|
|
96
|
+
|
|
97
|
+
"@mariozechner/clipboard-linux-arm64-gnu": ["@mariozechner/clipboard-linux-arm64-gnu@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw=="],
|
|
98
|
+
|
|
99
|
+
"@mariozechner/clipboard-linux-arm64-musl": ["@mariozechner/clipboard-linux-arm64-musl@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ=="],
|
|
100
|
+
|
|
101
|
+
"@mariozechner/clipboard-linux-riscv64-gnu": ["@mariozechner/clipboard-linux-riscv64-gnu@0.3.9", "", { "os": "linux", "cpu": "none" }, "sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw=="],
|
|
102
|
+
|
|
103
|
+
"@mariozechner/clipboard-linux-x64-gnu": ["@mariozechner/clipboard-linux-x64-gnu@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw=="],
|
|
104
|
+
|
|
105
|
+
"@mariozechner/clipboard-linux-x64-musl": ["@mariozechner/clipboard-linux-x64-musl@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ=="],
|
|
106
|
+
|
|
107
|
+
"@mariozechner/clipboard-win32-arm64-msvc": ["@mariozechner/clipboard-win32-arm64-msvc@0.3.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ=="],
|
|
108
|
+
|
|
109
|
+
"@mariozechner/clipboard-win32-x64-msvc": ["@mariozechner/clipboard-win32-x64-msvc@0.3.9", "", { "os": "win32", "cpu": "x64" }, "sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA=="],
|
|
110
|
+
|
|
111
|
+
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
|
112
|
+
|
|
113
|
+
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
|
|
114
|
+
|
|
115
|
+
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.5", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="],
|
|
116
|
+
|
|
117
|
+
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.1", "", {}, "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg=="],
|
|
118
|
+
|
|
119
|
+
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1" } }, "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw=="],
|
|
120
|
+
|
|
121
|
+
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
|
|
122
|
+
|
|
123
|
+
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
|
|
124
|
+
|
|
125
|
+
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
|
126
|
+
|
|
127
|
+
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.2", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="],
|
|
128
|
+
|
|
129
|
+
"@silvia-odwyer/photon-node": ["@silvia-odwyer/photon-node@0.3.4", "", {}, "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA=="],
|
|
130
|
+
|
|
131
|
+
"@smithy/core": ["@smithy/core@3.33.3", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg=="],
|
|
132
|
+
|
|
133
|
+
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg=="],
|
|
134
|
+
|
|
135
|
+
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.7.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw=="],
|
|
136
|
+
|
|
137
|
+
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
|
138
|
+
|
|
139
|
+
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="],
|
|
140
|
+
|
|
141
|
+
"@smithy/signature-v4": ["@smithy/signature-v4@5.7.3", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow=="],
|
|
142
|
+
|
|
143
|
+
"@smithy/types": ["@smithy/types@4.17.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA=="],
|
|
144
|
+
|
|
145
|
+
"@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
|
146
|
+
|
|
147
|
+
"@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
|
148
|
+
|
|
149
|
+
"@types/bun": ["@types/bun@1.4.0", "", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="],
|
|
150
|
+
|
|
151
|
+
"@types/node": ["@types/node@26.3.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw=="],
|
|
152
|
+
|
|
153
|
+
"@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
|
|
154
|
+
|
|
155
|
+
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
|
156
|
+
|
|
157
|
+
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
|
158
|
+
|
|
159
|
+
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
|
160
|
+
|
|
161
|
+
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
|
162
|
+
|
|
163
|
+
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
|
|
164
|
+
|
|
165
|
+
"brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="],
|
|
166
|
+
|
|
167
|
+
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
|
|
168
|
+
|
|
169
|
+
"bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="],
|
|
170
|
+
|
|
171
|
+
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
|
172
|
+
|
|
173
|
+
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
|
174
|
+
|
|
175
|
+
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
|
176
|
+
|
|
177
|
+
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
178
|
+
|
|
179
|
+
"diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
|
|
180
|
+
|
|
181
|
+
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
|
182
|
+
|
|
183
|
+
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
|
184
|
+
|
|
185
|
+
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
|
186
|
+
|
|
187
|
+
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
|
188
|
+
|
|
189
|
+
"gaxios": ["gaxios@7.3.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ=="],
|
|
190
|
+
|
|
191
|
+
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
|
192
|
+
|
|
193
|
+
"get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
|
|
194
|
+
|
|
195
|
+
"google-auth-library": ["google-auth-library@10.9.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw=="],
|
|
196
|
+
|
|
197
|
+
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
|
198
|
+
|
|
199
|
+
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
200
|
+
|
|
201
|
+
"grok-mermaid": ["grok-mermaid@0.2.2", "", {}, "sha512-XcJEP5dDC8liHBh52mlLjU18fNvu1ckFsu0QpIG3+APZ270fsj9wxpiA6cOURmbUEuoMVgjbC2+UYgTdCqqgzA=="],
|
|
202
|
+
|
|
203
|
+
"highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="],
|
|
204
|
+
|
|
205
|
+
"hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="],
|
|
206
|
+
|
|
207
|
+
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
|
208
|
+
|
|
209
|
+
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
|
210
|
+
|
|
211
|
+
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
|
212
|
+
|
|
213
|
+
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
|
214
|
+
|
|
215
|
+
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
|
|
216
|
+
|
|
217
|
+
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
|
218
|
+
|
|
219
|
+
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
|
|
220
|
+
|
|
221
|
+
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
|
|
222
|
+
|
|
223
|
+
"jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
|
|
224
|
+
|
|
225
|
+
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
|
226
|
+
|
|
227
|
+
"lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="],
|
|
228
|
+
|
|
229
|
+
"marked": ["marked@18.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="],
|
|
230
|
+
|
|
231
|
+
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
|
232
|
+
|
|
233
|
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
234
|
+
|
|
235
|
+
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
|
236
|
+
|
|
237
|
+
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
|
238
|
+
|
|
239
|
+
"openai": ["openai@6.40.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"] }, "sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA=="],
|
|
240
|
+
|
|
241
|
+
"p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
|
|
242
|
+
|
|
243
|
+
"partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="],
|
|
244
|
+
|
|
245
|
+
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
|
246
|
+
|
|
247
|
+
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
|
|
248
|
+
|
|
249
|
+
"protobufjs": ["protobufjs@7.6.5", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw=="],
|
|
250
|
+
|
|
251
|
+
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
|
|
252
|
+
|
|
253
|
+
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
|
254
|
+
|
|
255
|
+
"semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
|
|
256
|
+
|
|
257
|
+
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
|
258
|
+
|
|
259
|
+
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
|
260
|
+
|
|
261
|
+
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
|
262
|
+
|
|
263
|
+
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
|
264
|
+
|
|
265
|
+
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
266
|
+
|
|
267
|
+
"typebox": ["typebox@1.3.19", "", {}, "sha512-OGhGi3JkTMol9QW8Bc6tgSD4ghV8v5WubAi1XeYnlxdocHvgS7TQJ8FcFZrQhVhigQU0z7bRFAEsZ+wpkRdCfg=="],
|
|
268
|
+
|
|
269
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
270
|
+
|
|
271
|
+
"undici": ["undici@8.9.0", "", {}, "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA=="],
|
|
272
|
+
|
|
273
|
+
"undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
|
|
274
|
+
|
|
275
|
+
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
|
276
|
+
|
|
277
|
+
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
|
278
|
+
|
|
279
|
+
"ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="],
|
|
280
|
+
|
|
281
|
+
"yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
|
|
282
|
+
|
|
283
|
+
"@aws-sdk/credential-provider-http/@smithy/node-http-handler": ["@smithy/node-http-handler@4.11.3", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA=="],
|
|
284
|
+
|
|
285
|
+
"@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1116.0", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q=="],
|
|
286
|
+
|
|
287
|
+
"@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.11.3", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA=="],
|
|
288
|
+
|
|
289
|
+
"@earendil-works/pi-agent-core/typebox": ["typebox@1.3.7", "", {}, "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg=="],
|
|
290
|
+
|
|
291
|
+
"@earendil-works/pi-ai/typebox": ["typebox@1.3.7", "", {}, "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg=="],
|
|
292
|
+
|
|
293
|
+
"@earendil-works/pi-coding-agent/typebox": ["typebox@1.3.7", "", {}, "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg=="],
|
|
294
|
+
|
|
295
|
+
"@earendil-works/pi-protocol/typebox": ["typebox@1.3.7", "", {}, "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg=="],
|
|
296
|
+
|
|
297
|
+
"p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Domain Docs
|
|
2
|
+
|
|
3
|
+
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
|
|
4
|
+
|
|
5
|
+
## Before exploring, read these
|
|
6
|
+
|
|
7
|
+
- **`CONTEXT.md`** at the repo root, or
|
|
8
|
+
- **`CONTEXT-MAP.md`** at the repo root if it exists: it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
|
|
9
|
+
- **`docs/adr/`**: read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
|
|
10
|
+
|
|
11
|
+
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
|
|
12
|
+
|
|
13
|
+
## File structure
|
|
14
|
+
|
|
15
|
+
Single-context repo (most repos):
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/
|
|
19
|
+
├── CONTEXT.md
|
|
20
|
+
├── docs/adr/
|
|
21
|
+
│ ├── 0001-event-sourced-orders.md
|
|
22
|
+
│ └── 0002-postgres-for-write-model.md
|
|
23
|
+
└── src/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
/
|
|
30
|
+
├── CONTEXT-MAP.md
|
|
31
|
+
├── docs/adr/ ← system-wide decisions
|
|
32
|
+
└── src/
|
|
33
|
+
├── ordering/
|
|
34
|
+
│ ├── CONTEXT.md
|
|
35
|
+
│ └── docs/adr/ ← context-specific decisions
|
|
36
|
+
└── billing/
|
|
37
|
+
├── CONTEXT.md
|
|
38
|
+
└── docs/adr/
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Use the glossary's vocabulary
|
|
42
|
+
|
|
43
|
+
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
|
|
44
|
+
|
|
45
|
+
If the concept you need isn't in the glossary yet, that's a signal: either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
|
|
46
|
+
|
|
47
|
+
## Flag ADR conflicts
|
|
48
|
+
|
|
49
|
+
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
|
|
50
|
+
|
|
51
|
+
> _Contradicts ADR-0007 (event-sourced orders), but worth reopening because…_
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Issue tracker: GitHub
|
|
2
|
+
|
|
3
|
+
Issues and specs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
|
|
4
|
+
|
|
5
|
+
## Conventions
|
|
6
|
+
|
|
7
|
+
- **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
|
|
8
|
+
- **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
|
|
9
|
+
- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
|
|
10
|
+
- **Comment on an issue**: `gh issue comment <number> --body "..."`
|
|
11
|
+
- **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
|
|
12
|
+
- **Close**: `gh issue close <number> --comment "..."`
|
|
13
|
+
|
|
14
|
+
Infer the repo from `git remote -v`; `gh` does this automatically when run inside a clone.
|
|
15
|
+
|
|
16
|
+
## Pull requests as a triage surface
|
|
17
|
+
|
|
18
|
+
**PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; `/triage` reads this flag.)_
|
|
19
|
+
|
|
20
|
+
When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents:
|
|
21
|
+
|
|
22
|
+
- **Read a PR**: `gh pr view <number> --comments` and `gh pr diff <number>` for the diff.
|
|
23
|
+
- **List external PRs for triage**: `gh pr list --state open --json number,title,body,labels,author,authorAssociation,comments` then keep only `authorAssociation` of `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, or `NONE` (drop `OWNER`/`MEMBER`/`COLLABORATOR`).
|
|
24
|
+
- **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`.
|
|
25
|
+
|
|
26
|
+
GitHub shares one number space across issues and PRs, so a bare `#42` may be either: resolve with `gh pr view 42` and fall back to `gh issue view 42`.
|
|
27
|
+
|
|
28
|
+
## When a skill says "publish to the issue tracker"
|
|
29
|
+
|
|
30
|
+
Create a GitHub issue.
|
|
31
|
+
|
|
32
|
+
## When a skill says "fetch the relevant ticket"
|
|
33
|
+
|
|
34
|
+
Run `gh issue view <number> --comments`.
|
|
35
|
+
|
|
36
|
+
## Wayfinding operations
|
|
37
|
+
|
|
38
|
+
Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
|
39
|
+
|
|
40
|
+
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`.
|
|
41
|
+
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
|
42
|
+
- **Blocking**: GitHub's **native issue dependencies**, the canonical, UI-visible representation. Add an edge with `gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<blocker-db-id>`, where `<blocker-db-id>` is the blocker's numeric **database id** (`gh api repos/<owner>/<repo>/issues/<n> --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only, the live gate). Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
|
|
43
|
+
- **Frontier query**: list the map's open children (`gh issue list --state open`, scoped to the map's sub-issues / task list), drop any with an open blocker (`issue_dependencies_summary.blocked_by > 0`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
|
|
44
|
+
- **Claim**: `gh issue edit <n> --add-assignee @me`, the session's first write.
|
|
45
|
+
- **Resolve**: `gh issue comment <n> --body "<answer>"`, then `gh issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@npm-ken/pi-tool-discovery",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "bun test",
|
|
7
|
+
"typecheck": "tsc --noEmit"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@earendil-works/pi-coding-agent": "^0.84.3",
|
|
11
|
+
"@iarna/toml": "^2.2.5",
|
|
12
|
+
"typebox": "^1.3.7"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/bun": "latest",
|
|
16
|
+
"typescript": "^5.9.3"
|
|
17
|
+
},
|
|
18
|
+
"pi": {
|
|
19
|
+
"extensions": ["./src/index.ts"]
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { parse } from "@iarna/toml";
|
|
3
|
+
|
|
4
|
+
export interface AllowList {
|
|
5
|
+
tools: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function resolveDirectTools(
|
|
9
|
+
globalPath: string,
|
|
10
|
+
projectPath: string,
|
|
11
|
+
projectTrusted: boolean,
|
|
12
|
+
warn: (message: string) => void,
|
|
13
|
+
): Promise<Set<string>> {
|
|
14
|
+
const global = await readAllowListOrWarn(globalPath, warn);
|
|
15
|
+
const project = projectTrusted ? await readAllowListOrWarn(projectPath, warn) : [];
|
|
16
|
+
return new Set([...global, ...project]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function readAllowListOrWarn(path: string, warn: (message: string) => void): Promise<string[]> {
|
|
20
|
+
try {
|
|
21
|
+
return (await readAllowList(path)).tools;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
warn(errorMessage(error));
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function readAllowList(path: string): Promise<AllowList> {
|
|
29
|
+
let content: string;
|
|
30
|
+
try {
|
|
31
|
+
content = await readFile(path, "utf8");
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (isMissingFile(error)) return { tools: [] };
|
|
34
|
+
throw new Error(`${path}: ${errorMessage(error)}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let parsed: unknown;
|
|
38
|
+
try {
|
|
39
|
+
parsed = parse(content);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new Error(`${path}: ${errorMessage(error)}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.tools) || !parsed.tools.every((tool) => typeof tool === "string")) {
|
|
45
|
+
throw new Error(`${path}: tools must be an array of strings`);
|
|
46
|
+
}
|
|
47
|
+
return { tools: [...new Set(parsed.tools)] };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isMissingFile(error: unknown): error is NodeJS.ErrnoException {
|
|
51
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function errorMessage(error: unknown): string {
|
|
55
|
+
return error instanceof Error ? error.message : String(error);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
59
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60
|
+
}
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export interface ToolMetadata {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
parameters: unknown;
|
|
5
|
+
promptGuidelines?: string[];
|
|
6
|
+
sourceInfo: { source: string };
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ACTIVATION_TOOL_NAME = "activate_tool";
|
|
11
|
+
|
|
12
|
+
export function selectDeferredTools(tools: ToolMetadata[]): ToolMetadata[] {
|
|
13
|
+
return tools.filter(
|
|
14
|
+
(tool) => tool.name !== ACTIVATION_TOOL_NAME && tool.sourceInfo.source !== "builtin",
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function selectActiveDeferredTools(
|
|
19
|
+
tools: ToolMetadata[],
|
|
20
|
+
activeNames: Set<string>,
|
|
21
|
+
directNames: Set<string>,
|
|
22
|
+
): ToolMetadata[] {
|
|
23
|
+
return selectDeferredTools(tools).filter(
|
|
24
|
+
(tool) => activeNames.has(tool.name) && !directNames.has(tool.name),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const GENERIC_TERMS = new Set([
|
|
29
|
+
"tool", "tools", "capability", "capabilities", "need", "needs", "needed",
|
|
30
|
+
"use", "using", "find", "help", "with",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
function terms(text: string): string[] {
|
|
34
|
+
return (text.toLowerCase().match(/[a-z0-9]+/g) ?? [])
|
|
35
|
+
.filter((term) => term.length >= 3 && !GENERIC_TERMS.has(term));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function matchScore(requestTerms: string[], searchable: string[], exactWeight: number, partialWeight: number): number {
|
|
39
|
+
return requestTerms.reduce((total, term) => {
|
|
40
|
+
if (searchable.some((candidate) => candidate === term)) return total + exactWeight;
|
|
41
|
+
if (searchable.some((candidate) => candidate.startsWith(term) || term.startsWith(candidate))) return total + partialWeight;
|
|
42
|
+
return total;
|
|
43
|
+
}, 0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function score(requestTerms: string[], tool: ToolMetadata): number {
|
|
47
|
+
// Tool names are concise capability labels. Give them priority over long,
|
|
48
|
+
// often overlapping descriptions so that "search the web" selects web_search,
|
|
49
|
+
// not a tool that merely mentions search in its usage notes.
|
|
50
|
+
const nameTerms = terms(tool.name.replaceAll("_", " "));
|
|
51
|
+
const descriptionTerms = terms(tool.description);
|
|
52
|
+
return matchScore(requestTerms, nameTerms, 10, 5)
|
|
53
|
+
+ matchScore(requestTerms, descriptionTerms, 2, 1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function findMatchingTools(request: string, tools: ToolMetadata[]): ToolMetadata[] {
|
|
57
|
+
const requestTerms = terms(request);
|
|
58
|
+
if (requestTerms.length === 0) return [];
|
|
59
|
+
return tools
|
|
60
|
+
.map((tool) => ({ tool, score: score(requestTerms, tool) }))
|
|
61
|
+
.filter((match) => match.score > 0)
|
|
62
|
+
.sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name))
|
|
63
|
+
.map((match) => match.tool);
|
|
64
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ToolMetadata } from "./core.ts";
|
|
2
|
+
|
|
3
|
+
const DESCRIPTION_LIMIT = 200;
|
|
4
|
+
|
|
5
|
+
/** Return a compact prompt index for inactive tools. */
|
|
6
|
+
export async function createDiscoverableTools(
|
|
7
|
+
tools: ToolMetadata[],
|
|
8
|
+
_sessionId: string,
|
|
9
|
+
): Promise<string> {
|
|
10
|
+
if (tools.length === 0) return "";
|
|
11
|
+
|
|
12
|
+
const entries = tools.map((tool) => [
|
|
13
|
+
" <tool",
|
|
14
|
+
` name="${escapeXml(tool.name)}"`,
|
|
15
|
+
` description="${escapeXml(shortDescription(tool.description))}"`,
|
|
16
|
+
" />",
|
|
17
|
+
].join("\n"));
|
|
18
|
+
|
|
19
|
+
return `<tool_discovery>
|
|
20
|
+
Here are tools available for use after they are activated using activate_tool.
|
|
21
|
+
${entries.join("\n")}
|
|
22
|
+
</tool_discovery>`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function injectDiscoverableTools(systemPrompt: string, discoverableTools: string): string {
|
|
26
|
+
// The activate_tool guideline names this tag. Only an opening tag followed by a
|
|
27
|
+
// newline is an injected discovery section; a textual mention must not block it.
|
|
28
|
+
if (!discoverableTools || /<tool_discovery>\r?\n/.test(systemPrompt)) return systemPrompt;
|
|
29
|
+
|
|
30
|
+
const toolsEnd = "</tools>";
|
|
31
|
+
const toolsEndIndex = systemPrompt.indexOf(toolsEnd);
|
|
32
|
+
if (toolsEndIndex !== -1) {
|
|
33
|
+
const insertIndex = toolsEndIndex + toolsEnd.length;
|
|
34
|
+
return `${systemPrompt.slice(0, insertIndex)}\n\n${discoverableTools}${systemPrompt.slice(insertIndex)}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fallbackSectionEnd = "\n\nIn addition to the tools above";
|
|
38
|
+
const fallbackIndex = systemPrompt.indexOf(fallbackSectionEnd);
|
|
39
|
+
if (fallbackIndex === -1) return `${systemPrompt}\n\n${discoverableTools}`;
|
|
40
|
+
return `${systemPrompt.slice(0, fallbackIndex)}\n\n${discoverableTools}${systemPrompt.slice(fallbackIndex)}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function shortDescription(description: string): string {
|
|
44
|
+
const sentences = description.match(/.*?[.!?](?:\s|$)/g) ?? [description];
|
|
45
|
+
const summary = sentences.slice(0, 2).join("").trim();
|
|
46
|
+
return summary.length <= DESCRIPTION_LIMIT
|
|
47
|
+
? summary
|
|
48
|
+
: `${summary.slice(0, DESCRIPTION_LIMIT - 1).trimEnd()}…`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function escapeXml(value: string): string {
|
|
52
|
+
return value.replace(/[&<>"']/g, (character) => ({
|
|
53
|
+
"&": "&",
|
|
54
|
+
"<": "<",
|
|
55
|
+
">": ">",
|
|
56
|
+
'"': """,
|
|
57
|
+
"'": "'",
|
|
58
|
+
})[character] ?? character);
|
|
59
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { CONFIG_DIR_NAME, getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
import {
|
|
5
|
+
selectActiveDeferredTools,
|
|
6
|
+
type ToolMetadata,
|
|
7
|
+
} from "./core.ts";
|
|
8
|
+
import { resolveDirectTools } from "./config.ts";
|
|
9
|
+
import { createDiscoverableTools, injectDiscoverableTools } from "./discoverable-tools.ts";
|
|
10
|
+
|
|
11
|
+
const ACTIVATION_TOOL_NAME = "activate_tool";
|
|
12
|
+
const ACTIVATION_TOOL_DESCRIPTION = "Activate one deferred tool by name.";
|
|
13
|
+
const REQUIRED_DIRECT_TOOLS = ["read"];
|
|
14
|
+
|
|
15
|
+
function asToolMetadata(tools: ReturnType<ExtensionAPI["getAllTools"]>): ToolMetadata[] {
|
|
16
|
+
return tools as ToolMetadata[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function toolDiscovery(pi: ExtensionAPI): void {
|
|
20
|
+
let deferredTools: ToolMetadata[] = [];
|
|
21
|
+
let directTools = new Set<string>();
|
|
22
|
+
let discoverableTools = "";
|
|
23
|
+
let sessionId = "unknown-session";
|
|
24
|
+
let reconciled = false;
|
|
25
|
+
|
|
26
|
+
function deferActiveTools(): void {
|
|
27
|
+
const activeTools = pi.getActiveTools();
|
|
28
|
+
deferredTools = selectActiveDeferredTools(
|
|
29
|
+
asToolMetadata(pi.getAllTools()),
|
|
30
|
+
new Set(activeTools),
|
|
31
|
+
directTools,
|
|
32
|
+
);
|
|
33
|
+
const deferredNames = new Set(deferredTools.map((tool) => tool.name));
|
|
34
|
+
pi.setActiveTools(activeTools.filter((name) => !deferredNames.has(name)));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pi.registerTool({
|
|
38
|
+
name: ACTIVATION_TOOL_NAME,
|
|
39
|
+
label: "Activate Tool",
|
|
40
|
+
description: ACTIVATION_TOOL_DESCRIPTION,
|
|
41
|
+
promptSnippet: ACTIVATION_TOOL_DESCRIPTION,
|
|
42
|
+
promptGuidelines: [
|
|
43
|
+
"Call activate_tool when an inactive tool is needed. Set name to the tool name shown in <tool_discovery>. It activates that tool for the next response.",
|
|
44
|
+
],
|
|
45
|
+
parameters: Type.Object({
|
|
46
|
+
name: Type.String({ description: "The inactive tool name to activate" }),
|
|
47
|
+
}),
|
|
48
|
+
async execute(_toolCallId, params) {
|
|
49
|
+
const selected = deferredTools.find((tool) => tool.name === params.name);
|
|
50
|
+
if (!selected) {
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text", text: `No inactive tool is named: ${params.name}` }],
|
|
53
|
+
details: { activated: [], alreadyActive: [] },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const active = pi.getActiveTools();
|
|
58
|
+
const activated = active.includes(selected.name) ? [] : [selected.name];
|
|
59
|
+
const alreadyActive = active.includes(selected.name) ? [selected.name] : [];
|
|
60
|
+
if (activated.length > 0) pi.setActiveTools([...active, selected.name]);
|
|
61
|
+
|
|
62
|
+
const status = activated.length > 0
|
|
63
|
+
? `Activated: ${selected.name}.`
|
|
64
|
+
: `Already active: ${selected.name}.`;
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: "text", text: status }],
|
|
67
|
+
details: { activated, alreadyActive },
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
73
|
+
deferredTools = [];
|
|
74
|
+
discoverableTools = "";
|
|
75
|
+
sessionId = ctx.sessionManager.getSessionId();
|
|
76
|
+
reconciled = false;
|
|
77
|
+
|
|
78
|
+
const configuredDirectTools = await resolveDirectTools(
|
|
79
|
+
join(getAgentDir(), "tool-discovery.toml"),
|
|
80
|
+
join(ctx.cwd, CONFIG_DIR_NAME, "tool-discovery.toml"),
|
|
81
|
+
ctx.isProjectTrusted(),
|
|
82
|
+
(message) => ctx.ui.notify(`Tool discovery configuration warning: ${message}`, "warning"),
|
|
83
|
+
);
|
|
84
|
+
directTools = new Set([...REQUIRED_DIRECT_TOOLS, ...configuredDirectTools]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
pi.on("before_agent_start", async (event) => {
|
|
88
|
+
if (!reconciled) {
|
|
89
|
+
deferActiveTools();
|
|
90
|
+
discoverableTools = await createDiscoverableTools(deferredTools, sessionId);
|
|
91
|
+
reconciled = true;
|
|
92
|
+
}
|
|
93
|
+
return { systemPrompt: injectDiscoverableTools(event.systemPrompt, discoverableTools) };
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { readAllowList, resolveDirectTools } from "../src/config.ts";
|
|
6
|
+
|
|
7
|
+
describe("readAllowList", () => {
|
|
8
|
+
test("reads tool names from a TOML tools array", async () => {
|
|
9
|
+
const directory = await mkdtemp(join(tmpdir(), "tool-discovery-config-"));
|
|
10
|
+
const path = join(directory, "tool-discovery.toml");
|
|
11
|
+
await writeFile(path, 'tools = ["search_issues", "deploy_preview"]\n');
|
|
12
|
+
|
|
13
|
+
await expect(readAllowList(path)).resolves.toEqual({ tools: ["search_issues", "deploy_preview"] });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("accepts a missing file as an empty allow-list", async () => {
|
|
17
|
+
await expect(readAllowList("/definitely/not/a/config.toml")).resolves.toEqual({ tools: [] });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("merges global and trusted project allow-lists", async () => {
|
|
21
|
+
const directory = await mkdtemp(join(tmpdir(), "tool-discovery-config-"));
|
|
22
|
+
const globalPath = join(directory, "global.toml");
|
|
23
|
+
const projectPath = join(directory, "project.toml");
|
|
24
|
+
await writeFile(globalPath, 'tools = ["search_issues"]\n');
|
|
25
|
+
await writeFile(projectPath, 'tools = ["deploy_preview"]\n');
|
|
26
|
+
|
|
27
|
+
await expect(resolveDirectTools(globalPath, projectPath, true, () => {})).resolves.toEqual(
|
|
28
|
+
new Set(["search_issues", "deploy_preview"]),
|
|
29
|
+
);
|
|
30
|
+
await expect(resolveDirectTools(globalPath, projectPath, false, () => {})).resolves.toEqual(
|
|
31
|
+
new Set(["search_issues"]),
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("warns and ignores invalid files", async () => {
|
|
36
|
+
const warnings: string[] = [];
|
|
37
|
+
const directory = await mkdtemp(join(tmpdir(), "tool-discovery-config-"));
|
|
38
|
+
const path = join(directory, "tool-discovery.toml");
|
|
39
|
+
await writeFile(path, "tools = [42]\n");
|
|
40
|
+
|
|
41
|
+
await expect(resolveDirectTools(path, "/not-used.toml", false, (warning) => warnings.push(warning))).resolves.toEqual(new Set());
|
|
42
|
+
expect(warnings).toEqual([`${path}: tools must be an array of strings`]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("identifies invalid files and validation errors", async () => {
|
|
46
|
+
const directory = await mkdtemp(join(tmpdir(), "tool-discovery-config-"));
|
|
47
|
+
const path = join(directory, "tool-discovery.toml");
|
|
48
|
+
await writeFile(path, "tools = [42]\n");
|
|
49
|
+
|
|
50
|
+
await expect(readAllowList(path)).rejects.toThrow(`${path}: tools must be an array of strings`);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
findMatchingTools,
|
|
4
|
+
selectActiveDeferredTools,
|
|
5
|
+
selectDeferredTools,
|
|
6
|
+
type ToolMetadata,
|
|
7
|
+
} from "../src/core.ts";
|
|
8
|
+
|
|
9
|
+
const tools: ToolMetadata[] = [
|
|
10
|
+
{ name: "search_issues", description: "Search GitHub issues by keyword and state.", parameters: { type: "object", properties: { query: { type: "string" } } }, sourceInfo: { source: "extension" } },
|
|
11
|
+
{ name: "deploy_preview", description: "Deploy the current project to a preview environment.", parameters: { type: "object", properties: {} }, sourceInfo: { source: "extension" } },
|
|
12
|
+
{ name: "read", description: "Read a file.", parameters: {}, sourceInfo: { source: "builtin" } },
|
|
13
|
+
{ name: "activate_tool", description: "Activate deferred tools.", parameters: {}, sourceInfo: { source: "extension" } },
|
|
14
|
+
{ name: "sdk_tool", description: "Run an SDK capability.", parameters: {}, sourceInfo: { source: "sdk" } },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
test("selects active non-built-in tools except direct and discovery tools", () => {
|
|
18
|
+
expect(selectDeferredTools(tools)).toEqual([tools[0], tools[1], tools[4]]);
|
|
19
|
+
expect(selectActiveDeferredTools(tools, new Set(tools.map((tool) => tool.name)), new Set(["read", "search_issues"])))
|
|
20
|
+
.toEqual([tools[1], tools[4]]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("matches tool requests against tool names and descriptions", () => {
|
|
24
|
+
expect(findMatchingTools("find GitHub tickets", tools.slice(0, 2))).toEqual([tools[0]]);
|
|
25
|
+
expect(findMatchingTools("transcode a video", tools.slice(0, 2))).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("does not match generic discovery words", () => {
|
|
29
|
+
expect(findMatchingTools("need a tool for this capability", tools.slice(0, 2))).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("prefers a capability named by the request over a broad tool description", () => {
|
|
33
|
+
const webSearch: ToolMetadata = {
|
|
34
|
+
name: "web_search",
|
|
35
|
+
description: "Search the public web and return cited results.",
|
|
36
|
+
parameters: {},
|
|
37
|
+
sourceInfo: { source: "extension" },
|
|
38
|
+
};
|
|
39
|
+
const batchCommands: ToolMetadata = {
|
|
40
|
+
name: "ctx_batch_execute",
|
|
41
|
+
description: "Run multiple shell commands. It can also search indexed command output and web content.",
|
|
42
|
+
parameters: {},
|
|
43
|
+
sourceInfo: { source: "extension" },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
expect(findMatchingTools("search the web", [batchCommands, webSearch]))
|
|
47
|
+
.toEqual([webSearch, batchCommands]);
|
|
48
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { createDiscoverableTools, injectDiscoverableTools } from "../src/discoverable-tools.ts";
|
|
3
|
+
|
|
4
|
+
const tool = {
|
|
5
|
+
name: "search_issues",
|
|
6
|
+
description: "Search GitHub issues by keyword and state. Supports repository filters. Returns matching issue metadata.",
|
|
7
|
+
parameters: { type: "object", properties: { query: { type: "string" } } },
|
|
8
|
+
promptGuidelines: ["Use search_issues to find GitHub issues."],
|
|
9
|
+
sourceInfo: { source: "extension" },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
test("returns a compact tool index without file pointers", async () => {
|
|
13
|
+
const section = await createDiscoverableTools([tool], "test/session");
|
|
14
|
+
|
|
15
|
+
expect(section).toContain("<tool_discovery>");
|
|
16
|
+
expect(section).toContain(' <tool\n name="search_issues"');
|
|
17
|
+
expect(section).toContain(' description="Search GitHub issues by keyword and state. Supports repository filters."');
|
|
18
|
+
expect(section).not.toContain("Returns matching issue metadata.");
|
|
19
|
+
expect(section).toContain("Here are tools available for use after they are activated using activate_tool.");
|
|
20
|
+
expect(section).not.toContain("location=");
|
|
21
|
+
expect(section).not.toContain("Read a tool file at its location");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("places tool discovery below the tools section", () => {
|
|
25
|
+
const prompt = "<tools>\n <tool name=\"read\" />\n</tools>\n\n<instructions>Use tools safely.</instructions>";
|
|
26
|
+
const section = "<tool_discovery>\n <tool name=\"search_issues\" />\n</tool_discovery>";
|
|
27
|
+
|
|
28
|
+
expect(injectDiscoverableTools(prompt, section)).toBe(
|
|
29
|
+
"<tools>\n <tool name=\"read\" />\n</tools>\n\n<tool_discovery>\n <tool name=\"search_issues\" />\n</tool_discovery>\n\n<instructions>Use tools safely.</instructions>",
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("does not mistake a guideline mention for an injected discovery section", () => {
|
|
34
|
+
const prompt = "Guideline: set name to the tool name shown in <tool_discovery>.\n\nIn addition to the tools above, more instructions.";
|
|
35
|
+
const section = "<tool_discovery>\n <tool name=\"search_issues\" />\n</tool_discovery>";
|
|
36
|
+
|
|
37
|
+
expect(injectDiscoverableTools(prompt, section)).toBe(
|
|
38
|
+
"Guideline: set name to the tool name shown in <tool_discovery>.\n\n<tool_discovery>\n <tool name=\"search_issues\" />\n</tool_discovery>\n\nIn addition to the tools above, more instructions.",
|
|
39
|
+
);
|
|
40
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import toolDiscovery from "../src/index.ts";
|
|
3
|
+
|
|
4
|
+
interface HandlerMap {
|
|
5
|
+
session_start?: (event: unknown, ctx: unknown) => Promise<void>;
|
|
6
|
+
before_agent_start?: (event: { systemPrompt: string }) => Promise<{ systemPrompt: string }>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const baseSystemPrompt = `Available tools:
|
|
10
|
+
- read: Read files.
|
|
11
|
+
- activate_tool: Activate an inactive tool.
|
|
12
|
+
|
|
13
|
+
In addition to the tools above, you may have access to other custom tools depending on the project.`;
|
|
14
|
+
|
|
15
|
+
function createPi(initialActive = ["read", "bash", "search_issues", "search_comments", "deploy_preview", "activate_tool"]) {
|
|
16
|
+
const active = [...initialActive];
|
|
17
|
+
const handlers: HandlerMap = {};
|
|
18
|
+
let activationTool: any;
|
|
19
|
+
const tools = [
|
|
20
|
+
{ name: "read", description: "Read files.", parameters: {}, sourceInfo: { source: "builtin" } },
|
|
21
|
+
{ name: "bash", description: "Run shell commands.", parameters: {}, sourceInfo: { source: "builtin" } },
|
|
22
|
+
{ name: "search_issues", description: "Search GitHub issues by keyword.", parameters: {}, sourceInfo: { source: "extension" } },
|
|
23
|
+
{ name: "search_comments", description: "Search GitHub comments by keyword.", parameters: {}, sourceInfo: { source: "extension" } },
|
|
24
|
+
{ name: "deploy_preview", description: "Deploy a preview environment.", parameters: {}, sourceInfo: { source: "extension" } },
|
|
25
|
+
];
|
|
26
|
+
const pi = {
|
|
27
|
+
registerTool(tool: any) { activationTool = tool; },
|
|
28
|
+
on(name: keyof HandlerMap, handler: HandlerMap[keyof HandlerMap]) { handlers[name] = handler as never; },
|
|
29
|
+
getAllTools() { return [...tools, { name: "activate_tool", description: "Activate a tool.", parameters: {}, sourceInfo: { source: "extension" } }]; },
|
|
30
|
+
getActiveTools() { return [...active]; },
|
|
31
|
+
setActiveTools(names: string[]) { active.splice(0, active.length, ...names); },
|
|
32
|
+
};
|
|
33
|
+
return { pi, active, tools, handlers, getActivationTool: () => activationTool };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const sessionContext = (id: string) => ({
|
|
37
|
+
cwd: `/test/project/${id}`,
|
|
38
|
+
isProjectTrusted: () => false,
|
|
39
|
+
sessionManager: { getSessionId: () => id },
|
|
40
|
+
ui: { notify: () => {} },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("tool discovery extension", () => {
|
|
44
|
+
test("defers eligible tools and provides names and descriptions", async () => {
|
|
45
|
+
const fixture = createPi();
|
|
46
|
+
toolDiscovery(fixture.pi as never);
|
|
47
|
+
await fixture.handlers.session_start?.({}, sessionContext("test-session"));
|
|
48
|
+
const prompt = await fixture.handlers.before_agent_start?.({ systemPrompt: baseSystemPrompt });
|
|
49
|
+
|
|
50
|
+
expect(fixture.active).toEqual(["read", "bash", "activate_tool"]);
|
|
51
|
+
expect(prompt?.systemPrompt).toContain("<tool_discovery>");
|
|
52
|
+
expect(prompt?.systemPrompt).not.toContain('name="bash"');
|
|
53
|
+
expect(prompt?.systemPrompt).toContain('name="search_issues"');
|
|
54
|
+
expect(prompt?.systemPrompt).toContain('description="Search GitHub issues by keyword."');
|
|
55
|
+
expect(prompt?.systemPrompt).not.toContain("location=");
|
|
56
|
+
expect(prompt?.systemPrompt).toContain("Here are tools available for use after they are activated using activate_tool.");
|
|
57
|
+
expect(fixture.getActivationTool().description).toBe("Activate one deferred tool by name.");
|
|
58
|
+
expect(fixture.getActivationTool().promptSnippet).toBe("Activate one deferred tool by name.");
|
|
59
|
+
expect(fixture.getActivationTool().promptGuidelines).toEqual([
|
|
60
|
+
"Call activate_tool when an inactive tool is needed. Set name to the tool name shown in <tool_discovery>. It activates that tool for the next response.",
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const result = await fixture.getActivationTool().execute("call", { name: "search_issues" });
|
|
64
|
+
expect(result.details).toEqual({ activated: ["search_issues"], alreadyActive: [] });
|
|
65
|
+
expect(fixture.active).toEqual(["read", "bash", "activate_tool", "search_issues"]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("activates only the named tool", async () => {
|
|
69
|
+
const fixture = createPi();
|
|
70
|
+
toolDiscovery(fixture.pi as never);
|
|
71
|
+
await fixture.handlers.session_start?.({}, sessionContext("test-single-activation"));
|
|
72
|
+
await fixture.handlers.before_agent_start?.({ systemPrompt: baseSystemPrompt });
|
|
73
|
+
|
|
74
|
+
const result = await fixture.getActivationTool().execute("call", { name: "search_comments" });
|
|
75
|
+
expect(result.details).toEqual({ activated: ["search_comments"], alreadyActive: [] });
|
|
76
|
+
expect(fixture.active).toEqual(["read", "bash", "activate_tool", "search_comments"]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("keeps activated tools active", async () => {
|
|
80
|
+
const fixture = createPi();
|
|
81
|
+
toolDiscovery(fixture.pi as never);
|
|
82
|
+
await fixture.handlers.session_start?.({}, sessionContext("test-later-start"));
|
|
83
|
+
await fixture.handlers.before_agent_start?.({ systemPrompt: baseSystemPrompt });
|
|
84
|
+
await fixture.getActivationTool().execute("call", { name: "deploy_preview" });
|
|
85
|
+
|
|
86
|
+
expect(fixture.active).toEqual(["read", "bash", "activate_tool", "deploy_preview"]);
|
|
87
|
+
const result = await fixture.getActivationTool().execute("call", { name: "deploy_preview" });
|
|
88
|
+
expect(result.details).toEqual({ activated: [], alreadyActive: ["deploy_preview"] });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("does not activate disabled or unknown tools", async () => {
|
|
92
|
+
const fixture = createPi(["read", "search_issues", "activate_tool"]);
|
|
93
|
+
toolDiscovery(fixture.pi as never);
|
|
94
|
+
await fixture.handlers.session_start?.({}, sessionContext("test-disabled"));
|
|
95
|
+
await fixture.handlers.before_agent_start?.({ systemPrompt: baseSystemPrompt });
|
|
96
|
+
|
|
97
|
+
const result = await fixture.getActivationTool().execute("call", { name: "deploy_preview" });
|
|
98
|
+
expect(result.details).toEqual({ activated: [], alreadyActive: [] });
|
|
99
|
+
});
|
|
100
|
+
});
|
package/tsconfig.json
ADDED