diff --git a/README.md b/README.md index 60e5f99..68ceb73 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Kompass keeps AI coding agents on course with token-efficient, composable workfl - Commands cover direct work (`/ask`, `/commit`, `/merge`, `/skill/create`, `/skill/optimize`), orchestration (`/dev`, `/ship`, `/todo`, `/pr/fix/loop`), ticket planning/sync, and PR review/shipping flows. `/branch/inline`, `/commit/inline`, `/commit-and-push/inline`, `/pr/create/inline`, and `/ship/inline` reuse the invoking session instead of starting a subtask. - Agents are intentionally narrow: `worker` handles implementation and multi-step workflows, `planner` is no-edit planning, and `reviewer` is a no-edit review specialist. -- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `pr_load`, `pr_load_review`, `pr_sync`, `ticket_load`, `ticket_sync`. OpenCode users can opt into the eight Navigator session and worktree tools. +- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `pr_load`, `pr_load_review`, `pr_sync`, `ticket_load`, `ticket_sync`. OpenCode 1 also includes the eight Navigator session and worktree tools. - Reusable command-template components live in `packages/core/components/` and are documented in the components reference. ## Prerequisites @@ -29,7 +29,7 @@ Kompass keeps AI coding agents on course with token-efficient, composable workfl ## Installation -For OpenCode, add the adapter package to your config: +The same package supports both OpenCode plugin APIs. For OpenCode 1, add: ```json { @@ -37,6 +37,16 @@ For OpenCode, add the adapter package to your config: } ``` +For OpenCode 2, use the plural `plugins` key: + +```json +{ + "plugins": ["@kompassdev/opencode"] +} +``` + +OpenCode 2 agent transforms are update-only. If an agent is renamed in Kompass config, the host must register that agent ID before Kompass setup runs. + Project config is optional. To start from the published base config: ```bash @@ -54,7 +64,7 @@ The recommended project override path is `.opencode/kompass.jsonc`. ## Kompass Navigator -Navigator is an OpenCode capability for explicitly requested orchestration of native sessions in the current checkout and OpenCode-managed Git worktrees. It is not a subagent mechanism; ordinary delegation should use OpenCode's built-in `task` tool. Navigator is enabled by default, follows OpenCode Desktop's protocol detection so session creation, prompts, reads, status, and interrupts stay on one compatible API, returns immediately after admitting prompts, and supports parallel sessions. Until OpenCode implements V2 wait, Navigator waits by polling the active-session API locally. It requires OpenCode `1.17.12` or newer. +Navigator is available through the OpenCode 1 plugin API for explicitly requested orchestration of native sessions in the current checkout and OpenCode-managed Git worktrees. OpenCode 2 does not yet expose the workspace, worktree, and session-discovery APIs required to register Navigator safely. Navigator is not a subagent mechanism; ordinary delegation should use OpenCode's built-in `task` tool. It is enabled by default on OpenCode 1, follows OpenCode Desktop's protocol detection so session creation, prompts, reads, status, and interrupts stay on one compatible API, returns immediately after admitting prompts, and supports parallel sessions. Until OpenCode implements V2 wait, Navigator waits by polling the active-session API locally. It requires OpenCode `1.17.12` or newer. Configure Navigator and its limits with: diff --git a/bun.lock b/bun.lock index bfa0525..ecb659a 100644 --- a/bun.lock +++ b/bun.lock @@ -18,19 +18,16 @@ }, "packages/opencode": { "name": "@kompassdev/opencode", - "version": "0.16.1", + "version": "0.17.4", "dependencies": { + "@opencode-ai/plugin": "0.0.0-beta-18684", + "@opencode-ai/sdk": "^1.18.7", "rift-snapshot": "^0.0.10", + "zod": "^4.1.8", }, "devDependencies": { - "@opencode-ai/plugin": "^1.18.7", - "@opencode-ai/sdk": "^1.18.7", "yaml": "^2.8.2", }, - "peerDependencies": { - "@opencode-ai/plugin": "^1.18.7", - "@opencode-ai/sdk": "^1.18.7", - }, }, "packages/web": { "name": "@kompassdev/web", @@ -70,6 +67,12 @@ "@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.3", "", { "dependencies": { "yaml": "^2.8.2" } }, "sha512-PJzRmgQzUxI2uwpdX2lXSHtP4G8ocp24/t+bZyf5Fy0SZLSF9f9KXZoMlFM/XCGue+B0nH/2IZ7FpBYQATBsCg=="], + "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], + + "@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=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.974.5", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ=="], + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], @@ -210,6 +213,8 @@ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], "@kompassdev/core": ["@kompassdev/core@workspace:packages/core"], @@ -232,7 +237,15 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="], - "@opencode-ai/plugin": ["@opencode-ai/plugin@1.18.7", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@opencode-ai/sdk": "1.18.7", "effect": "4.0.0-beta.83", "zod": "4.1.8" }, "peerDependencies": { "@opentui/core": ">=0.4.5", "@opentui/keymap": ">=0.4.5", "@opentui/solid": ">=0.4.5" }, "optionalPeers": ["@opentui/core", "@opentui/keymap", "@opentui/solid"] }, "sha512-kXm5hqur5JiF/xJPaQYkuNn7vSl4ya09YgL/pIvIrggWCLFsfo1gXqNbONRcGW7m5Ih2k6kgMXFyjB9or1+dEg=="], + "@opencode-ai/ai": ["@opencode-ai/ai@0.0.0-beta-18684", "", { "dependencies": { "@opencode-ai/schema": "0.0.0-beta-18684", "@smithy/eventstream-codec": "4.2.14", "@smithy/util-utf8": "4.2.2", "aws4fetch": "1.0.20", "effect": "4.0.0-rc.112", "google-auth-library": "10.5.0" } }, "sha512-gZTPELkm9aSv0cxWt9gGSdN9fecO9/GlplzUT4knrBD70vxQGnOYx7LGDUPZokW8Fzn7gjD824Qh9AIVjyb07g=="], + + "@opencode-ai/client": ["@opencode-ai/client@0.0.0-beta-18684", "", { "dependencies": { "@opencode-ai/protocol": "0.0.0-beta-18684", "@opencode-ai/schema": "0.0.0-beta-18684" }, "peerDependencies": { "effect": "4.0.0-rc.112", "solid-js": ">=1.9.0" }, "optionalPeers": ["effect", "solid-js"] }, "sha512-Qjq07sTId3gZcqqMsPg+gtqU3PXneOWje+BSzBXepimAfPNqL0aIpH+em2Y4KL3lxEuJK3vdfyx8UXI8gzGRlQ=="], + + "@opencode-ai/plugin": ["@opencode-ai/plugin@0.0.0-beta-18684", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@opencode-ai/ai": "0.0.0-beta-18684", "@opencode-ai/client": "0.0.0-beta-18684", "@opencode-ai/protocol": "0.0.0-beta-18684", "@opencode-ai/schema": "0.0.0-beta-18684", "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112", "zod": "4.1.8" }, "peerDependencies": { "@opencode-ai/theme": "0.0.0-beta-18684", "@opentui/core": ">=0.5.9", "@opentui/solid": ">=0.5.9", "solid-js": ">=1.9.0" }, "optionalPeers": ["@opencode-ai/theme", "@opentui/core", "@opentui/solid", "solid-js"] }, "sha512-OOgIRF563MsMROHrxXuJtdG5729PE1DhN4ASRW6OuH0KNjEbIkCrnOJmBI5by+JSbwTbHrsgKXhk18ee9MpRUg=="], + + "@opencode-ai/protocol": ["@opencode-ai/protocol@0.0.0-beta-18684", "", { "dependencies": { "@opencode-ai/schema": "0.0.0-beta-18684", "effect": "4.0.0-rc.112" } }, "sha512-BXeauVOxxcam0zgOxJcpkmoQQTiqq3L6l3i8qF/Wsa3iPf7cA1SCM6+3r6iJ/9akckEEi9ZL85M3b8NhOcU85A=="], + + "@opencode-ai/schema": ["@opencode-ai/schema@0.0.0-beta-18684", "", { "dependencies": { "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112" } }, "sha512-aoQEG+KBSTAq7IaNVpAfXKNSvcBaRwpjPgPL0DuKoj39g013/NoR/rmbWtjxKkkczBqbWp/91zLGvXW+bIOotQ=="], "@opencode-ai/sdk": ["@opencode-ai/sdk@1.18.7", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-5txVELvCxzC3O/MYnRKdTvBN3TqQjE3PaEks3kDRA/moWzMSUhdm15xCth/Mz/NPYyGTn2u4IdScdV9vLTfLRg=="], @@ -252,6 +265,8 @@ "@pagefind/windows-x64": ["@pagefind/windows-x64@1.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.0", "", { "os": "android", "cpu": "arm" }, "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A=="], @@ -318,6 +333,20 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@smithy/core": ["@smithy/core@3.33.3", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg=="], + + "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.14", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw=="], + + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@smithy/types": ["@smithy/types@4.17.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA=="], + + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "tslib": "^2.6.2" } }, "sha512-nxu3SgmAw9JXT2CtkU0m/XNLWpP9MsaBx1zAGAypCbYj15tIFlmcYwpF+Oh18le83d+IM9PT7ENdXnE4C+d5mA=="], + + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "tslib": "^2.6.2" } }, "sha512-iq+cW3mAb7vfcxEEpYi3zXKpDtbrIFyanWjQl4zBq4seWD4OSxXDWSfespZxenX6aEaighn+NR3u1nU1DSvs3w=="], + + "@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], @@ -366,6 +395,8 @@ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], @@ -392,20 +423,32 @@ "astro-expressive-code": ["astro-expressive-code@0.38.3", "", { "dependencies": { "rehype-expressive-code": "^0.38.3" }, "peerDependencies": { "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0" } }, "sha512-Tvdc7RV0G92BbtyEOsfJtXU35w41CkM94fOAzxbQP67Wj5jArfserJ321FO4XA7WG9QMV0GIBmQq77NBIRDzpQ=="], + "aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="], + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="], "bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="], + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], + "brace-expansion": ["brace-expansion@2.1.4", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -462,6 +505,8 @@ "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], @@ -496,7 +541,11 @@ "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], - "effect": ["effect@4.0.0-beta.83", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-0wsak8RtgGAr9UWSbVDgJHZcUqMSvicHcvaZv1MbMM7MCGgW4Rn/137J1MHQbwYPcwYGxT/IqehFd+UbYuj78w=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + + "effect": ["effect@4.0.0-rc.112", "", { "dependencies": { "fast-check": "^4.9.0", "msgpackr": "^2.0.5" } }, "sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A=="], "emmet": ["emmet@2.4.11", "", { "dependencies": { "@emmetio/abbreviation": "^2.3.3", "@emmetio/css-abbreviation": "^2.1.8" } }, "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ=="], @@ -540,7 +589,7 @@ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], - "fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + "fast-check": ["fast-check@4.9.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -548,7 +597,7 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="], + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], @@ -556,14 +605,30 @@ "fontkitten": ["fontkitten@1.0.3", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "gaxios": ["gaxios@7.3.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ=="], + + "gcp-metadata": ["gcp-metadata@8.1.4", "", { "dependencies": { "gaxios": "7.1.3", "google-logging-utils": "1.1.3", "json-bigint": "^1.0.0" } }, "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw=="], + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], "get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="], "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + "glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + + "google-auth-library": ["google-auth-library@10.5.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="], + + "google-logging-utils": ["google-logging-utils@1.2.0", "", {}, "sha512-WE9av4wKDZgRjBwgVUabocx8T6/7o3Ca1Fat46FXDhXVAFibzNadedcOXrdgd1Kzmk8tsk/9ZH89Wyf/SqeZ3A=="], + + "gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="], + "h3": ["h3@1.15.10", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-YzJeWSkDZxAhvmp8dexjRK5hxziRO7I9m0N53WhvYL5NiWfkUkzssVzY9jvGu0HBoLFW6+duYmNSn6MaZBCCtg=="], "hast-util-embedded": ["hast-util-embedded@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA=="], @@ -614,12 +679,12 @@ "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="], "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], - "ini": ["ini@7.0.0", "", {}, "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w=="], - "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], @@ -644,17 +709,23 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="], - "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + "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=="], + + "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=="], - "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -778,24 +849,30 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "msgpackr": ["msgpackr@2.0.3", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-vFKpMYFTEQujRQxvdS/u6zlfesws0J40K74w6E1fVsYnIa9WKJKB5xIVVON8L7S39hCNrCVGXcPjrYmCb9lT+w=="], + "msgpackr": ["msgpackr@2.1.0", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ=="], "msgpackr-extract": ["msgpackr-extract@3.0.4", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw=="], "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], - "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], "nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="], + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "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=="], + "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], @@ -820,6 +897,8 @@ "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], "pagefind": ["pagefind@1.4.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.4.0", "@pagefind/darwin-x64": "1.4.0", "@pagefind/freebsd-x64": "1.4.0", "@pagefind/linux-arm64": "1.4.0", "@pagefind/linux-x64": "1.4.0", "@pagefind/windows-x64": "1.4.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g=="], @@ -834,6 +913,8 @@ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -918,8 +999,12 @@ "rift-snapshot": ["rift-snapshot@0.0.10", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "rift": "bin/rift.js" } }, "sha512-A10cjyHqylhvD+Qu7gu/oCZ7z7euhsDV1K3Uz0byATct2Vt6IYvGDbuRcE/Hq7RrqBOGM8STLeFm8EPpTBYKcw=="], + "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], + "rollup": ["rollup@4.60.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.0", "@rollup/rollup-android-arm64": "4.60.0", "@rollup/rollup-darwin-arm64": "4.60.0", "@rollup/rollup-darwin-x64": "4.60.0", "@rollup/rollup-freebsd-arm64": "4.60.0", "@rollup/rollup-freebsd-x64": "4.60.0", "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", "@rollup/rollup-linux-arm-musleabihf": "4.60.0", "@rollup/rollup-linux-arm64-gnu": "4.60.0", "@rollup/rollup-linux-arm64-musl": "4.60.0", "@rollup/rollup-linux-loong64-gnu": "4.60.0", "@rollup/rollup-linux-loong64-musl": "4.60.0", "@rollup/rollup-linux-ppc64-gnu": "4.60.0", "@rollup/rollup-linux-ppc64-musl": "4.60.0", "@rollup/rollup-linux-riscv64-gnu": "4.60.0", "@rollup/rollup-linux-riscv64-musl": "4.60.0", "@rollup/rollup-linux-s390x-gnu": "4.60.0", "@rollup/rollup-linux-x64-gnu": "4.60.0", "@rollup/rollup-linux-x64-musl": "4.60.0", "@rollup/rollup-openbsd-x64": "4.60.0", "@rollup/rollup-openharmony-arm64": "4.60.0", "@rollup/rollup-win32-arm64-msvc": "4.60.0", "@rollup/rollup-win32-ia32-msvc": "4.60.0", "@rollup/rollup-win32-x64-gnu": "4.60.0", "@rollup/rollup-win32-x64-msvc": "4.60.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ=="], + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -932,6 +1017,8 @@ "shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], "sitemap": ["sitemap@9.0.1", "", { "dependencies": { "@types/node": "^24.9.2", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/esm/cli.js" } }, "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ=="], @@ -948,10 +1035,14 @@ "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], @@ -964,8 +1055,6 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], - "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], @@ -1018,8 +1107,6 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - "uuid": ["uuid@14.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg=="], - "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], @@ -1066,6 +1153,8 @@ "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], @@ -1074,6 +1163,8 @@ "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], @@ -1092,7 +1183,7 @@ "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], - "zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], @@ -1100,12 +1191,20 @@ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@astrojs/sitemap/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@astrojs/yaml2ts/yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + "@aws-crypto/util/@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=="], + "@expressive-code/plugin-shiki/shiki": ["shiki@1.29.2", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@opencode-ai/plugin/zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="], + "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -1120,8 +1219,14 @@ "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "gcp-metadata/gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="], + + "gcp-metadata/google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "sitemap/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], @@ -1138,12 +1243,16 @@ "wrap-ansi/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], "yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], "zod-to-ts/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@aws-crypto/util/@smithy/util-utf8/@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=="], + "@expressive-code/plugin-shiki/shiki/@shikijs/core": ["@shikijs/core@1.29.2", "", { "dependencies": { "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ=="], "@expressive-code/plugin-shiki/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "oniguruma-to-es": "^2.2.0" } }, "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A=="], @@ -1156,6 +1265,10 @@ "@expressive-code/plugin-shiki/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "boxen/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "boxen/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], diff --git a/packages/opencode/README.md b/packages/opencode/README.md index 60e5f99..68ceb73 100644 --- a/packages/opencode/README.md +++ b/packages/opencode/README.md @@ -20,7 +20,7 @@ Kompass keeps AI coding agents on course with token-efficient, composable workfl - Commands cover direct work (`/ask`, `/commit`, `/merge`, `/skill/create`, `/skill/optimize`), orchestration (`/dev`, `/ship`, `/todo`, `/pr/fix/loop`), ticket planning/sync, and PR review/shipping flows. `/branch/inline`, `/commit/inline`, `/commit-and-push/inline`, `/pr/create/inline`, and `/ship/inline` reuse the invoking session instead of starting a subtask. - Agents are intentionally narrow: `worker` handles implementation and multi-step workflows, `planner` is no-edit planning, and `reviewer` is a no-edit review specialist. -- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `pr_load`, `pr_load_review`, `pr_sync`, `ticket_load`, `ticket_sync`. OpenCode users can opt into the eight Navigator session and worktree tools. +- Structured tools keep workflows grounded in repo and GitHub state: `changes_load`, `pr_load`, `pr_load_review`, `pr_sync`, `ticket_load`, `ticket_sync`. OpenCode 1 also includes the eight Navigator session and worktree tools. - Reusable command-template components live in `packages/core/components/` and are documented in the components reference. ## Prerequisites @@ -29,7 +29,7 @@ Kompass keeps AI coding agents on course with token-efficient, composable workfl ## Installation -For OpenCode, add the adapter package to your config: +The same package supports both OpenCode plugin APIs. For OpenCode 1, add: ```json { @@ -37,6 +37,16 @@ For OpenCode, add the adapter package to your config: } ``` +For OpenCode 2, use the plural `plugins` key: + +```json +{ + "plugins": ["@kompassdev/opencode"] +} +``` + +OpenCode 2 agent transforms are update-only. If an agent is renamed in Kompass config, the host must register that agent ID before Kompass setup runs. + Project config is optional. To start from the published base config: ```bash @@ -54,7 +64,7 @@ The recommended project override path is `.opencode/kompass.jsonc`. ## Kompass Navigator -Navigator is an OpenCode capability for explicitly requested orchestration of native sessions in the current checkout and OpenCode-managed Git worktrees. It is not a subagent mechanism; ordinary delegation should use OpenCode's built-in `task` tool. Navigator is enabled by default, follows OpenCode Desktop's protocol detection so session creation, prompts, reads, status, and interrupts stay on one compatible API, returns immediately after admitting prompts, and supports parallel sessions. Until OpenCode implements V2 wait, Navigator waits by polling the active-session API locally. It requires OpenCode `1.17.12` or newer. +Navigator is available through the OpenCode 1 plugin API for explicitly requested orchestration of native sessions in the current checkout and OpenCode-managed Git worktrees. OpenCode 2 does not yet expose the workspace, worktree, and session-discovery APIs required to register Navigator safely. Navigator is not a subagent mechanism; ordinary delegation should use OpenCode's built-in `task` tool. It is enabled by default on OpenCode 1, follows OpenCode Desktop's protocol detection so session creation, prompts, reads, status, and interrupts stay on one compatible API, returns immediately after admitting prompts, and supports parallel sessions. Until OpenCode implements V2 wait, Navigator waits by polling the active-session API locally. It requires OpenCode `1.17.12` or newer. Configure Navigator and its limits with: diff --git a/packages/opencode/config.ts b/packages/opencode/config.ts index 26bab67..a3b00e1 100644 --- a/packages/opencode/config.ts +++ b/packages/opencode/config.ts @@ -1,5 +1,5 @@ -import type { Config } from "@opencode-ai/plugin"; import type { AgentConfig } from "@opencode-ai/sdk"; +import type { Config } from "./legacy-plugin.ts"; import { loadResolvedAgents, loadResolvedCommands } from "./cache.ts"; import type { PluginLogger } from "./logging.ts"; diff --git a/packages/opencode/index.ts b/packages/opencode/index.ts index 03c1818..8fcdf10 100644 --- a/packages/opencode/index.ts +++ b/packages/opencode/index.ts @@ -1,471 +1,24 @@ -import type { Hooks, Plugin, PluginInput } from "@opencode-ai/plugin"; -import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"; -import { createOpencodeClient as createLegacyClient, type OpencodeClient as LegacyClient } from "@opencode-ai/sdk/client"; -import { createOpencodeClient as createV2Client, type OpencodeClient as V2Client } from "@opencode-ai/sdk/v2"; -import { execFile } from "node:child_process"; -import { promisify } from "node:util"; - -import { - createChangesLoadTool, - createPrLoadTool, - createPrLoadReviewTool, - createPrSyncTool, - createTicketLoadTool, - createTicketSyncTool, - getEnabledToolNames, - type MergedKompassConfig, - type Shell, - type ShellPromise, -} from "../core/index.ts"; -import { loadMergedKompassConfig } from "./cache.ts"; -import { applyAgentsConfig, applyCommandsConfig } from "./config.ts"; -import { createPluginLogger, getErrorDetails, type PluginLogger } from "./logging.ts"; -import { createNavigatorTools, detectNavigatorProtocol, getNavigatorCompatibilityWarning } from "./navigator.ts"; -import { registerRiftWorkspaceAdapter } from "./rift-workspace.ts"; +import { OpenCodeCompassPluginV1 } from "./v1.ts"; import { - getConfiguredOpenCodeToolName, -} from "./tool-names.ts"; - -const execFileAsync = promisify(execFile); - -type CommandExecuteBeforeHook = NonNullable; -type CommandExecuteBeforeInput = Parameters[0]; -type CommandExecuteBeforeOutput = Parameters[1]; -type OpenCodeToolCreator = ( - client: PluginInput["client"], - config: MergedKompassConfig, - projectRoot: string, - shell: Shell, -) => ToolDefinition; - -type ShellResult = ShellPromise & { - stdout: Buffer; -}; - -function shellEscape(value: unknown): string { - return `'${String(value).replaceAll("'", `'\\''`)}'`; -} - -function shellResult(stdout: string, stderr: string, exitCode: number): ShellResult { - return { - cwd: () => { - throw new Error("Cannot change cwd after execution"); - }, - quiet: () => { - throw new Error("Cannot change quiet after execution"); - }, - nothrow: () => { - throw new Error("Cannot change nothrow after execution"); - }, - text: () => stdout, - json: () => JSON.parse(stdout), - exitCode, - stderr: Buffer.from(stderr), - stdout: Buffer.from(stdout), - }; -} - -class NodeShellCommand implements PromiseLike { - #command: string; - #cwd: string; - #quiet = false; - #nothrow = false; - #result?: Promise; - - constructor(command: string, cwd: string) { - this.#command = command; - this.#cwd = cwd; - } - - cwd(dir: string) { - this.#cwd = dir; - return this; - } - - quiet() { - this.#quiet = true; - return this; - } - - nothrow() { - this.#nothrow = true; - return this; - } - - then( - onfulfilled?: ((value: ShellResult) => TResult1 | PromiseLike) | null, - onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, - ) { - return this.run().then(onfulfilled, onrejected); - } - - private run() { - this.#result ??= this.execute(); - return this.#result; - } - - private async execute() { - try { - const { stdout, stderr } = await execFileAsync("/bin/bash", ["-lc", this.#command], { - cwd: this.#cwd, - encoding: "utf8", - maxBuffer: 10 * 1024 * 1024, - }); - - this.write(stdout, stderr); - return shellResult(stdout, stderr, 0); - } catch (error) { - const failed = error as { stdout?: unknown; stderr?: unknown; message?: unknown; code?: unknown }; - const stdout = String(failed.stdout ?? ""); - const stderr = String(failed.stderr ?? failed.message ?? ""); - const exitCode = typeof failed.code === "number" ? failed.code : 1; - const result = shellResult(stdout, stderr, exitCode); - - this.write(stdout, stderr); - if (this.#nothrow) return result; - - throw new Error(stderr || `Command failed: ${this.#command}`); - } - } - - private write(stdout: string, stderr: string) { - if (this.#quiet) return; - if (stdout) process.stdout.write(stdout); - if (stderr) process.stderr.write(stderr); - } -} - -function createNodeShell(defaultDirectory: string): Shell { - return (strings: TemplateStringsArray, ...expressions: unknown[]) => { - let command = strings[0] ?? ""; - - expressions.forEach((expression, index) => { - command += Array.isArray(expression) - ? expression.map((item) => shellEscape(item)).join(" ") - : shellEscape(expression); - command += strings[index + 1] ?? ""; - }); - - return new NodeShellCommand(command, defaultDirectory) as unknown as ShellPromise; - }; -} - -export type CommandExecution = { - command: string; - arguments: string; - prompt: string; + OpenCodeCompassPluginV2, + setupOpenCodeV2, +} from "./v2.ts"; + +export const OpenCodeCompassPlugin = { + id: "kompass", + server: OpenCodeCompassPluginV1, + setup: setupOpenCodeV2, }; -function getString(value: unknown): string | undefined { - return typeof value === "string" ? value : undefined; -} - -function logObservedFailure( - logger: PluginLogger, - message: string, - error: unknown, - extra?: Record, -) { - logger.warn(message, { - ...(extra ?? {}), - ...getErrorDetails(error), - }); -} - -export function getCommandExecution( - input: CommandExecuteBeforeInput, - output: CommandExecuteBeforeOutput, -): CommandExecution | undefined { - const prompt = output.parts - .flatMap((part) => part.type === "text" ? [part.text] : []) - .join("\n") - .trim(); - - if (!prompt) return; - - return { - command: input.command, - arguments: input.arguments, - prompt, - }; -} - -const opencodeToolCreators: Record = { - changes_load(_: PluginInput["client"], __: MergedKompassConfig, _projectRoot: string, shell: Shell) { - const definition = createChangesLoadTool(shell); - return tool({ - description: definition.description, - args: { - base: tool.schema.string().describe("Base branch or ref").optional(), - head: tool.schema.string().describe("Head branch, commit, or ref override").optional(), - depthHint: tool.schema.number().int().positive() - .describe("Optional shallow-fetch hint, such as PR commit count") - .optional(), - uncommitted: tool.schema.boolean() - .describe("Only load uncommitted changes (staged and unstaged), never fall back to branch comparison") - .optional(), - }, - execute: (args, context) => definition.execute(args, context), - }); - }, - pr_load(_: PluginInput["client"], __: MergedKompassConfig, _projectRoot: string, shell: Shell) { - const definition = createPrLoadTool(shell); - return tool({ - description: definition.description, - args: { - pr: tool.schema.string().describe("PR number or URL").optional(), - }, - execute: (args, context) => definition.execute(args, context), - }); - }, - pr_load_review(_: PluginInput["client"], __: MergedKompassConfig, _projectRoot: string, shell: Shell) { - const definition = createPrLoadReviewTool(shell); - return tool({ - description: definition.description, - args: { - pr: tool.schema.string().describe("PR number or URL").optional(), - since: tool.schema.string().describe("Exclusive ISO-8601 review checkpoint"), - }, - execute: (args, context) => definition.execute(args, context), - }); - }, - pr_sync(_: PluginInput["client"], config: MergedKompassConfig, _projectRoot: string, shell: Shell) { - const definition = createPrSyncTool(shell); - - return tool({ - description: definition.description, - args: { - title: tool.schema.string().describe("PR title; required when creating or renaming a PR").optional(), - body: tool.schema.string().describe("PR body override").optional(), - description: tool.schema.string().describe("Short PR description rendered above checklist sections").optional(), - base: tool.schema.string().describe("Base branch to merge into").optional(), - head: tool.schema.string().describe("Head branch to use when creating a PR").optional(), - labels: tool.schema.array(tool.schema.string()).describe("Labels to add to the PR").optional(), - removeLabels: tool.schema.array(tool.schema.string()).describe("Labels to remove from an existing PR").optional(), - replaceLabels: tool.schema.array(tool.schema.string()).describe("Exact label set for the PR; an empty array clears all labels").optional(), - assignees: tool.schema.array(tool.schema.string()).describe("Assignees to apply to the PR").optional(), - checklists: tool.schema.array(tool.schema.object({ - name: tool.schema.string().describe("Checklist section name"), - items: tool.schema.array(tool.schema.object({ - name: tool.schema.string().describe("Checklist item name"), - completed: tool.schema.boolean().describe("Whether the item is completed"), - })).describe("Checklist items"), - })).describe("Checklist sections rendered as markdown").optional(), - draft: tool.schema.boolean().describe("Create as draft PR").optional(), - refUrl: tool.schema.string().describe("Optional PR URL to update").optional(), - commitId: tool.schema.string().describe("Commit SHA to anchor review comments to; omit unless sending review comments").optional(), - review: tool.schema.object({ - body: tool.schema.string().describe("Optional review summary body").optional(), - comments: tool.schema.array(tool.schema.object({ - path: tool.schema.string().describe("Changed file path"), - body: tool.schema.string().describe("Inline review comment body"), - line: tool.schema.number().int().describe("Ending line on the diff side"), - startLine: tool.schema.number().int().describe("Starting line for multi-line comments").optional(), - side: tool.schema.enum(["LEFT", "RIGHT"]).describe("Diff side for the ending line").optional(), - startSide: tool.schema.enum(["LEFT", "RIGHT"]).describe("Diff side for the starting line").optional(), - })).describe("Inline review comments to submit").optional(), - ...(config.shared.prApprove - ? { approve: tool.schema.boolean().describe("Approve the PR with this review comment").optional() } - : {} - ), - }).describe("Optional structured review submission; omit the field entirely unless submitting a review body, inline comments, or approval").optional(), - replies: tool.schema.array(tool.schema.object({ - inReplyTo: tool.schema.number().int().describe("Existing review comment ID to reply to"), - body: tool.schema.string().describe("Reply body"), - })).describe("Replies to existing review comments").optional(), - commentBody: tool.schema.string().describe("General PR comment body").optional(), - }, - execute: (args, context) => definition.execute(args, context), - }); - }, - ticket_sync(_: PluginInput["client"], __: MergedKompassConfig, _projectRoot: string, shell: Shell) { - const definition = createTicketSyncTool(shell); - return tool({ - description: definition.description, - args: { - title: tool.schema.string().describe("Issue title").optional(), - body: tool.schema.string().describe("Issue body override").optional(), - description: tool.schema.string().describe("Issue description rendered above checklist sections").optional(), - labels: tool.schema.array(tool.schema.string()).describe("Labels to apply to the issue").optional(), - assignees: tool.schema.array(tool.schema.string()).describe("Assignees to apply to the issue").optional(), - checklists: tool.schema.array(tool.schema.object({ - name: tool.schema.string().describe("Checklist section name"), - items: tool.schema.array(tool.schema.object({ - name: tool.schema.string().describe("Checklist item name"), - completed: tool.schema.boolean().describe("Whether the item is completed"), - })).describe("Checklist items"), - })).describe("Checklist sections rendered as markdown").optional(), - refUrl: tool.schema.string().describe("Optional issue URL to update").optional(), - comments: tool.schema.array(tool.schema.string()).describe("Optional issue comments to post").optional(), - }, - execute: (args, context) => definition.execute(args, context), - }); - }, - ticket_load(_: PluginInput["client"], __: MergedKompassConfig, _projectRoot: string, shell: Shell) { - const definition = createTicketLoadTool(shell); - return tool({ - description: definition.description, - args: { - source: tool.schema.string().describe("Issue URL, repo#id, #id, file path, or raw text"), - comments: tool.schema.boolean().describe("Include issue comments").optional(), - }, - execute: (args, context) => definition.execute(args, context), - }); - }, -}; - -export async function createOpenCodeTools( - client: PluginInput["client"], - projectRoot: string, - navigator?: { - client: V2Client; - legacyClient: (directory: string) => LegacyClient; - projectID: string; - protocol: "v1" | "v2"; - }, -): Promise> { - const config = await loadMergedKompassConfig(projectRoot); - const tools: Record = {}; - const logger = createPluginLogger(client, projectRoot); - const shell = createNodeShell(projectRoot); - - const navigatorEnabled = config.adapters.opencode.navigator.enabled; - let agentNames: string[] | undefined; - if (navigatorEnabled && navigator) { - try { - const response = await navigator.client.v2.agent.list({ location: { directory: projectRoot } }); - agentNames = response.data?.data.map((agent) => agent.id); - } catch { - // Target-workspace validation remains authoritative if discovery is unavailable during registration. - } - } - const navigatorTools: Record = navigatorEnabled && navigator - ? createNavigatorTools({ - agentNames, - client: navigator.client, - legacyClient: navigator.legacyClient, - projectID: navigator.projectID, - checkout: projectRoot, - config: config.adapters.opencode.navigator, - protocol: navigator.protocol, - }) - : {}; - - for (const toolName of getEnabledToolNames(config.tools, navigatorEnabled)) { - const creator = opencodeToolCreators[toolName as keyof typeof opencodeToolCreators]; - const navigatorTool = navigatorTools[toolName]; - if (creator || navigatorTool) { - const registeredName = getConfiguredOpenCodeToolName(toolName, config.tools[toolName].name); - tools[registeredName] = navigatorTool ?? creator(client, config, projectRoot, shell); - logger.info("Loaded Kompass tool", { - tool: toolName, - registeredName, - }); - } - } - - return tools; -} - -export const OpenCodeCompassPlugin: Plugin = async (input: PluginInput) => { - const { client, worktree } = input; - const logger = createPluginLogger(client, worktree); - - logger.info("Initialized Kompass plugin", { - directory: getString(input.directory), - worktree: getString(worktree), - projectPath: getString((input as { project?: { path?: string } }).project?.path), - }); - - async function createToolsSafely() { - try { - const config = await loadMergedKompassConfig(worktree); - let navigator: { - client: V2Client; - legacyClient: (directory: string) => LegacyClient; - projectID: string; - protocol: "v1" | "v2"; - } | undefined; - if (config.adapters.opencode.navigator.enabled) { - const projectID = getString(input.project?.id); - if (!input.serverUrl || !projectID) { - logger.warn("Navigator requires OpenCode 1.17.12 or newer; Navigator tools were not registered"); - } else { - const legacyClients = new Map(); - const navigatorClient = createV2Client({ baseUrl: input.serverUrl.toString() }); - const protocol = await detectNavigatorProtocol(navigatorClient); - navigator = { - // A client-wide directory would silently hide sessions in managed worktrees. - client: navigatorClient, - legacyClient(directory) { - let located = legacyClients.get(directory); - if (!located) { - located = createLegacyClient({ baseUrl: input.serverUrl!.toString(), directory }); - legacyClients.set(directory, located); - } - return located; - }, - projectID, - protocol, - }; - const warning = await getNavigatorCompatibilityWarning( - navigator.client, - navigator.protocol, - navigator.legacyClient(worktree), - ); - if (warning) { - logger.warn(`${warning}; Navigator tools were not registered`); - navigator = undefined; - } - } - } - return await createOpenCodeTools(client, worktree, navigator); - } catch (error) { - logger.warn("Skipping Kompass tool registration", { - ...getErrorDetails(error), - }); - return {}; - } - } - - async function runConfigStep(name: string, register: () => Promise) { - try { - await register(); - } catch (error) { - logger.warn("Skipping Kompass config registration step", { - step: name, - worktree, - ...getErrorDetails(error), - }); - } - } - - const tools = await createToolsSafely(); - await registerRiftWorkspaceAdapter(input as never, logger); - - return { - tool: tools, - async config(cfg) { - await runConfigStep("agents", () => applyAgentsConfig(cfg, worktree, { logger })); - await runConfigStep("commands", () => applyCommandsConfig(cfg, worktree, { logger })); - }, - async "command.execute.before"(input, output) { - try { - const commandExecution = getCommandExecution(input, output); - - if (!commandExecution) return; - - logger.info("Executing Kompass command", commandExecution as Record); - } catch (error) { - logObservedFailure(logger, "command.execute.before hook failed", error, { - command: input.command, - arguments: input.arguments, - sessionID: input.sessionID, - }); - } - }, - }; +export { + OpenCodeCompassPluginV1, + OpenCodeCompassPluginV2, + setupOpenCodeV2, }; - -export { applyAgentsConfig, applyCommandsConfig }; +export { + applyAgentsConfig, + applyCommandsConfig, + createOpenCodeTools, + getCommandExecution, +} from "./v1.ts"; export default OpenCodeCompassPlugin; diff --git a/packages/opencode/legacy-plugin.ts b/packages/opencode/legacy-plugin.ts new file mode 100644 index 0000000..4096dfb --- /dev/null +++ b/packages/opencode/legacy-plugin.ts @@ -0,0 +1,31 @@ +import type { Config as SDKConfig, Project, createOpencodeClient } from "@opencode-ai/sdk"; +import type { ToolDefinition } from "./shared-tools.ts"; + +export { tool, type ToolContext, type ToolDefinition, type ToolResult } from "./shared-tools.ts"; + +export type Config = Omit & { + plugin?: Array]>; +}; + +export type PluginInput = { + client: ReturnType; + project: Project; + directory: string; + worktree: string; + experimental_workspace: { + register(type: string, adapter: unknown): void; + }; + serverUrl: URL; + $: unknown; +}; + +export type Hooks = { + config?: (config: Config) => Promise; + tool?: Record; + "command.execute.before"?: ( + input: { command: string; sessionID: string; arguments: string }, + output: { parts: Array<{ type: string; text?: string; [key: string]: unknown }> }, + ) => Promise; +}; + +export type Plugin = (input: PluginInput, options?: Record) => Promise; diff --git a/packages/opencode/logging.ts b/packages/opencode/logging.ts index aedfaed..70eece3 100644 --- a/packages/opencode/logging.ts +++ b/packages/opencode/logging.ts @@ -1,4 +1,4 @@ -import type { PluginInput } from "@opencode-ai/plugin"; +import type { PluginInput } from "./legacy-plugin.ts"; type LogLevel = "debug" | "info" | "warn" | "error"; @@ -21,9 +21,23 @@ export function getErrorDetails(error: unknown): Record { } export function createPluginLogger( - client: PluginInput["client"], - directory: string, + clientOrDirectory: PluginInput["client"] | string, + directory = typeof clientOrDirectory === "string" ? clientOrDirectory : "", ): PluginLogger { + if (typeof clientOrDirectory === "string") { + function write(level: LogLevel, message: string, extra?: Record) { + const output = extra ? [`[kompass] ${message}`, { directory, ...extra }] : [`[kompass] ${message}`]; + console[level](...output); + } + return { + debug: (message, extra) => write("debug", message, extra), + info: (message, extra) => write("info", message, extra), + warn: (message, extra) => write("warn", message, extra), + error: (message, extra) => write("error", message, extra), + }; + } + const client = clientOrDirectory; + function dispatch(level: LogLevel, message: string, extra?: Record) { void client.app.log({ query: { directory }, @@ -33,7 +47,7 @@ export function createPluginLogger( message: `[kompass] ${message}`, ...(extra ? { extra } : {}), }, - }).catch((err) => { + }).catch((err: unknown) => { // Log to console for debugging; plugin behavior must not depend on logging. console.error("[kompass] Log write failed:", err); }); diff --git a/packages/opencode/navigator.ts b/packages/opencode/navigator.ts index 8f79a60..e03a655 100644 --- a/packages/opencode/navigator.ts +++ b/packages/opencode/navigator.ts @@ -1,5 +1,4 @@ -import type { ToolContext, ToolDefinition } from "@opencode-ai/plugin/tool"; -import { tool } from "@opencode-ai/plugin/tool"; +import { tool, type ToolContext, type ToolDefinition } from "./legacy-plugin.ts"; import type { Message as LegacyMessage, OpencodeClient as LegacyOpencodeClient, diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b1c0bca..562d271 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -46,16 +46,13 @@ "bugs": { "url": "https://github.com/kompassdev/kompass/issues" }, - "peerDependencies": { - "@opencode-ai/plugin": "^1.18.7", - "@opencode-ai/sdk": "^1.18.7" - }, "dependencies": { - "rift-snapshot": "^0.0.10" + "@opencode-ai/plugin": "0.0.0-beta-18684", + "@opencode-ai/sdk": "^1.18.7", + "rift-snapshot": "^0.0.10", + "zod": "^4.1.8" }, "devDependencies": { - "@opencode-ai/plugin": "^1.18.7", - "@opencode-ai/sdk": "^1.18.7", "yaml": "^2.8.2" } } diff --git a/packages/opencode/plugin.ts b/packages/opencode/plugin.ts index 74c8790..a59dc17 100644 --- a/packages/opencode/plugin.ts +++ b/packages/opencode/plugin.ts @@ -1 +1,7 @@ -export { OpenCodeCompassPlugin, OpenCodeCompassPlugin as default } from "./index.ts"; +export { + OpenCodeCompassPlugin, + OpenCodeCompassPlugin as default, + OpenCodeCompassPluginV1, + OpenCodeCompassPluginV2, + setupOpenCodeV2, +} from "./index.ts"; diff --git a/packages/opencode/scripts/build.ts b/packages/opencode/scripts/build.ts index 300c46b..e250a71 100644 --- a/packages/opencode/scripts/build.ts +++ b/packages/opencode/scripts/build.ts @@ -15,7 +15,6 @@ const packageReadme = path.join(packageRoot, "README.md"); const runtimeDirs = ["agents", "commands", "components"] as const; const bundleExternals = [ "@opencode-ai/plugin", - "@opencode-ai/plugin/tool", "@opencode-ai/sdk/v2", ] as const; const bundleArgs = [ diff --git a/packages/opencode/shared-tools.ts b/packages/opencode/shared-tools.ts new file mode 100644 index 0000000..411f2a6 --- /dev/null +++ b/packages/opencode/shared-tools.ts @@ -0,0 +1,301 @@ +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { z } from "zod"; + +import { + createChangesLoadTool, + createPrLoadReviewTool, + createPrLoadTool, + createPrSyncTool, + createTicketLoadTool, + createTicketSyncTool, + type MergedKompassConfig, + type Shell, + type ShellPromise, +} from "../core/index.ts"; + +export type ToolContext = { + sessionID: string; + messageID: string; + agent: string; + directory: string; + worktree: string; + abort: AbortSignal; + metadata(input: { title?: string; metadata?: Record }): void; + ask(input: { + permission: string; + patterns: string[]; + always: string[]; + metadata: Record; + }): Promise; +}; + +export type ToolResult = string | { + title?: string; + output: string; + metadata?: Record; +}; + +export function tool(input: { + description: string; + args: Args; + execute(args: z.infer>, context: ToolContext): Promise; +}) { + return input; +} + +tool.schema = z; + +export type ToolDefinition = ReturnType; + +const execFileAsync = promisify(execFile); + +type OpenCodeToolCreator = ( + config: MergedKompassConfig, + shell: Shell, +) => ToolDefinition; + +type ShellResult = ShellPromise & { + stdout: Buffer; +}; + +function shellEscape(value: unknown): string { + return `'${String(value).replaceAll("'", `'\\''`)}'`; +} + +function shellResult(stdout: string, stderr: string, exitCode: number): ShellResult { + return { + cwd: () => { + throw new Error("Cannot change cwd after execution"); + }, + quiet: () => { + throw new Error("Cannot change quiet after execution"); + }, + nothrow: () => { + throw new Error("Cannot change nothrow after execution"); + }, + text: () => stdout, + json: () => JSON.parse(stdout), + exitCode, + stderr: Buffer.from(stderr), + stdout: Buffer.from(stdout), + }; +} + +class NodeShellCommand implements PromiseLike { + #command: string; + #cwd: string; + #quiet = false; + #nothrow = false; + #result?: Promise; + + constructor(command: string, cwd: string) { + this.#command = command; + this.#cwd = cwd; + } + + cwd(dir: string) { + this.#cwd = dir; + return this; + } + + quiet() { + this.#quiet = true; + return this; + } + + nothrow() { + this.#nothrow = true; + return this; + } + + then( + onfulfilled?: ((value: ShellResult) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ) { + return this.run().then(onfulfilled, onrejected); + } + + private run() { + this.#result ??= this.execute(); + return this.#result; + } + + private async execute() { + try { + const { stdout, stderr } = await execFileAsync("/bin/bash", ["-lc", this.#command], { + cwd: this.#cwd, + encoding: "utf8", + maxBuffer: 10 * 1024 * 1024, + }); + + this.write(stdout, stderr); + return shellResult(stdout, stderr, 0); + } catch (error) { + const failed = error as { stdout?: unknown; stderr?: unknown; message?: unknown; code?: unknown }; + const stdout = String(failed.stdout ?? ""); + const stderr = String(failed.stderr ?? failed.message ?? ""); + const exitCode = typeof failed.code === "number" ? failed.code : 1; + const result = shellResult(stdout, stderr, exitCode); + + this.write(stdout, stderr); + if (this.#nothrow) return result; + + throw new Error(stderr || `Command failed: ${this.#command}`); + } + } + + private write(stdout: string, stderr: string) { + if (this.#quiet) return; + if (stdout) process.stdout.write(stdout); + if (stderr) process.stderr.write(stderr); + } +} + +export function createNodeShell(defaultDirectory: string): Shell { + return (strings: TemplateStringsArray, ...expressions: unknown[]) => { + let command = strings[0] ?? ""; + + expressions.forEach((expression, index) => { + command += Array.isArray(expression) + ? expression.map((item) => shellEscape(item)).join(" ") + : shellEscape(expression); + command += strings[index + 1] ?? ""; + }); + + return new NodeShellCommand(command, defaultDirectory) as unknown as ShellPromise; + }; +} + +const opencodeToolCreators: Record = { + changes_load(_: MergedKompassConfig, shell: Shell) { + const definition = createChangesLoadTool(shell); + return tool({ + description: definition.description, + args: { + base: tool.schema.string().describe("Base branch or ref").optional(), + head: tool.schema.string().describe("Head branch, commit, or ref override").optional(), + depthHint: tool.schema.number().int().positive() + .describe("Optional shallow-fetch hint, such as PR commit count") + .optional(), + uncommitted: tool.schema.boolean() + .describe("Only load uncommitted changes (staged and unstaged), never fall back to branch comparison") + .optional(), + }, + execute: (args, context) => definition.execute(args, context), + }); + }, + pr_load(_: MergedKompassConfig, shell: Shell) { + const definition = createPrLoadTool(shell); + return tool({ + description: definition.description, + args: { + pr: tool.schema.string().describe("PR number or URL").optional(), + }, + execute: (args, context) => definition.execute(args, context), + }); + }, + pr_load_review(_: MergedKompassConfig, shell: Shell) { + const definition = createPrLoadReviewTool(shell); + return tool({ + description: definition.description, + args: { + pr: tool.schema.string().describe("PR number or URL").optional(), + since: tool.schema.string().describe("Exclusive ISO-8601 review checkpoint"), + }, + execute: (args, context) => definition.execute(args, context), + }); + }, + pr_sync(config: MergedKompassConfig, shell: Shell) { + const definition = createPrSyncTool(shell); + return tool({ + description: definition.description, + args: { + title: tool.schema.string().describe("PR title; required when creating or renaming a PR").optional(), + body: tool.schema.string().describe("PR body override").optional(), + description: tool.schema.string().describe("Short PR description rendered above checklist sections").optional(), + base: tool.schema.string().describe("Base branch to merge into").optional(), + head: tool.schema.string().describe("Head branch to use when creating a PR").optional(), + labels: tool.schema.array(tool.schema.string()).describe("Labels to add to the PR").optional(), + removeLabels: tool.schema.array(tool.schema.string()).describe("Labels to remove from an existing PR").optional(), + replaceLabels: tool.schema.array(tool.schema.string()).describe("Exact label set for the PR; an empty array clears all labels").optional(), + assignees: tool.schema.array(tool.schema.string()).describe("Assignees to apply to the PR").optional(), + checklists: tool.schema.array(tool.schema.object({ + name: tool.schema.string().describe("Checklist section name"), + items: tool.schema.array(tool.schema.object({ + name: tool.schema.string().describe("Checklist item name"), + completed: tool.schema.boolean().describe("Whether the item is completed"), + })).describe("Checklist items"), + })).describe("Checklist sections rendered as markdown").optional(), + draft: tool.schema.boolean().describe("Create as draft PR").optional(), + refUrl: tool.schema.string().describe("Optional PR URL to update").optional(), + commitId: tool.schema.string().describe("Commit SHA to anchor review comments to; omit unless sending review comments").optional(), + review: tool.schema.object({ + body: tool.schema.string().describe("Optional review summary body").optional(), + comments: tool.schema.array(tool.schema.object({ + path: tool.schema.string().describe("Changed file path"), + body: tool.schema.string().describe("Inline review comment body"), + line: tool.schema.number().int().describe("Ending line on the diff side"), + startLine: tool.schema.number().int().describe("Starting line for multi-line comments").optional(), + side: tool.schema.enum(["LEFT", "RIGHT"]).describe("Diff side for the ending line").optional(), + startSide: tool.schema.enum(["LEFT", "RIGHT"]).describe("Diff side for the starting line").optional(), + })).describe("Inline review comments to submit").optional(), + ...(config.shared.prApprove + ? { approve: tool.schema.boolean().describe("Approve the PR with this review comment").optional() } + : {} + ), + }).describe("Optional structured review submission; omit the field entirely unless submitting a review body, inline comments, or approval").optional(), + replies: tool.schema.array(tool.schema.object({ + inReplyTo: tool.schema.number().int().describe("Existing review comment ID to reply to"), + body: tool.schema.string().describe("Reply body"), + })).describe("Replies to existing review comments").optional(), + commentBody: tool.schema.string().describe("General PR comment body").optional(), + }, + execute: (args, context) => definition.execute(args, context), + }); + }, + ticket_sync(_: MergedKompassConfig, shell: Shell) { + const definition = createTicketSyncTool(shell); + return tool({ + description: definition.description, + args: { + title: tool.schema.string().describe("Issue title").optional(), + body: tool.schema.string().describe("Issue body override").optional(), + description: tool.schema.string().describe("Issue description rendered above checklist sections").optional(), + labels: tool.schema.array(tool.schema.string()).describe("Labels to apply to the issue").optional(), + assignees: tool.schema.array(tool.schema.string()).describe("Assignees to apply to the issue").optional(), + checklists: tool.schema.array(tool.schema.object({ + name: tool.schema.string().describe("Checklist section name"), + items: tool.schema.array(tool.schema.object({ + name: tool.schema.string().describe("Checklist item name"), + completed: tool.schema.boolean().describe("Whether the item is completed"), + })).describe("Checklist items"), + })).describe("Checklist sections rendered as markdown").optional(), + refUrl: tool.schema.string().describe("Optional issue URL to update").optional(), + comments: tool.schema.array(tool.schema.string()).describe("Optional issue comments to post").optional(), + }, + execute: (args, context) => definition.execute(args, context), + }); + }, + ticket_load(_: MergedKompassConfig, shell: Shell) { + const definition = createTicketLoadTool(shell); + return tool({ + description: definition.description, + args: { + source: tool.schema.string().describe("Issue URL, repo#id, #id, file path, or raw text"), + comments: tool.schema.boolean().describe("Include issue comments").optional(), + }, + execute: (args, context) => definition.execute(args, context), + }); + }, +}; + +export function createCoreOpenCodeToolDefinitions( + config: MergedKompassConfig, + projectRoot: string, +): Record { + const shell = createNodeShell(projectRoot); + return Object.fromEntries( + Object.entries(opencodeToolCreators).map(([name, create]) => [name, create(config, shell)]), + ); +} diff --git a/packages/opencode/test/agents-config.test.ts b/packages/opencode/test/agents-config.test.ts index 7c5f9c9..80875e2 100644 --- a/packages/opencode/test/agents-config.test.ts +++ b/packages/opencode/test/agents-config.test.ts @@ -45,7 +45,6 @@ describe("applyAgentsConfig", () => { todowrite: "allow", }); assert.equal(cfg.agent.worker?.prompt, undefined); - assert.equal(cfg.agent.navigator, undefined); assert.match(cfg.agent.reviewer?.prompt ?? "", /Never switch branches/i); }); diff --git a/packages/opencode/test/plugin-entry.test.ts b/packages/opencode/test/plugin-entry.test.ts index 8682437..b6b234b 100644 --- a/packages/opencode/test/plugin-entry.test.ts +++ b/packages/opencode/test/plugin-entry.test.ts @@ -2,13 +2,23 @@ import { describe, test } from "node:test"; import assert from "node:assert/strict"; describe("plugin entry", () => { - test("only exposes plugin factories", async () => { + test("exposes one dual-version plugin definition", async () => { const mod = await import("../plugin.ts"); assert.deepEqual( Object.keys(mod).sort(), - ["OpenCodeCompassPlugin", "default"], + [ + "OpenCodeCompassPlugin", + "OpenCodeCompassPluginV1", + "OpenCodeCompassPluginV2", + "default", + "setupOpenCodeV2", + ], ); assert.equal(mod.default, mod.OpenCodeCompassPlugin); + assert.equal(mod.default.server, mod.OpenCodeCompassPluginV1); + assert.equal(mod.default.setup, mod.setupOpenCodeV2); + assert.equal(mod.default.id, "kompass"); + assert.equal(mod.OpenCodeCompassPluginV2.setup, mod.setupOpenCodeV2); }); }); diff --git a/packages/opencode/test/tool-registration.test.ts b/packages/opencode/test/tool-registration.test.ts index d9f8562..0f81cb8 100644 --- a/packages/opencode/test/tool-registration.test.ts +++ b/packages/opencode/test/tool-registration.test.ts @@ -6,7 +6,7 @@ import os from "node:os"; import path from "node:path"; import { promisify } from "node:util"; -import { createOpenCodeTools, OpenCodeCompassPlugin } from "../index.ts"; +import { createOpenCodeTools, OpenCodeCompassPluginV1 } from "../index.ts"; import { createRiftWorkspaceAdapter, resolveRiftSourceDirectory } from "../rift-workspace.ts"; const execFileAsync = promisify(execFile); @@ -535,7 +535,7 @@ describe("createOpenCodeTools", () => { const tempDir = await createTempGitRepo(); try { - const plugin = await OpenCodeCompassPlugin({ + const plugin = await OpenCodeCompassPluginV1({ client: createMockClient() as never, directory: tempDir, worktree: tempDir, diff --git a/packages/opencode/test/v2-plugin.test.ts b/packages/opencode/test/v2-plugin.test.ts new file mode 100644 index 0000000..9be38ba --- /dev/null +++ b/packages/opencode/test/v2-plugin.test.ts @@ -0,0 +1,127 @@ +import { describe, test } from "node:test"; +import assert from "node:assert/strict"; + +import { + OpenCodeCompassPluginV2, + setupOpenCodeV2, +} from "../index.ts"; +import { expandCommand } from "../v2.ts"; + +describe("OpenCode v2 plugin", () => { + test("exports the v2 plugin definition", () => { + assert.equal(OpenCodeCompassPluginV2.id, "kompass"); + assert.equal(OpenCodeCompassPluginV2.setup, setupOpenCodeV2); + }); + + test("inlines output from failing shell substitutions", async () => { + const prompt = await expandCommand( + "Result: !`printf stdout; printf stderr >&2; exit 1`", + "", + process.cwd(), + ); + + assert.equal(prompt, "Result: stdoutstderr"); + }); + + test("registers location-scoped agents, commands, and tools", async () => { + const agents: string[] = []; + const commands = new Map }>(); + const tools = new Map(); + const prompts: Array> = []; + const switchedAgents: string[] = []; + const createdSessions: Array> = []; + const disposed: string[] = []; + const availableAgents = new Map([ + ["worker", { id: "worker", permissions: [] as unknown[] }], + ["planner", { id: "planner", permissions: [] as unknown[] }], + ["reviewer", { id: "reviewer", permissions: [] as unknown[] }], + ]); + const context = { + location: { directory: process.cwd() }, + agent: { + transform: async (transform: (draft: unknown) => void) => { + transform({ + get(name: string) { + return availableAgents.get(name); + }, + update(name: string, update: (agent: Record) => void) { + agents.push(name); + update(availableAgents.get(name) as Record); + }, + }); + return { dispose: async () => { disposed.push("agent"); } }; + }, + reload: async () => {}, + }, + command: { + transform: async (transform: (draft: unknown) => void) => { + transform({ + add(command: { name: string; execute(input: unknown): Promise }) { + commands.set(command.name, command); + }, + }); + return { dispose: async () => { disposed.push("command"); } }; + }, + reload: async () => {}, + }, + tool: { + transform: async (transform: (draft: unknown) => void) => { + transform({ + add(tool: { name: string; input: unknown }) { + tools.set(tool.name, tool); + }, + }); + return { dispose: async () => { disposed.push("tool"); } }; + }, + reload: async () => {}, + }, + session: { + create: async (input: Record) => { + createdSessions.push(input); + return { id: "child-1" }; + }, + switchAgent: async ({ agent }: { agent: string }) => { + switchedAgents.push(agent); + }, + prompt: async (input: Record) => { + prompts.push(input); + }, + }, + }; + + const cleanup = await setupOpenCodeV2(context as never); + assert.ok(agents.includes("worker")); + assert.ok(commands.has("dev")); + assert.deepEqual([...tools.keys()].sort(), [ + "kompass_changes_load", + "kompass_pr_load", + "kompass_pr_load_review", + "kompass_pr_sync", + "kompass_ticket_load", + "kompass_ticket_sync", + ]); + assert.equal(typeof (tools.get("kompass_changes_load")?.input as any)?.["~standard"]?.validate, "function"); + + await commands.get("dev")?.execute({ + sessionID: "session-1", + prompt: { text: "fix auth" }, + delivery: "steer", + }); + assert.equal(createdSessions.length, 1); + assert.equal(createdSessions[0]?.agent, "worker"); + assert.equal(switchedAgents.length, 0); + assert.equal(prompts[0]?.sessionID, "child-1"); + assert.match(String(prompts[0]?.text), /fix auth/); + + await commands.get("learn")?.execute({ + sessionID: "session-1", + prompt: { text: "remember this" }, + delivery: "steer", + }); + assert.deepEqual(switchedAgents, ["worker"]); + assert.equal(prompts[1]?.sessionID, "session-1"); + + await cleanup(); + assert.deepEqual(disposed.sort(), ["agent", "command", "tool"]); + }); +}); diff --git a/packages/opencode/v1.ts b/packages/opencode/v1.ts new file mode 100644 index 0000000..f4ac1af --- /dev/null +++ b/packages/opencode/v1.ts @@ -0,0 +1,214 @@ +import type { Hooks, Plugin, PluginInput } from "./legacy-plugin.ts"; +import { createOpencodeClient as createLegacyClient, type OpencodeClient as LegacyClient } from "@opencode-ai/sdk/client"; +import { createOpencodeClient as createV2Client, type OpencodeClient as V2Client } from "@opencode-ai/sdk/v2"; + +import { + getEnabledToolNames, +} from "../core/index.ts"; +import { loadMergedKompassConfig } from "./cache.ts"; +import { applyAgentsConfig, applyCommandsConfig } from "./config.ts"; +import { createPluginLogger, getErrorDetails, type PluginLogger } from "./logging.ts"; +import { createNavigatorTools, detectNavigatorProtocol, getNavigatorCompatibilityWarning } from "./navigator.ts"; +import { registerRiftWorkspaceAdapter } from "./rift-workspace.ts"; +import { createCoreOpenCodeToolDefinitions, type ToolDefinition } from "./shared-tools.ts"; +import { getConfiguredOpenCodeToolName } from "./tool-names.ts"; + +export { createCoreOpenCodeToolDefinitions, createNodeShell } from "./shared-tools.ts"; + +type CommandExecuteBeforeHook = NonNullable; +type CommandExecuteBeforeInput = Parameters[0]; +type CommandExecuteBeforeOutput = Parameters[1]; +export type CommandExecution = { + command: string; + arguments: string; + prompt: string; +}; + +function getString(value: unknown): string | undefined { + return typeof value === "string" ? value : undefined; +} + +function logObservedFailure( + logger: PluginLogger, + message: string, + error: unknown, + extra?: Record, +) { + logger.warn(message, { + ...(extra ?? {}), + ...getErrorDetails(error), + }); +} + +export function getCommandExecution( + input: CommandExecuteBeforeInput, + output: CommandExecuteBeforeOutput, +): CommandExecution | undefined { + const prompt = output.parts + .flatMap((part) => part.type === "text" ? [part.text] : []) + .join("\n") + .trim(); + + if (!prompt) return; + + return { + command: input.command, + arguments: input.arguments, + prompt, + }; +} + +export async function createOpenCodeTools( + client: PluginInput["client"], + projectRoot: string, + navigator?: { + client: V2Client; + legacyClient: (directory: string) => LegacyClient; + projectID: string; + protocol: "v1" | "v2"; + }, +): Promise> { + const config = await loadMergedKompassConfig(projectRoot); + const tools: Record = {}; + const logger = createPluginLogger(client, projectRoot); + const coreTools = createCoreOpenCodeToolDefinitions(config, projectRoot); + + const navigatorEnabled = config.adapters.opencode.navigator.enabled; + let agentNames: string[] | undefined; + if (navigatorEnabled && navigator) { + try { + const response = await navigator.client.v2.agent.list({ location: { directory: projectRoot } }); + agentNames = response.data?.data.map((agent) => agent.id); + } catch { + // Target-workspace validation remains authoritative if discovery is unavailable during registration. + } + } + const navigatorTools: Record = navigatorEnabled && navigator + ? createNavigatorTools({ + agentNames, + client: navigator.client, + legacyClient: navigator.legacyClient, + projectID: navigator.projectID, + checkout: projectRoot, + config: config.adapters.opencode.navigator, + protocol: navigator.protocol, + }) + : {}; + + for (const toolName of getEnabledToolNames(config.tools, navigatorEnabled)) { + const coreTool = coreTools[toolName]; + const navigatorTool = navigatorTools[toolName]; + if (coreTool || navigatorTool) { + const registeredName = getConfiguredOpenCodeToolName(toolName, config.tools[toolName].name); + tools[registeredName] = navigatorTool ?? coreTool; + logger.info("Loaded Kompass tool", { + tool: toolName, + registeredName, + }); + } + } + + return tools; +} + +export const OpenCodeCompassPluginV1: Plugin = async (input: PluginInput) => { + const { client, worktree } = input; + const logger = createPluginLogger(client, worktree); + + logger.info("Initialized Kompass plugin", { + directory: getString(input.directory), + worktree: getString(worktree), + projectPath: getString((input as { project?: { path?: string } }).project?.path), + }); + + async function createToolsSafely() { + try { + const config = await loadMergedKompassConfig(worktree); + let navigator: { + client: V2Client; + legacyClient: (directory: string) => LegacyClient; + projectID: string; + protocol: "v1" | "v2"; + } | undefined; + if (config.adapters.opencode.navigator.enabled) { + const projectID = getString(input.project?.id); + if (!input.serverUrl || !projectID) { + logger.warn("Navigator requires OpenCode 1.17.12 or newer; Navigator tools were not registered"); + } else { + const legacyClients = new Map(); + const navigatorClient = createV2Client({ baseUrl: input.serverUrl.toString() }); + const protocol = await detectNavigatorProtocol(navigatorClient); + navigator = { + client: navigatorClient, + legacyClient(directory) { + let located = legacyClients.get(directory); + if (!located) { + located = createLegacyClient({ baseUrl: input.serverUrl.toString(), directory }); + legacyClients.set(directory, located); + } + return located; + }, + projectID, + protocol, + }; + const warning = await getNavigatorCompatibilityWarning( + navigator.client, + navigator.protocol, + navigator.legacyClient(worktree), + ); + if (warning) { + logger.warn(`${warning}; Navigator tools were not registered`); + navigator = undefined; + } + } + } + return await createOpenCodeTools(client, worktree, navigator); + } catch (error) { + logger.warn("Skipping Kompass tool registration", { + ...getErrorDetails(error), + }); + return {}; + } + } + + async function runConfigStep(name: string, register: () => Promise) { + try { + await register(); + } catch (error) { + logger.warn("Skipping Kompass config registration step", { + step: name, + worktree, + ...getErrorDetails(error), + }); + } + } + + const tools = await createToolsSafely(); + await registerRiftWorkspaceAdapter(input, logger); + + return { + tool: tools, + async config(cfg) { + await runConfigStep("agents", () => applyAgentsConfig(cfg, worktree, { logger })); + await runConfigStep("commands", () => applyCommandsConfig(cfg, worktree, { logger })); + }, + async "command.execute.before"(input, output) { + try { + const commandExecution = getCommandExecution(input, output); + + if (!commandExecution) return; + + logger.info("Executing Kompass command", commandExecution as Record); + } catch (error) { + logObservedFailure(logger, "command.execute.before hook failed", error, { + command: input.command, + arguments: input.arguments, + sessionID: input.sessionID, + }); + } + }, + }; +}; + +export { applyAgentsConfig, applyCommandsConfig }; +export default OpenCodeCompassPluginV1; diff --git a/packages/opencode/v2.ts b/packages/opencode/v2.ts new file mode 100644 index 0000000..b007eac --- /dev/null +++ b/packages/opencode/v2.ts @@ -0,0 +1,175 @@ +import { Plugin } from "@opencode-ai/plugin"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { z } from "zod"; + +import { + getEnabledToolNames, + type ResolvedAgentDefinition, + type ResolvedCommandDefinition, +} from "../core/index.ts"; +import { + loadMergedKompassConfig, + loadResolvedAgents, + loadResolvedCommands, +} from "./cache.ts"; +import { createPluginLogger } from "./logging.ts"; +import { createCoreOpenCodeToolDefinitions } from "./shared-tools.ts"; +import { getConfiguredOpenCodeToolName } from "./tool-names.ts"; + +type PluginContext = Plugin.Context; +type ToolDraft = Parameters[0]>[0]; +type OpenCodeTool = Parameters[0]; +const execFileAsync = promisify(execFile); + +function toOpenCodeTool( + name: string, + definition: ReturnType[string], + projectRoot: string, +): OpenCodeTool { + return { + name, + description: definition.description, + input: z.object(definition.args) as OpenCodeTool["input"], + options: { codemode: false }, + async execute(args) { + const result = await definition.execute(args as never, { + worktree: projectRoot, + directory: projectRoot, + } as never); + const content = typeof result === "string" ? result : result.output; + return { output: JSON.parse(content), content }; + }, + }; +} + +export async function createOpenCodeV2Tools(projectRoot: string): Promise { + const config = await loadMergedKompassConfig(projectRoot); + const definitions = createCoreOpenCodeToolDefinitions(config, projectRoot); + + return getEnabledToolNames(config.tools).flatMap((toolName) => { + const definition = definitions[toolName]; + if (!definition) return []; + const name = getConfiguredOpenCodeToolName(toolName, config.tools[toolName].name); + return [toOpenCodeTool(name, definition, projectRoot)]; + }); +} + +function applyAgents( + draft: Parameters[0]>[0], + agents: Record, +) { + for (const [name, definition] of Object.entries(agents)) { + if (!draft.get(name)) { + throw new Error(`OpenCode v2 agent "${name}" must be registered before Kompass setup`); + } + draft.update(name, (agent) => { + agent.description = definition.description; + agent.permissions.push(...Object.entries(definition.permission).map(([action, effect]) => ({ + action, + resource: "*", + effect: effect as "allow" | "ask" | "deny", + }))); + agent.system = definition.prompt; + if (definition.mode) agent.mode = definition.mode; + }); + } +} + +const argumentPattern = /(?:\[Image\s+\d+\]|"[^"]*"|'[^']*'|[^\s"']+)/gi; +const placeholderPattern = /\$(\d+)/g; +const shellPattern = /!`([^`]+)`/g; + +export async function expandCommand(template: string, input: string, projectRoot: string) { + const args = (input.match(argumentPattern) ?? []).map((arg) => arg.replace(/^['"]|['"]$/g, "")); + const placeholders = template.match(placeholderPattern) ?? []; + const last = Math.max(0, ...placeholders.map((item) => Number(item.slice(1)))); + const expanded = template.replaceAll(placeholderPattern, (_, rawIndex: string) => { + const index = Number(rawIndex) - 1; + if (index >= args.length) return ""; + return index + 1 === last ? args.slice(index).join(" ") : args[index] ?? ""; + }); + const withArguments = expanded.replaceAll("$ARGUMENTS", input); + const text = placeholders.length === 0 && !template.includes("$ARGUMENTS") && input.trim() + ? `${withArguments}\n\n${input}`.trim() + : withArguments.trim(); + const matches = Array.from(text.matchAll(shellPattern)); + const outputs = await Promise.all(matches.map(async (match) => { + try { + const { stdout, stderr } = await execFileAsync("/bin/bash", ["-lc", match[1] ?? ""], { + cwd: projectRoot, + encoding: "utf8", + maxBuffer: 10 * 1024 * 1024, + }); + return `${stdout}${stderr}`; + } catch (error) { + const failed = error as { stdout?: unknown; stderr?: unknown; message?: unknown }; + return `${String(failed.stdout ?? "")}${String(failed.stderr ?? failed.message ?? "")}`; + } + })); + const iterator = outputs[Symbol.iterator](); + return text.replace(shellPattern, () => iterator.next().value ?? ""); +} + +function applyCommands( + draft: Parameters[0]>[0], + commands: Record, + ctx: PluginContext, + projectRoot: string, +) { + for (const [name, definition] of Object.entries(commands)) { + draft.add({ + name, + description: definition.description, + async execute({ sessionID, prompt, delivery }) { + let targetSessionID = sessionID; + if (definition.subtask) { + const session = await ctx.session.create({ + agent: definition.agent, + location: { + directory: projectRoot, + ...(ctx.location.workspaceID ? { workspaceID: ctx.location.workspaceID } : {}), + }, + }); + targetSessionID = session.id as typeof sessionID; + } else if (definition.agent) { + await ctx.session.switchAgent({ sessionID, agent: definition.agent }); + } + await ctx.session.prompt({ + ...prompt, + sessionID: targetSessionID, + text: await expandCommand(definition.template, prompt.text, projectRoot), + delivery, + }); + }, + }); + } +} + +export async function setupOpenCodeV2(ctx: PluginContext) { + const projectRoot = ctx.location.directory; + const logger = createPluginLogger(projectRoot); + const [agents, commands, tools] = await Promise.all([ + loadResolvedAgents(projectRoot), + loadResolvedCommands(projectRoot), + createOpenCodeV2Tools(projectRoot), + ]); + const agentRegistration = await ctx.agent.transform((draft) => applyAgents(draft, agents)); + const commandRegistration = await ctx.command.transform((draft) => applyCommands(draft, commands, ctx, projectRoot)); + const toolRegistration = await ctx.tool.transform((draft) => { + for (const tool of tools) draft.add(tool); + }); + logger.info("Initialized Kompass v2 plugin", { directory: projectRoot, tools: tools.length }); + return async () => { + await Promise.all([ + agentRegistration.dispose(), + commandRegistration.dispose(), + toolRegistration.dispose(), + ]); + }; +} + +export const OpenCodeCompassPluginV2 = Plugin.define({ + id: "kompass", + setup: setupOpenCodeV2, +}); diff --git a/packages/web/src/content/docs/docs/adapters/opencode.mdx b/packages/web/src/content/docs/docs/adapters/opencode.mdx index 74597aa..9979755 100644 --- a/packages/web/src/content/docs/docs/adapters/opencode.mdx +++ b/packages/web/src/content/docs/docs/adapters/opencode.mdx @@ -7,6 +7,10 @@ description: Use Kompass workflows inside OpenCode through the published adapter The OpenCode adapter package is `@kompassdev/opencode`. +It exports one definition for both plugin APIs. Configure it with `"plugin": ["@kompassdev/opencode"]` on OpenCode 1 or `"plugins": ["@kompassdev/opencode"]` on OpenCode 2. + +OpenCode 2 agent transforms are update-only. If an agent is renamed in Kompass config, the host must register that agent ID before Kompass setup runs. + ## What it provides Inside OpenCode, the adapter exposes: @@ -14,7 +18,7 @@ Inside OpenCode, the adapter exposes: - Kompass commands - Kompass agents - Kompass tools -- Kompass Navigator session and worktree orchestration +- Kompass Navigator session and worktree orchestration through the OpenCode 1 plugin API - project override loading from local config files ## Project overrides @@ -33,7 +37,7 @@ You can use overrides to: ## Kompass Navigator -Navigator is for explicitly requested native-session and managed-worktree workflows. It is not a subagent mechanism; ordinary delegation should use OpenCode's built-in `task` tool. Navigator follows OpenCode Desktop's protocol detection so session creation, prompts, reads, status, and interrupts stay on the same compatible API in the current checkout or managed Git worktrees. Until OpenCode implements V2 wait, Navigator waits by polling the active-session API locally. It requires OpenCode `1.17.12` or newer and is enabled by default. +Navigator is available through the OpenCode 1 plugin API for explicitly requested native-session and managed-worktree workflows. OpenCode 2 does not yet expose the workspace, worktree, and session-discovery APIs required to register Navigator safely. Navigator is not a subagent mechanism; ordinary delegation should use OpenCode's built-in `task` tool. Navigator follows OpenCode Desktop's protocol detection so session creation, prompts, reads, status, and interrupts stay on the same compatible API in the current checkout or managed Git worktrees. Until OpenCode implements V2 wait, Navigator waits by polling the active-session API locally. It requires OpenCode `1.17.12` or newer and is enabled by default on OpenCode 1. ```jsonc { diff --git a/packages/web/src/content/docs/docs/config/schema.mdx b/packages/web/src/content/docs/docs/config/schema.mdx index 69e2bc6..56eb9d3 100644 --- a/packages/web/src/content/docs/docs/config/schema.mdx +++ b/packages/web/src/content/docs/docs/config/schema.mdx @@ -9,7 +9,7 @@ The published schema lives in `kompass.schema.json`. - default base branch: `main` - OpenCode adapter agent mode: `all` -- OpenCode Navigator: enabled, with limits of 8 concurrent sessions, 20,000 read characters, 4,000 output characters per item, and 120,000 milliseconds per wait +- OpenCode 1 Navigator: enabled, with limits of 8 concurrent sessions, 20,000 read characters, 4,000 output characters per item, and 120,000 milliseconds per wait - OpenCode subtask command mode: `kompass` - bundled shared validation guidance is included by default diff --git a/packages/web/src/content/docs/docs/reference/tools/index.mdx b/packages/web/src/content/docs/docs/reference/tools/index.mdx index de32e78..c08db72 100644 --- a/packages/web/src/content/docs/docs/reference/tools/index.mdx +++ b/packages/web/src/content/docs/docs/reference/tools/index.mdx @@ -50,7 +50,7 @@ Creates or updates GitHub issues, renders checklist sections, and can append iss ## OpenCode Navigator tools -Navigator tools are enabled by default and are available only through the OpenCode adapter. Use them only for explicitly requested native-session or managed-worktree workflows, not for subagent delegation; OpenCode's built-in `task` tool owns subagent lifecycle and waiting. Their default runtime names use the `kompass_` prefix. See [Kompass Navigator](./navigator/) for arguments and safety behavior. +Navigator tools are enabled by default through the OpenCode 1 plugin API. OpenCode 2 does not yet expose the host APIs they require. Use them only for explicitly requested native-session or managed-worktree workflows, not for subagent delegation; OpenCode's built-in `task` tool owns subagent lifecycle and waiting. Their default runtime names use the `kompass_` prefix. See [Kompass Navigator](./navigator/) for arguments and safety behavior. ### `worktree_list` diff --git a/packages/web/src/content/docs/docs/reference/tools/navigator.mdx b/packages/web/src/content/docs/docs/reference/tools/navigator.mdx index 146d0fd..a1ee198 100644 --- a/packages/web/src/content/docs/docs/reference/tools/navigator.mdx +++ b/packages/web/src/content/docs/docs/reference/tools/navigator.mdx @@ -3,7 +3,7 @@ title: Kompass Navigator description: Native OpenCode session and managed-worktree orchestration tools. --- -Navigator is enabled by default in the OpenCode adapter. Use it only when the user explicitly requests native-session, managed-worktree, or multi-session workflow orchestration. Do not use it for subagent delegation: OpenCode's built-in `task` tool creates child sessions, waits for foreground tasks, and reports background completion automatically. Default runtime names use the `kompass_` prefix; set `adapters.opencode.navigator.enabled` to `false` to disable all eight tools. +Navigator is enabled by default through the OpenCode 1 plugin API. OpenCode 2 does not yet expose the workspace, worktree, and session-discovery APIs required by these tools. Use Navigator only when the user explicitly requests native-session, managed-worktree, or multi-session workflow orchestration. Do not use it for subagent delegation: OpenCode's built-in `task` tool creates child sessions, waits for foreground tasks, and reports background completion automatically. Default runtime names use the `kompass_` prefix; set `adapters.opencode.navigator.enabled` to `false` to disable all eight tools. | Logical tool | Purpose | | --- | --- |