diff --git a/README.md b/README.md index 60e5f99..bb62d11 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`. Navigator adds nine session/worktree tools on OpenCode 1 and eight native orchestration tools on OpenCode 2. - 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 1 handles command expansion natively. On OpenCode 2, the plugin inserts the raw input at `$ARGUMENTS`, or appends it when that placeholder is absent. Numbered placeholders and shell interpolation in custom command templates are not supported by the v2 adapter. OpenCode 2 agent transforms create missing agent IDs, including renamed Kompass agents. + Project config is optional. To start from the published base config: ```bash @@ -52,9 +62,55 @@ Kompass loads the bundled base config, then optional home-directory overrides, t The recommended project override path is `.opencode/kompass.jsonc`. -## Kompass Navigator +## OpenCode 2 worktrees + +Kompass registers `rift` as a native OpenCode 2 worktree strategy using its bundled `rift-snapshot` dependency. It becomes the location's default for API calls that omit a strategy. Desktop's current New worktree action explicitly selects Git, so that button does not use Rift; Kompass's `worktree_create` tool uses the registered default. This requires a recent V2 release with plugin worktree strategies and returned-directory support; the adapter targets `@opencode/plugin` beta `19381`. + +Load Kompass in OpenCode's `opencode.jsonc`; no destination setting is needed: + +```jsonc +{ + "plugins": ["@kompassdev/opencode"] +} +``` + +Rift creates a copy-on-write snapshot of the source checkout, including dependencies and uncommitted files. Rift controls storage using its default `.rifts//` layout beside the source root; OpenCode records the returned directory. The strategy uses OpenCode's suggested name, suffixing collisions in Rift's storage, but does not use OpenCode's destination override or `worktree.directory` setting. Rift's configured creation hooks run, followed by OpenCode's project setup. Rift cannot select a starting branch or ref. Use an explicit `strategy: "git"` when creating from a ref. + +Inventory includes sibling and nested snapshots. Removal requests force confirmation for uncommitted files and requires child snapshots to be removed first, even with force. Rift moves removed snapshots to its trash; its `gc` operation reclaims that storage. + +The last active strategy registration wins. Unloading Kompass restores the previous default; existing snapshots keep their Rift owner and need the plugin available for removal. Rift errors are surfaced without a Git fallback. The strategy uses Rift's native bindings, loaded on first use, and requires a supported copy-on-write filesystem. Its synchronous native calls cannot be interrupted mid-operation; cancellation is checked between calls. + +## OpenCode 2 session orchestration + +All V2 commands execute inline in the invoking session, including commands marked `subtask` in shared definitions. They switch to the command's agent and submit its expanded template there. Separate sessions are created explicitly through Navigator tools. + +Worktrees and sessions have separate lifecycles. `kompass_worktree_create({ name?, strategy?, branch? })` creates a worktree without a session. Pass its returned `directory` to `session_create`. Omit `directory` to use the calling session's current location, including when it is already in a worktree. Omit `prompt` to create an idle session, then start it with `session_send`. With a prompt, creation starts execution immediately. `session_send` targets a session ID and uses that session's location. + +`kompass_worktree_remove({ directory, force? })` removes a managed worktree through OpenCode. It rejects the checkout and any worktree containing the calling session or plugin. `force` defaults to false; true permits discarding uncommitted files. Rift still rejects snapshots with children. V2 cannot discover other active sessions, so finish or interrupt known sessions in that directory before removal. Removing a worktree does not delete its sessions. + +Navigator is enabled by default on V2 and exposes `kompass_worktree_create`, `kompass_worktree_list`, `kompass_worktree_remove`, `kompass_session_create`, `kompass_session_read`, `kompass_session_send`, `kompass_session_wait`, and `kompass_session_interrupt`. Use these for explicitly requested native-session workflows. Ordinary delegation uses OpenCode's built-in subagent tool. + +For example, call `kompass_worktree_create` with `{ "name": "parser-fix" }`, then pass the returned directory to `kompass_session_create`: + +```json +{ + "prompt": "Implement the parser fix and run its tests.", + "title": "Parser fix", + "directory": "/projects/.rifts/repo/parser-fix" +} +``` + +Session creation inherits the caller's agent/model/variant unless overridden, admits the optional first prompt, and returns the session ID and directory immediately. Explicit directories may select the project checkout or a managed worktree; relative paths resolve from the caller's directory. Session creation never creates or removes worktrees. V2 model overrides use `{ "providerID": "...", "id": "...", "variant": "..." }`. On `worktree_create`, a starting `branch` requires `strategy: "git"`; Rift uses the source checkout's state. + +Use the returned session ID to send steered or queued follow-ups, read bounded current context, wait for completion, or interrupt execution. Waits use the native Effect API, support one to eight targets, and release wait subscriptions on timeout or cancellation without interrupting the target sessions. `timeoutMs` must be positive and is capped by `maxWaitMs`. Reads use the current model context, not paginated full history. Partial failures report known resources and preserve them for recovery. + +The existing `adapters.opencode.navigator.enabled`, tool aliases/disables, `maxReadChars`, `maxOutputCharsPerItem`, and `maxWaitMs` settings apply to V2. `session_list` and the `maxConcurrentSessions` limit remain V1-only because the V2 plugin context lacks session discovery. Known session IDs continue to work after plugin reloads; Kompass does not keep its own session registry. + +## OpenCode 1 Navigator + +V1 also uses `session_create({ directory?, prompt?, agent?, model? })`: omit directory for the calling session's location, or pass the directory from a separate `worktree_create` call. The old `environment` argument has been removed. Omit prompt for an idle session. On the legacy session protocol, agent/model selection happens only when prompting, so supply those overrides to `session_send` when starting an idle session. V1 model overrides use `modelID`; V2 uses `id`. -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's OpenCode 1 adapter provides nine tools, including standalone `worktree_create({ name?, startCommand? })`. It prefers Rift when available; `startCommand` selects Git. V1 removal checks active sessions and has no force option. Ordinary delegation uses OpenCode's built-in `task` tool. Navigator follows Desktop's protocol detection and requires OpenCode `1.17.12` or newer. Configure Navigator and its limits with: @@ -80,7 +136,7 @@ Set `adapters.opencode.navigator.enabled` to `false` to disable all Navigator to Navigator accepts only sessions from the current OpenCode project and only worktrees returned by OpenCode. New sessions inherit the calling session's agent, model, and variant unless explicitly overridden. It rejects self-targeting lifecycle calls, arbitrary directories, unknown V2 agent overrides, main-checkout removal, unmanaged worktrees, and removal while a worktree has active sessions. `session_send` can switch the target session's agent or model before admitting a steered prompt when the detected OpenCode protocol supports it. `session_wait` defaults to `maxWaitMs`, caps requested timeouts at `maxWaitMs`, and treats `timeoutMs: 0` as an immediate snapshot. Navigator never force-removes or automatically cleans up resources after a partial failure. -When OpenCode exposes experimental workspace adapters, Kompass registers a `rift` workspace adapter backed by its bundled `rift-snapshot` dependency. Navigator automatically uses that adapter for `new_worktree` sessions when no `startCommand` is requested, falling back to Git worktrees only when the experimental API or Rift adapter is unavailable. +On OpenCode 1, when experimental workspace adapters are available, Kompass registers a `rift` workspace adapter backed by its bundled `rift-snapshot` dependency. `worktree_create` uses that adapter when no `startCommand` is requested, using Git worktrees otherwise. Pass its returned directory to `session_create`. Kompass preserves the OpenCode workspace ID when it creates future sessions in Rift workspaces. Existing sessions without workspace identity are not migrated automatically. Rift workspaces are experimental OpenCode workspaces, not legacy Desktop sandboxes, and Kompass does not modify `project.sandboxes` or OpenCode's workspace database. diff --git a/bun.lock b/bun.lock index bfa0525..52b4ebd 100644 --- a/bun.lock +++ b/bun.lock @@ -18,19 +18,18 @@ }, "packages/opencode": { "name": "@kompassdev/opencode", - "version": "0.16.1", + "version": "0.17.4", "dependencies": { + "@opencode-ai/sdk": "^1.18.7", + "@opencode/plugin": "0.0.0-beta-19381", + "@opencode/schema": "0.0.0-beta-19381", + "effect": "4.0.0-rc.112", "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 +69,54 @@ "@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/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], + + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], + + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], + + "@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/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1057.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-node": "^3.972.47", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-5MliYkp2u0+2arTp5fZIaxl+xmm90LEKv/VeSxhfNQW4t0fvWJrNO429/jchWQenNoDRrOGE59VfbuZUfwFujg=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.977.9", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@aws-sdk/xml-builder": "^3.972.40", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.33.3", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA=="], + + "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.69", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-vpsh9VWmQVC/nsdzf72F2yUMBOFT1aq+hoLseYV03zjVXVHkjqSNJskFRKPFxc3MRFrHNbSSmOwsbYE15u9ecw=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.70", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.72", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/node-http-handler": "^4.11.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.15", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/credential-provider-env": "^3.972.70", "@aws-sdk/credential-provider-http": "^3.972.72", "@aws-sdk/credential-provider-login": "^3.972.77", "@aws-sdk/credential-provider-process": "^3.972.70", "@aws-sdk/credential-provider-sso": "^3.973.14", "@aws-sdk/credential-provider-web-identity": "^3.972.76", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg=="], + + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.77", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.82", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.70", "@aws-sdk/credential-provider-http": "^3.972.72", "@aws-sdk/credential-provider-ini": "^3.973.15", "@aws-sdk/credential-provider-process": "^3.972.70", "@aws-sdk/credential-provider-sso": "^3.973.14", "@aws-sdk/credential-provider-web-identity": "^3.972.76", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.70", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.14", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/token-providers": "3.1116.0", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.76", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA=="], + + "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1057.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1057.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-cognito-identity": "^3.972.38", "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-ini": "^3.972.46", "@aws-sdk/credential-provider-login": "^3.972.45", "@aws-sdk/credential-provider-node": "^3.972.47", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.6", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-rbrEHtz11g0kxsSkYr3fx2HABNNblp4AhB2MgPvJHgYOWfJ2eBviU7Mvoaef0PW8QH6lbZDfJcnM7eKvtvz3sw=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.44", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/signature-v4-multi-region": "^3.996.46", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/node-http-handler": "^4.11.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw=="], + + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.46", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1116.0", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.974.5", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ=="], + + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.10", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ycwH6Zd2GhuSqdXX9ihbCjeGTB6xOJs+O3+Jb8/zDG9978XU80qs75dfkPJRMNKe5MvBZPuNeFpd4JZKPoUF4g=="], + + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.40", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA=="], + + "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="], + "@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=="], @@ -84,6 +131,12 @@ "@ctrl/tinycolor": ["@ctrl/tinycolor@4.2.0", "", {}, "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A=="], + "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-rc.112", "", { "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <2.0.0", "@opentelemetry/api-logs": ">=0.203.0 <0.300.0", "@opentelemetry/resources": ">=2.0.0 <3.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": ">=2.0.0 <3.0.0", "@opentelemetry/sdk-trace-base": ">=2.0.0 <3.0.0", "@opentelemetry/sdk-trace-node": ">=2.0.0 <3.0.0", "@opentelemetry/sdk-trace-web": ">=2.0.0 <3.0.0", "@opentelemetry/semantic-conventions": ">=1.33.0 <2.0.0", "effect": "^4.0.0-rc.112" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/api-logs", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-OTRv1DxTHUmnakgJ6XVM8wVgF1KgZH4UXnOemwSLUwUjXO+RCikzF8oR/rlVmOGq81KtQzj1URM4M4nchlQOuQ=="], + + "@effect/platform-node": ["@effect/platform-node@4.0.0-rc.112", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-rc.112", "mime": "^4.1.0", "undici": "^8.10.0" }, "peerDependencies": { "effect": "^4.0.0-rc.112", "redis": ">=5.0.0 <7.0.0" } }, "sha512-/BMAcdNGQQskLmI0Zoa95KfTZkr9HV9N4NSxaSrusG6GeW6Ulp9KvZ+Rlaiw8lnOt43CXjFLdfll5/k5rxL4hQ=="], + + "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-rc.112", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.21.3" }, "peerDependencies": { "effect": "^4.0.0-rc.112" } }, "sha512-ttjz0xKamFN7vL8pNDYVwddJLjZvqKePc05djlz2VcdaKbLsnYbtMnL1rbOfHgEnIUSHGh7FkjaN4DM1Ov81sQ=="], + "@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="], "@emmetio/css-abbreviation": ["@emmetio/css-abbreviation@2.1.8", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="], @@ -160,6 +213,8 @@ "@expressive-code/plugin-text-markers": ["@expressive-code/plugin-text-markers@0.38.3", "", { "dependencies": { "@expressive-code/core": "^0.38.3" } }, "sha512-dPK3+BVGTbTmGQGU3Fkj3jZ3OltWUAlxetMHI6limUGCWBCucZiwoZeFM/WmqQa71GyKRzhBT+iEov6kkz2xVA=="], + "@gar/promise-retry": ["@gar/promise-retry@1.0.3", "", {}, "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA=="], + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], @@ -210,6 +265,12 @@ "@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=="], + + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + + "@isaacs/string-locale-compare": ["@isaacs/string-locale-compare@1.1.0", "", {}, "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], "@kompassdev/core": ["@kompassdev/core@workspace:packages/core"], @@ -232,10 +293,76 @@ "@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=="], + "@npmcli/agent": ["@npmcli/agent@4.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg=="], + + "@npmcli/arborist": ["@npmcli/arborist@9.4.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bin": { "arborist": "bin/index.js" } }, "sha512-4Bm8hNixJG/sii1PMnag0V9i/sGOX9VRzFrUiZMSBJpGlLR38f+Btl85d07G9GL56xO0l0OZjvrGNYsDYp0xKA=="], + + "@npmcli/config": ["@npmcli/config@10.8.1", "", { "dependencies": { "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "walk-up-path": "^4.0.0" } }, "sha512-MAYk9IlIGiyC0c9fnjdBSQfIFPZT0g1MfeSiD1UXTq2zJOLX55jS9/sETJHqw/7LN18JjITrhYfgCfapbmZHiQ=="], + + "@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="], + + "@npmcli/git": ["@npmcli/git@7.0.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg=="], + + "@npmcli/installed-package-contents": ["@npmcli/installed-package-contents@4.0.0", "", { "dependencies": { "npm-bundled": "^5.0.0", "npm-normalize-package-bin": "^5.0.0" }, "bin": { "installed-package-contents": "bin/index.js" } }, "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA=="], + + "@npmcli/map-workspaces": ["@npmcli/map-workspaces@5.0.3", "", { "dependencies": { "@npmcli/name-from-folder": "^4.0.0", "@npmcli/package-json": "^7.0.0", "glob": "^13.0.0", "minimatch": "^10.0.3" } }, "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw=="], + + "@npmcli/metavuln-calculator": ["@npmcli/metavuln-calculator@9.0.3", "", { "dependencies": { "cacache": "^20.0.0", "json-parse-even-better-errors": "^5.0.0", "pacote": "^21.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5" } }, "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg=="], + + "@npmcli/name-from-folder": ["@npmcli/name-from-folder@4.0.0", "", {}, "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg=="], + + "@npmcli/node-gyp": ["@npmcli/node-gyp@5.0.0", "", {}, "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ=="], + + "@npmcli/package-json": ["@npmcli/package-json@7.0.5", "", { "dependencies": { "@npmcli/git": "^7.0.0", "glob": "^13.0.0", "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.5.3", "spdx-expression-parse": "^4.0.0" } }, "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ=="], + + "@npmcli/promise-spawn": ["@npmcli/promise-spawn@9.0.1", "", { "dependencies": { "which": "^6.0.0" } }, "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q=="], + + "@npmcli/query": ["@npmcli/query@5.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" } }, "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ=="], + + "@npmcli/redact": ["@npmcli/redact@4.0.0", "", {}, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="], + + "@npmcli/run-script": ["@npmcli/run-script@10.0.4", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0" } }, "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg=="], "@opencode-ai/sdk": ["@opencode-ai/sdk@1.18.7", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-5txVELvCxzC3O/MYnRKdTvBN3TqQjE3PaEks3kDRA/moWzMSUhdm15xCth/Mz/NPYyGTn2u4IdScdV9vLTfLRg=="], + "@opencode/ai": ["@opencode/ai@0.0.0-beta-19381", "", { "dependencies": { "@aws-sdk/credential-providers": "3.1057.0", "@opencode/schema": "0.0.0-beta-19381", "@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-22V30RSke8NUKWYuDNwkKnO1mpzO1ouIqpOq70lUYB5A1a/UCLHNvAS76/2B+rdiVl0Y8jIPMkZ1BdV6VdZ+SA=="], + + "@opencode/client": ["@opencode/client@0.0.0-beta-19381", "", { "dependencies": { "@opencode/protocol": "0.0.0-beta-19381", "@opencode/schema": "0.0.0-beta-19381" }, "peerDependencies": { "effect": "4.0.0-rc.112", "solid-js": ">=1.9.0" }, "optionalPeers": ["effect", "solid-js"] }, "sha512-VIJstFeUC/XceLqQK5Mt3tHEl5OlsUF/M+wkhkhbLMpFx5F1J5pwc7jdMaXBJVMJzF6MFy233SzHH2vI8ALBjQ=="], + + "@opencode/plugin": ["@opencode/plugin@0.0.0-beta-19381", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@opencode/ai": "0.0.0-beta-19381", "@opencode/client": "0.0.0-beta-19381", "@opencode/protocol": "0.0.0-beta-19381", "@opencode/schema": "0.0.0-beta-19381", "@opencode/util": "0.0.0-beta-19381", "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112", "zod": "4.1.8" }, "peerDependencies": { "@opencode/theme": "0.0.0-beta-19381", "@opentui/core": ">=0.5.10", "@opentui/solid": ">=0.5.10", "solid-js": ">=1.9.0" }, "optionalPeers": ["@opencode/theme", "@opentui/core", "@opentui/solid", "solid-js"] }, "sha512-xs71eTix7Y16bob6PQvgW7XS5FqhKWeHnuChpMYh4gxhUG91MiuqwP/zOUxpny+JIYetslT/T76ua92mqMhBDg=="], + + "@opencode/protocol": ["@opencode/protocol@0.0.0-beta-19381", "", { "dependencies": { "@opencode/schema": "0.0.0-beta-19381", "effect": "4.0.0-rc.112" } }, "sha512-e1iHI2IUDiIuzMsVMU7I77xlzwwD+69r4xEdfVtr4FZQvNYIHv771rgFpBIjeuE1e/GtP6gx0wa/zvf7fAJo6w=="], + + "@opencode/schema": ["@opencode/schema@0.0.0-beta-19381", "", { "dependencies": { "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112" } }, "sha512-wrVyp4RcwMHglc+OIev/WeR1tUo1WfUkOKh8AbE/sY5NubnOa16zKDqrYC419ruU2x1A3H7octb1atNdwRtCBg=="], + + "@opencode/util": ["@opencode/util@0.0.0-beta-19381", "", { "dependencies": { "@effect/opentelemetry": "4.0.0-rc.112", "@effect/platform-node": "4.0.0-rc.112", "@effect/platform-node-shared": "4.0.0-rc.112", "@npmcli/arborist": "9.4.0", "@npmcli/config": "10.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/sdk-trace-base": "2.6.1", "@opentelemetry/sdk-trace-node": "2.6.1", "cross-spawn": "7.0.6", "effect": "4.0.0-rc.112", "glob": "13.0.5", "mime-types": "3.0.2", "minimatch": "10.2.5", "npm-package-arg": "13.0.2", "pacote": "21.5.1" } }, "sha512-DEm0InxoUwHV7U8xE2gw8tmU+J5wOON3A2Ht/REWI5KWtLpMq2z88x4gmgvUo4Ac6+c0s9/KQB4+6201Vg/hRQ=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA=="], + + "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.6.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ=="], + + "@opentelemetry/core": ["@opentelemetry/core@2.6.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g=="], + + "@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.214.0", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/otlp-exporter-base": "0.214.0", "@opentelemetry/otlp-transformer": "0.214.0", "@opentelemetry/resources": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-kIN8nTBMgV2hXzV/a20BCFilPZdAIMYYJGSgfMMRm/Xa+07y5hRDS2Vm12A/z8Cdu3Sq++ZvJfElokX2rkgGgw=="], + + "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.214.0", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/otlp-transformer": "0.214.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-u1Gdv0/E9wP+apqWf7Wv2npXmgJtxsW2XL0TEv9FZloTZRuMBKmu8cYVXwS4Hm3q/f/3FuCnPTgiwYvIqRSpRg=="], + + "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/sdk-logs": "0.214.0", "@opentelemetry/sdk-metrics": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1", "protobufjs": "^7.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w=="], + + "@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="], + + "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA=="], + + "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ=="], + + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw=="], + + "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.6.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/core": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Hh2i4FwHWRFhnO2Q/p6svMxy8MPsNCG0uuzUY3glqm0rwM0nQvbTO1dXSp9OqQoTKXcQzaz9q1f65fsurmOhNw=="], + + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="], + "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], "@pagefind/darwin-arm64": ["@pagefind/darwin-arm64@1.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ=="], @@ -252,6 +379,36 @@ "@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=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.5", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.1", "", {}, "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1" } }, "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.2", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="], + + "@redis/bloom": ["@redis/bloom@6.2.1", "", { "peerDependencies": { "@redis/client": "^6.2.1" } }, "sha512-huQgNLaCIZfQ9SeLn4q9124uOUd8HbZDYHwwUzNcRgHqCHiHKl2dDxMqJCeWh8cMqZAoWuHR8XnWbDMIf+o7ag=="], + + "@redis/client": ["@redis/client@6.2.1", "", { "dependencies": { "cluster-key-slot": "1.1.2" }, "peerDependencies": { "@node-rs/xxhash": "^1.1.0", "@opentelemetry/api": ">=1 <2" }, "optionalPeers": ["@node-rs/xxhash", "@opentelemetry/api"] }, "sha512-LzxBY7SIBvvJiyCgcaJZZakE3fJrZZ++i24+EDW9fKpCl68D35uJcKFpZZwCfOoG9WZTbyZlMzMeM0gtOAMU9Q=="], + + "@redis/json": ["@redis/json@6.2.1", "", { "peerDependencies": { "@redis/client": "^6.2.1" } }, "sha512-AFIUJ8Gj0DaaSBHYuSt8+O0oYWM+50OK1c0OmodB7XERIA8+BbyV3O4v76f9iccWasd1/7qjfZTpuzexUaZtrQ=="], + + "@redis/search": ["@redis/search@6.2.1", "", { "peerDependencies": { "@redis/client": "^6.2.1" } }, "sha512-2vfOAOyYFE7UUw3sBBlkqqruBtOUS4HRY5MtW4hp83llrwvtrTE4r22CEqXddlV+54zkLxBE4nmsIJ/dpezQrQ=="], + + "@redis/time-series": ["@redis/time-series@6.2.1", "", { "peerDependencies": { "@redis/client": "^6.2.1" } }, "sha512-kiYniph04dJOole+L359B6C9E+jYS2uDP7hca6Onj0xF38ZIpyxARO0Iq0W4ZRn1e8Q6vqW00QFZVSMRA/2Ijw=="], + "@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,8 +475,46 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@sigstore/bundle": ["@sigstore/bundle@4.0.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A=="], + + "@sigstore/core": ["@sigstore/core@3.2.1", "", {}, "sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g=="], + + "@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.5.2", "", {}, "sha512-SQqvFMt4V78fdjcDdYX6HbiVSOR4QK3ZgwCa2KOsopAgPIHy1rU5UDUmzLl02r5oyyaYcYHR1hpwDRk/yUe+Mw=="], + + "@sigstore/sign": ["@sigstore/sign@4.1.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.2", "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.4", "proc-log": "^6.1.0" } }, "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ=="], + + "@sigstore/tuf": ["@sigstore/tuf@4.0.2", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.1.0" } }, "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ=="], + + "@sigstore/verify": ["@sigstore/verify@3.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA=="], + + "@smithy/core": ["@smithy/core@3.33.3", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg=="], + + "@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/fetch-http-handler": ["@smithy/fetch-http-handler@5.8.0", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA=="], + + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.12.1", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.7.3", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow=="], + + "@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=="], + "@tufjs/canonical-json": ["@tufjs/canonical-json@2.0.0", "", {}, "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA=="], + + "@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="], + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], @@ -344,6 +539,8 @@ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], "@volar/kit": ["@volar/kit@2.4.28", "", { "dependencies": { "@volar/language-service": "2.4.28", "@volar/typescript": "2.4.28", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "*" } }, "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg=="], @@ -362,10 +559,14 @@ "@vscode/l10n": ["@vscode/l10n@0.0.18", "", {}, "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="], + "abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "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 +593,38 @@ "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@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "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=="], + + "bin-links": ["bin-links@6.0.2", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-frE1t78WOwJ45PKV2cF2tNPjTcs9L1J9s6VkrV59wanRP4GlaomuxYPVma7BwthMg8WnfSory4w5PTE6FZZ81w=="], + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + "bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="], + "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@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], + "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -422,6 +641,8 @@ "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], @@ -430,6 +651,10 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], + + "cmd-shim": ["cmd-shim@8.0.0", "", {}, "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA=="], + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -442,6 +667,8 @@ "common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + "content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], @@ -462,6 +689,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 +725,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=="], @@ -506,6 +739,8 @@ "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], @@ -536,11 +771,13 @@ "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], + "expressive-code": ["expressive-code@0.38.3", "", { "dependencies": { "@expressive-code/core": "^0.38.3", "@expressive-code/plugin-frames": "^0.38.3", "@expressive-code/plugin-shiki": "^0.38.3", "@expressive-code/plugin-text-markers": "^0.38.3" } }, "sha512-COM04AiUotHCKJgWdn7NtW2lqu8OW8owAidMpkXt1qxrZ9Q2iC7+tok/1qIn2ocGnczvr9paIySgGnEwFeEQ8Q=="], "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 +785,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 +793,34 @@ "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=="], + + "fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], + "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@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="], + + "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=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "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=="], @@ -606,6 +863,8 @@ "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + "hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="], + "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], @@ -614,14 +873,24 @@ "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "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=="], + "iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="], + + "ignore-walk": ["ignore-walk@8.0.0", "", { "dependencies": { "minimatch": "^10.0.3" } }, "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A=="], + "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=="], + "ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + "ip-address": ["ip-address@10.7.0", "", {}, "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA=="], + "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], @@ -644,17 +913,35 @@ "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-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "json-stringify-nice": ["json-stringify-nice@1.1.4", "", {}, "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw=="], + "jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="], + "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], + + "just-diff": ["just-diff@6.0.2", "", {}, "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA=="], + + "just-diff-apply": ["just-diff-apply@5.5.0", "", {}, "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw=="], + + "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=="], + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], - "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -664,6 +951,8 @@ "magicast": ["magicast@0.5.2", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ=="], + "make-fetch-happen": ["make-fetch-happen@15.0.6", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" } }, "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw=="], + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], @@ -778,32 +1067,76 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "mime": ["mime@4.1.0", "", { "bin": { "mime": "bin/cli.js" } }, "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="], + + "minipass-fetch": ["minipass-fetch@5.0.2", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^2.0.0", "minizlib": "^3.0.1" }, "optionalDependencies": { "iconv-lite": "^0.7.2" } }, "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ=="], + + "minipass-flush": ["minipass-flush@1.0.7", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA=="], + + "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="], + + "minipass-sized": ["minipass-sized@2.0.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA=="], + + "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + "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=="], + "negotiator": ["negotiator@1.1.0", "", { "dependencies": { "content-type": "^2.1.0" } }, "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg=="], + "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": ["node-gyp@12.4.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "undici": "^6.25.0", "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw=="], + "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=="], "node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="], + "nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + "npm-bundled": ["npm-bundled@5.0.0", "", { "dependencies": { "npm-normalize-package-bin": "^5.0.0" } }, "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw=="], + + "npm-install-checks": ["npm-install-checks@8.0.0", "", { "dependencies": { "semver": "^7.1.1" } }, "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA=="], + + "npm-normalize-package-bin": ["npm-normalize-package-bin@5.0.0", "", {}, "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag=="], + + "npm-package-arg": ["npm-package-arg@13.0.2", "", { "dependencies": { "hosted-git-info": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^7.0.0" } }, "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA=="], + + "npm-packlist": ["npm-packlist@10.0.4", "", { "dependencies": { "ignore-walk": "^8.0.0", "proc-log": "^6.0.0" } }, "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng=="], + + "npm-pick-manifest": ["npm-pick-manifest@11.0.3", "", { "dependencies": { "npm-install-checks": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "npm-package-arg": "^13.0.0", "semver": "^7.3.5" } }, "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ=="], + + "npm-registry-fetch": ["npm-registry-fetch@19.1.1", "", { "dependencies": { "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", "npm-package-arg": "^13.0.0", "proc-log": "^6.0.0" } }, "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw=="], + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], @@ -816,14 +1149,22 @@ "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + "p-map": ["p-map@7.0.7", "", {}, "sha512-VaWRu2i4FJNRtiRWCuuQRgfQ1B7a6+gMSrO+3j0EQi/k0ULfS9kosRxGoiqwzIjZTDI02tGfk5mXXltLg6QtfQ=="], + "p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], "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=="], + "pacote": ["pacote@21.5.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" } }, "sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg=="], + "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=="], + "parse-conflict-json": ["parse-conflict-json@5.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="], + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], "parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="], @@ -834,6 +1175,8 @@ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -844,20 +1187,32 @@ "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], - "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "postcss-selector-parser": ["postcss-selector-parser@7.1.6", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw=="], "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + "proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "proggy": ["proggy@4.0.0", "", {}, "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ=="], + + "promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="], + + "promise-call-limit": ["promise-call-limit@3.0.2", "", {}, "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw=="], + "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + "protobufjs": ["protobufjs@7.6.6", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg=="], + "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], + "read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="], + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], @@ -868,6 +1223,8 @@ "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + "redis": ["redis@6.2.1", "", { "dependencies": { "@redis/bloom": "6.2.1", "@redis/client": "6.2.1", "@redis/json": "6.2.1", "@redis/search": "6.2.1", "@redis/time-series": "6.2.1" } }, "sha512-Z9VHtgYs48PiQC77X9O2Er8Hj4T+5BtFjT91/vi5Is1D04N72cA946ZslM1ImJw8ZctFBZWAVjM7S5wJNeHMpg=="], + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], @@ -918,8 +1275,14 @@ "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=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -932,39 +1295,63 @@ "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=="], + + "sigstore": ["sigstore@4.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.1", "@sigstore/tuf": "^4.0.2", "@sigstore/verify": "^3.1.1" } }, "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w=="], + "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=="], + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], + "smol-toml": ["smol-toml@1.6.0", "", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="], + "socks": ["socks@2.8.10", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ=="], + + "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + + "spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="], + + "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], + + "ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], + "stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="], "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=="], "svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="], + "tar": ["tar@7.5.22", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA=="], + "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], "tinyexec": ["tinyexec@1.0.4", "", {}, "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw=="], "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=="], + "treeverse": ["treeverse@3.0.0", "", {}, "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ=="], "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], @@ -974,6 +1361,8 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="], + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], "typesafe-path": ["typesafe-path@0.2.2", "", {}, "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA=="], @@ -988,6 +1377,8 @@ "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], + "undici": ["undici@8.10.2", "", {}, "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ=="], + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], @@ -1018,7 +1409,7 @@ "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=="], + "validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", {}, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="], "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], @@ -1064,8 +1455,12 @@ "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + "walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], + "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,10 +1469,18 @@ "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=="], + + "write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], + + "ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], + "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=="], + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], "yaml-language-server": ["yaml-language-server@1.20.0", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "prettier": "^3.5.0", "request-light": "^0.5.7", "vscode-json-languageservice": "4.1.8", "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-uri": "^3.0.2", "yaml": "2.7.1" }, "bin": { "yaml-language-server": "bin/yaml-language-server" } }, "sha512-qhjK/bzSRZ6HtTvgeFvjNPJGWdZ0+x5NREV/9XZWFjIGezew2b4r5JPy66IfOhd5OA7KeFwk1JfmEbnTvev0cA=="], @@ -1092,7 +1495,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,14 +1503,44 @@ "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/sha256-browser/@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=="], + + "@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=="], + + "@aws-sdk/client-cognito-identity/@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + + "@aws-sdk/credential-provider-cognito-identity/@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + + "@aws-sdk/credential-provider-sso/@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + + "@aws-sdk/signature-v4-multi-region/@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + + "@aws-sdk/token-providers/@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + "@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=="], + + "@npmcli/arborist/common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="], + + "@npmcli/git/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + + "@npmcli/promise-spawn/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + + "@opencode/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=="], + "@smithy/fetch-http-handler/@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + + "@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -1120,10 +1553,26 @@ "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=="], + + "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "node-gyp/undici": ["undici@6.28.1", "", {}, "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA=="], + + "node-gyp/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "rimraf/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=="], + "sitemap/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], "unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], @@ -1138,12 +1587,18 @@ "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/sha256-browser/@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=="], + + "@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 +1611,14 @@ "@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=="], + + "@npmcli/git/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + + "@npmcli/promise-spawn/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + "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=="], @@ -1164,6 +1627,16 @@ "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], + "minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "node-gyp/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + + "rimraf/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "rimraf/glob/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=="], + "sitemap/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], @@ -1232,10 +1705,16 @@ "boxen/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@2.1.4", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg=="], + + "rimraf/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "@expressive-code/plugin-shiki/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="], "@expressive-code/plugin-shiki/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="], + + "rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], } } diff --git a/kompass.jsonc b/kompass.jsonc index 047f44a..fddc71c 100644 --- a/kompass.jsonc +++ b/kompass.jsonc @@ -54,8 +54,9 @@ "ticket_load": { "enabled": true }, "ticket_sync": { "enabled": true }, "worktree_list": { "enabled": true }, + "worktree_create": { "enabled": true }, "session_create": { "enabled": true }, - "session_list": { "enabled": true }, + "session_list": { "enabled": true }, // OpenCode 1 only "session_read": { "enabled": true }, "session_send": { "enabled": true }, "session_wait": { "enabled": true }, @@ -88,7 +89,7 @@ "agentMode": "all", "navigator": { "enabled": true, - "maxConcurrentSessions": 8, + "maxConcurrentSessions": 8, // OpenCode 1 only; V2 plugins lack active-session discovery "maxReadChars": 20000, "maxOutputCharsPerItem": 4000, "maxWaitMs": 120000, diff --git a/kompass.schema.json b/kompass.schema.json index 0efd73f..8fc296c 100644 --- a/kompass.schema.json +++ b/kompass.schema.json @@ -239,11 +239,15 @@ "worktree_list": { "$ref": "#/$defs/toolConfig" }, + "worktree_create": { + "$ref": "#/$defs/toolConfig" + }, "session_create": { "$ref": "#/$defs/toolConfig" }, "session_list": { - "$ref": "#/$defs/toolConfig" + "$ref": "#/$defs/toolConfig", + "description": "OpenCode 1 Navigator only; V2 plugins do not expose session listing." }, "session_read": { "$ref": "#/$defs/toolConfig" @@ -258,7 +262,8 @@ "$ref": "#/$defs/toolConfig" }, "worktree_remove": { - "$ref": "#/$defs/toolConfig" + "$ref": "#/$defs/toolConfig", + "description": "Remove managed worktrees. V2 uses native removal with optional force and cannot discover active sessions." } } }, @@ -356,8 +361,8 @@ "type": "object", "additionalProperties": false, "properties": { - "enabled": { "type": "boolean", "default": true }, - "maxConcurrentSessions": { "type": "integer", "minimum": 1, "maximum": 8, "default": 8 }, + "enabled": { "type": "boolean", "default": true, "description": "Enable Navigator: nine tools on OpenCode 1 and eight native orchestration tools on OpenCode 2." }, + "maxConcurrentSessions": { "type": "integer", "minimum": 1, "maximum": 8, "default": 8, "description": "OpenCode 1 only. The V2 plugin API does not expose active-session discovery, so V2 does not enforce this limit." }, "maxReadChars": { "type": "integer", "minimum": 1, "default": 20000 }, "maxOutputCharsPerItem": { "type": "integer", "minimum": 1, "default": 4000 }, "maxWaitMs": { "type": "integer", "minimum": 1, "default": 120000 } @@ -469,6 +474,7 @@ "ticket_sync", "ticket_load", "worktree_list", + "worktree_create", "session_create", "session_list", "session_read", diff --git a/packages/core/index.ts b/packages/core/index.ts index 5ae9986..662f7b9 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -9,6 +9,7 @@ export { getConfiguredToolName, getEnabledToolNames, NAVIGATOR_TOOL_NAMES, + V2_NAVIGATOR_TOOL_NAMES, loadKompassConfig, mergeWithDefaults, } from "./lib/config.ts"; diff --git a/packages/core/kompass.jsonc b/packages/core/kompass.jsonc index 047f44a..fddc71c 100644 --- a/packages/core/kompass.jsonc +++ b/packages/core/kompass.jsonc @@ -54,8 +54,9 @@ "ticket_load": { "enabled": true }, "ticket_sync": { "enabled": true }, "worktree_list": { "enabled": true }, + "worktree_create": { "enabled": true }, "session_create": { "enabled": true }, - "session_list": { "enabled": true }, + "session_list": { "enabled": true }, // OpenCode 1 only "session_read": { "enabled": true }, "session_send": { "enabled": true }, "session_wait": { "enabled": true }, @@ -88,7 +89,7 @@ "agentMode": "all", "navigator": { "enabled": true, - "maxConcurrentSessions": 8, + "maxConcurrentSessions": 8, // OpenCode 1 only; V2 plugins lack active-session discovery "maxReadChars": 20000, "maxOutputCharsPerItem": 4000, "maxWaitMs": 120000, diff --git a/packages/core/lib/config.ts b/packages/core/lib/config.ts index e2ee245..1e92a3e 100644 --- a/packages/core/lib/config.ts +++ b/packages/core/lib/config.ts @@ -21,6 +21,7 @@ export const DEFAULT_TOOL_NAMES = [ "ticket_sync", "ticket_load", "worktree_list", + "worktree_create", "session_create", "session_list", "session_read", @@ -32,6 +33,7 @@ export const DEFAULT_TOOL_NAMES = [ export const NAVIGATOR_TOOL_NAMES = [ "worktree_list", + "worktree_create", "session_create", "session_list", "session_read", @@ -41,6 +43,18 @@ export const NAVIGATOR_TOOL_NAMES = [ "worktree_remove", ] as const; +// V2 lacks session listing. Worktree removal uses native force semantics. +export const V2_NAVIGATOR_TOOL_NAMES = [ + "worktree_list", + "worktree_create", + "worktree_remove", + "session_create", + "session_read", + "session_send", + "session_wait", + "session_interrupt", +] as const satisfies readonly (typeof NAVIGATOR_TOOL_NAMES)[number][]; + export const DEFAULT_COMMAND_NAMES = [ "ask", "branch", @@ -165,6 +179,7 @@ export interface KompassConfig { ticket_sync?: ToolConfig; ticket_load?: ToolConfig; worktree_list?: ToolConfig; + worktree_create?: ToolConfig; session_create?: ToolConfig; session_list?: ToolConfig; session_read?: ToolConfig; @@ -227,6 +242,7 @@ export interface MergedKompassConfig { ticket_sync: ToolConfig; ticket_load: ToolConfig; worktree_list: ToolConfig; + worktree_create: ToolConfig; session_create: ToolConfig; session_list: ToolConfig; session_read: ToolConfig; @@ -252,6 +268,7 @@ export interface MergedKompassConfig { export interface NavigatorConfig { enabled: boolean; + /** OpenCode 1 only; V2 plugins cannot discover all active sessions. */ maxConcurrentSessions: number; maxReadChars: number; maxOutputCharsPerItem: number; @@ -527,6 +544,7 @@ const defaultToolConfig: Record = { ticket_sync: { enabled: true }, ticket_load: { enabled: true }, worktree_list: { enabled: true }, + worktree_create: { enabled: true }, session_create: { enabled: true }, session_list: { enabled: true }, session_read: { enabled: true }, @@ -710,6 +728,7 @@ export function mergeWithDefaults( ticket_sync: { ...defaultToolConfig.ticket_sync, ...config?.tools?.ticket_sync }, ticket_load: { ...defaultToolConfig.ticket_load, ...config?.tools?.ticket_load }, worktree_list: { ...defaultToolConfig.worktree_list, ...config?.tools?.worktree_list }, + worktree_create: { ...defaultToolConfig.worktree_create, ...config?.tools?.worktree_create }, session_create: { ...defaultToolConfig.session_create, ...config?.tools?.session_create }, session_list: { ...defaultToolConfig.session_list, ...config?.tools?.session_list }, session_read: { ...defaultToolConfig.session_read, ...config?.tools?.session_read }, diff --git a/packages/opencode/.opencode/kompass.jsonc b/packages/opencode/.opencode/kompass.jsonc index 7eface7..69e27d8 100644 --- a/packages/opencode/.opencode/kompass.jsonc +++ b/packages/opencode/.opencode/kompass.jsonc @@ -122,6 +122,9 @@ "worktree_list": { "enabled": true }, + "worktree_create": { + "enabled": true + }, "session_create": { "enabled": true }, diff --git a/packages/opencode/README.md b/packages/opencode/README.md index 60e5f99..bb62d11 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`. Navigator adds nine session/worktree tools on OpenCode 1 and eight native orchestration tools on OpenCode 2. - 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 1 handles command expansion natively. On OpenCode 2, the plugin inserts the raw input at `$ARGUMENTS`, or appends it when that placeholder is absent. Numbered placeholders and shell interpolation in custom command templates are not supported by the v2 adapter. OpenCode 2 agent transforms create missing agent IDs, including renamed Kompass agents. + Project config is optional. To start from the published base config: ```bash @@ -52,9 +62,55 @@ Kompass loads the bundled base config, then optional home-directory overrides, t The recommended project override path is `.opencode/kompass.jsonc`. -## Kompass Navigator +## OpenCode 2 worktrees + +Kompass registers `rift` as a native OpenCode 2 worktree strategy using its bundled `rift-snapshot` dependency. It becomes the location's default for API calls that omit a strategy. Desktop's current New worktree action explicitly selects Git, so that button does not use Rift; Kompass's `worktree_create` tool uses the registered default. This requires a recent V2 release with plugin worktree strategies and returned-directory support; the adapter targets `@opencode/plugin` beta `19381`. + +Load Kompass in OpenCode's `opencode.jsonc`; no destination setting is needed: + +```jsonc +{ + "plugins": ["@kompassdev/opencode"] +} +``` + +Rift creates a copy-on-write snapshot of the source checkout, including dependencies and uncommitted files. Rift controls storage using its default `.rifts//` layout beside the source root; OpenCode records the returned directory. The strategy uses OpenCode's suggested name, suffixing collisions in Rift's storage, but does not use OpenCode's destination override or `worktree.directory` setting. Rift's configured creation hooks run, followed by OpenCode's project setup. Rift cannot select a starting branch or ref. Use an explicit `strategy: "git"` when creating from a ref. + +Inventory includes sibling and nested snapshots. Removal requests force confirmation for uncommitted files and requires child snapshots to be removed first, even with force. Rift moves removed snapshots to its trash; its `gc` operation reclaims that storage. + +The last active strategy registration wins. Unloading Kompass restores the previous default; existing snapshots keep their Rift owner and need the plugin available for removal. Rift errors are surfaced without a Git fallback. The strategy uses Rift's native bindings, loaded on first use, and requires a supported copy-on-write filesystem. Its synchronous native calls cannot be interrupted mid-operation; cancellation is checked between calls. + +## OpenCode 2 session orchestration + +All V2 commands execute inline in the invoking session, including commands marked `subtask` in shared definitions. They switch to the command's agent and submit its expanded template there. Separate sessions are created explicitly through Navigator tools. + +Worktrees and sessions have separate lifecycles. `kompass_worktree_create({ name?, strategy?, branch? })` creates a worktree without a session. Pass its returned `directory` to `session_create`. Omit `directory` to use the calling session's current location, including when it is already in a worktree. Omit `prompt` to create an idle session, then start it with `session_send`. With a prompt, creation starts execution immediately. `session_send` targets a session ID and uses that session's location. + +`kompass_worktree_remove({ directory, force? })` removes a managed worktree through OpenCode. It rejects the checkout and any worktree containing the calling session or plugin. `force` defaults to false; true permits discarding uncommitted files. Rift still rejects snapshots with children. V2 cannot discover other active sessions, so finish or interrupt known sessions in that directory before removal. Removing a worktree does not delete its sessions. + +Navigator is enabled by default on V2 and exposes `kompass_worktree_create`, `kompass_worktree_list`, `kompass_worktree_remove`, `kompass_session_create`, `kompass_session_read`, `kompass_session_send`, `kompass_session_wait`, and `kompass_session_interrupt`. Use these for explicitly requested native-session workflows. Ordinary delegation uses OpenCode's built-in subagent tool. + +For example, call `kompass_worktree_create` with `{ "name": "parser-fix" }`, then pass the returned directory to `kompass_session_create`: + +```json +{ + "prompt": "Implement the parser fix and run its tests.", + "title": "Parser fix", + "directory": "/projects/.rifts/repo/parser-fix" +} +``` + +Session creation inherits the caller's agent/model/variant unless overridden, admits the optional first prompt, and returns the session ID and directory immediately. Explicit directories may select the project checkout or a managed worktree; relative paths resolve from the caller's directory. Session creation never creates or removes worktrees. V2 model overrides use `{ "providerID": "...", "id": "...", "variant": "..." }`. On `worktree_create`, a starting `branch` requires `strategy: "git"`; Rift uses the source checkout's state. + +Use the returned session ID to send steered or queued follow-ups, read bounded current context, wait for completion, or interrupt execution. Waits use the native Effect API, support one to eight targets, and release wait subscriptions on timeout or cancellation without interrupting the target sessions. `timeoutMs` must be positive and is capped by `maxWaitMs`. Reads use the current model context, not paginated full history. Partial failures report known resources and preserve them for recovery. + +The existing `adapters.opencode.navigator.enabled`, tool aliases/disables, `maxReadChars`, `maxOutputCharsPerItem`, and `maxWaitMs` settings apply to V2. `session_list` and the `maxConcurrentSessions` limit remain V1-only because the V2 plugin context lacks session discovery. Known session IDs continue to work after plugin reloads; Kompass does not keep its own session registry. + +## OpenCode 1 Navigator + +V1 also uses `session_create({ directory?, prompt?, agent?, model? })`: omit directory for the calling session's location, or pass the directory from a separate `worktree_create` call. The old `environment` argument has been removed. Omit prompt for an idle session. On the legacy session protocol, agent/model selection happens only when prompting, so supply those overrides to `session_send` when starting an idle session. V1 model overrides use `modelID`; V2 uses `id`. -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's OpenCode 1 adapter provides nine tools, including standalone `worktree_create({ name?, startCommand? })`. It prefers Rift when available; `startCommand` selects Git. V1 removal checks active sessions and has no force option. Ordinary delegation uses OpenCode's built-in `task` tool. Navigator follows Desktop's protocol detection and requires OpenCode `1.17.12` or newer. Configure Navigator and its limits with: @@ -80,7 +136,7 @@ Set `adapters.opencode.navigator.enabled` to `false` to disable all Navigator to Navigator accepts only sessions from the current OpenCode project and only worktrees returned by OpenCode. New sessions inherit the calling session's agent, model, and variant unless explicitly overridden. It rejects self-targeting lifecycle calls, arbitrary directories, unknown V2 agent overrides, main-checkout removal, unmanaged worktrees, and removal while a worktree has active sessions. `session_send` can switch the target session's agent or model before admitting a steered prompt when the detected OpenCode protocol supports it. `session_wait` defaults to `maxWaitMs`, caps requested timeouts at `maxWaitMs`, and treats `timeoutMs: 0` as an immediate snapshot. Navigator never force-removes or automatically cleans up resources after a partial failure. -When OpenCode exposes experimental workspace adapters, Kompass registers a `rift` workspace adapter backed by its bundled `rift-snapshot` dependency. Navigator automatically uses that adapter for `new_worktree` sessions when no `startCommand` is requested, falling back to Git worktrees only when the experimental API or Rift adapter is unavailable. +On OpenCode 1, when experimental workspace adapters are available, Kompass registers a `rift` workspace adapter backed by its bundled `rift-snapshot` dependency. `worktree_create` uses that adapter when no `startCommand` is requested, using Git worktrees otherwise. Pass its returned directory to `session_create`. Kompass preserves the OpenCode workspace ID when it creates future sessions in Rift workspaces. Existing sessions without workspace identity are not migrated automatically. Rift workspaces are experimental OpenCode workspaces, not legacy Desktop sandboxes, and Kompass does not modify `project.sandboxes` or OpenCode's workspace database. 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..b997a5f 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, + effect: OpenCodeCompassPluginV2.effect, }; -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/kompass.jsonc b/packages/opencode/kompass.jsonc index 047f44a..fddc71c 100644 --- a/packages/opencode/kompass.jsonc +++ b/packages/opencode/kompass.jsonc @@ -54,8 +54,9 @@ "ticket_load": { "enabled": true }, "ticket_sync": { "enabled": true }, "worktree_list": { "enabled": true }, + "worktree_create": { "enabled": true }, "session_create": { "enabled": true }, - "session_list": { "enabled": true }, + "session_list": { "enabled": true }, // OpenCode 1 only "session_read": { "enabled": true }, "session_send": { "enabled": true }, "session_wait": { "enabled": true }, @@ -88,7 +89,7 @@ "agentMode": "all", "navigator": { "enabled": true, - "maxConcurrentSessions": 8, + "maxConcurrentSessions": 8, // OpenCode 1 only; V2 plugins lack active-session discovery "maxReadChars": 20000, "maxOutputCharsPerItem": 4000, "maxWaitMs": 120000, 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-v2.ts b/packages/opencode/navigator-v2.ts new file mode 100644 index 0000000..f781120 --- /dev/null +++ b/packages/opencode/navigator-v2.ts @@ -0,0 +1,252 @@ +import { Agent, Location, Model, Worktree, type Plugin } from "@opencode/plugin/effect"; +import { Session } from "@opencode/schema/session"; +import { SessionMessage } from "@opencode/schema/session-message"; +import { Tool } from "@opencode/schema/tool"; +import { DateTime, Effect, Option, Schema } from "effect"; +import path from "node:path"; +import { z } from "zod"; +import { V2_NAVIGATOR_TOOL_NAMES, type MergedKompassConfig, type NavigatorConfig } from "../core/index.ts"; +import { getConfiguredOpenCodeToolName } from "./tool-names.ts"; + +const guidance = "Use only when the user explicitly asks to create or manage native OpenCode sessions, worktrees, or a multi-session workflow. Use OpenCode's subagent tool for ordinary delegation."; +const sessionIDSchema = z.string().startsWith("ses_"); +const modelSchema = z.object({ + providerID: z.string().min(1), + id: z.string().min(1), + variant: z.string().min(1).optional(), +}); +const deliverySchema = z.enum(["steer", "queue"]).default("steer"); +const worktreeCreateSchema = z.object({ + name: z.string().min(1).optional(), + strategy: z.enum(["rift", "git"]).optional(), + branch: z.string().min(1).optional(), +}); + +function message(error: unknown) { + return error instanceof Error ? error.message : String(error); +} + +function summary(session: Session.Info) { + return { + sessionID: session.id, + projectID: session.projectID, + directory: session.location.directory, + workspaceID: session.location.workspaceID, + title: session.title, + agent: session.agent, + model: session.model, + outcome: session.outcome, + createdAt: DateTime.toEpochMillis(session.time.created), + updatedAt: DateTime.toEpochMillis(session.time.updated), + }; +} + +export function summarizeV2Messages( + messages: readonly SessionMessage.Info[], + config: NavigatorConfig, + options: { turnLimit?: number; includeOutputs?: boolean; maxOutputCharsPerItem?: number } = {}, +) { + const perItem = Math.min(options.maxOutputCharsPerItem ?? config.maxOutputCharsPerItem, config.maxOutputCharsPerItem, config.maxReadChars); + let truncated = false; + const text = (value: string) => { + if (value.length > perItem) truncated = true; + return value.slice(0, perItem); + }; + const selected = messages.slice(-(options.turnLimit ?? 20)).map((entry) => ({ + id: entry.id, + type: entry.type, + createdAt: DateTime.toEpochMillis(entry.time.created), + ...("text" in entry && typeof entry.text === "string" ? { text: text(entry.text) } : {}), + ...(entry.type === "assistant" ? { + error: entry.error?.message, + items: entry.content.map((item) => { + if (item.type !== "tool") return { type: item.type, text: text(item.text) }; + return { + type: "tool", + name: item.name, + status: item.state.status, + ...(item.state.status === "error" ? { error: text(item.state.error.message) } : {}), + ...(options.includeOutputs && "content" in item.state ? { + output: text((item.state.content ?? []).flatMap((part) => part.type === "text" ? [part.text] : []).join("\n")), + } : {}), + }; + }), + } : {}), + })); + while (selected.length && JSON.stringify(selected).length > config.maxReadChars) { + selected.shift(); + truncated = true; + } + return { messages: selected, truncation: { truncated: truncated || selected.length < messages.length, totalMessages: messages.length, returnedMessages: selected.length } }; +} + +export function createNavigatorV2Tools(ctx: Plugin.Context, config: MergedKompassConfig): Tool.Info[] { + if (!config.adapters.opencode.navigator.enabled) return []; + const limits = config.adapters.opencode.navigator; + const createWorktree = Effect.fn("navigator.createWorktree")(function* (location: Location.Ref, input: z.infer) { + if (location.workspaceID) return yield* Effect.fail(new Error("Worktree creation requires a local session")); + if (input.branch && input.strategy !== "git") { + return yield* Effect.fail(new Error("A starting branch/ref requires strategy: git; Rift snapshots the source checkout")); + } + return yield* ctx.worktree.create({ location, name: input.name, strategy: input.strategy ? Worktree.StrategyID.make(input.strategy) : undefined, branch: input.branch }); + }); + const owned = Effect.fn("navigator.owned")(function* (sessionID: string) { + const session = yield* ctx.session.get({ sessionID: Session.ID.make(sessionID) }); + if (session.projectID !== ctx.location.project.id) { + return yield* Effect.fail(new Error(`Session ${sessionID} belongs to another OpenCode project`)); + } + return session; + }); + const target = Effect.fn("navigator.target")(function* (sessionID: string, callerID: string) { + if (sessionID === callerID) return yield* Effect.fail(new Error("Navigator cannot send to, wait for, or interrupt its calling session")); + return yield* owned(sessionID); + }); + const validateSelection = Effect.fn("navigator.validateSelection")(function* (location: Location.Ref, agent?: string, model?: z.infer) { + if (agent) { + yield* ctx.agent.get({ agentID: Agent.ID.make(agent), location }); + } + if (model) { + const models = yield* ctx.catalog.model.list({ location }); + const selected = models.data.find((item) => item.providerID === model.providerID && item.id === model.id && item.enabled); + if (!selected) return yield* Effect.fail(new Error(`Unknown or disabled OpenCode model ${model.providerID}/${model.id}`)); + if (model.variant && !selected.variants.some((variant) => variant.id === model.variant)) { + return yield* Effect.fail(new Error(`Unknown model variant ${model.variant}`)); + } + } + }); + const read = Effect.fn("navigator.read")(function* (sessionID: string, options: Parameters[2] = {}) { + const session = yield* owned(sessionID); + const messages = yield* ctx.session.context({ sessionID: session.id }); + return { session: summary(session), ...summarizeV2Messages(messages, limits, options) }; + }); + function tool( + name: (typeof V2_NAVIGATOR_TOOL_NAMES)[number], + description: string, + input: S, + execute: (args: z.output, invocation: Tool.Context) => Effect.Effect, + ): Tool.Info { + return { + name: getConfiguredOpenCodeToolName(name, config.tools[name].name), + description: `${guidance} ${description}`, + input, + options: { codemode: false }, + execute: (args, invocation) => execute(args as z.output, invocation).pipe( + Effect.map((result) => ({ content: JSON.stringify(result) })), + Effect.mapError((error) => new Tool.Error({ message: message(error) })), + ), + }; + } + const definitions = { + worktree_create: tool("worktree_create", "Create a managed worktree without creating a session. Uses the caller's location as source and the registered default strategy, normally Rift. Pass the returned directory to session_create to start work there. A starting branch requires strategy: git.", worktreeCreateSchema, + Effect.fn("navigator.worktree_create")(function* (args, invocation) { + const caller = yield* owned(invocation.sessionID); + const worktree = yield* createWorktree(caller.location, args); + return { status: "created", worktree }; + })), + worktree_remove: tool("worktree_remove", "Remove a managed worktree through OpenCode. Does not delete sessions or interrupt them. V2 cannot discover active sessions: finish or interrupt known sessions using this directory first. force defaults to false and permits discarding uncommitted files when true. Rift snapshots with children cannot be removed, even with force.", z.object({ directory: z.string().min(1), force: z.boolean().default(false) }), + Effect.fn("navigator.worktree_remove")(function* (args, invocation) { + const caller = yield* owned(invocation.sessionID); + const directory = path.resolve(args.directory); + const within = (root: string) => { + const relative = path.relative(directory, root); + return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative)); + }; + if (within(ctx.location.project.canonical) || within(caller.location.directory) || within(ctx.location.directory)) { + return yield* Effect.fail(new Error("Cannot remove the checkout or a worktree containing the calling session or plugin")); + } + const inventory = yield* ctx.worktree.list({ location: caller.location }); + const selected = inventory.find((entry) => entry.strategy !== undefined && path.resolve(entry.directory) === directory); + if (!selected) return yield* Effect.fail(new Error("The requested directory is not a managed OpenCode worktree for this project")); + yield* ctx.worktree.remove({ location: caller.location, directory: selected.directory, force: args.force }); + return { status: "removed", directory: selected.directory }; + })), + worktree_list: tool("worktree_list", "List this project's checkout and native managed worktrees, including Rift snapshots.", z.object({}), + Effect.fn("navigator.worktree_list")(function* (_args, invocation) { + const caller = yield* owned(invocation.sessionID); + const inventory = yield* ctx.worktree.list({ location: caller.location }); + return { checkout: { directory: ctx.location.project.canonical }, worktrees: inventory.filter((entry) => entry.strategy !== undefined) }; + })), + session_create: tool("session_create", "Create a native session. directory defaults to the calling session's current directory; supply a checkout or managed worktree directory to work elsewhere. To use a new worktree, call worktree_create first and pass its returned directory. Optional prompt starts execution immediately; omit it to create an idle session and use session_send later.", z.object({ + prompt: z.string().min(1).optional(), + title: z.string().min(1).optional(), + directory: z.string().min(1).optional(), + agent: z.string().min(1).optional(), + model: modelSchema.optional(), + }).strict(), Effect.fn("navigator.session_create")(function* (args, invocation) { + const caller = yield* owned(invocation.sessionID); + const agent = args.agent ?? caller.agent; + const model = args.model ?? caller.model; + let location = caller.location; + const directory = args.directory ? path.resolve(caller.location.directory, args.directory) : caller.location.directory; + if (directory !== caller.location.directory) { + if (caller.location.workspaceID) return yield* Effect.fail(new Error("Selecting another directory requires a local session")); + if (directory === ctx.location.project.canonical) { + location = Location.Ref.make({ directory: ctx.location.project.canonical }); + } else { + const inventory = yield* ctx.worktree.list({ location: caller.location }); + const selected = inventory.find((entry) => entry.strategy !== undefined && path.resolve(entry.directory) === directory); + if (!selected) return yield* Effect.fail(new Error("The requested directory is not a managed OpenCode worktree for this project")); + location = Location.Ref.make({ directory: selected.directory }); + } + } + yield* validateSelection(location, agent, model); + let session: Session.Info | undefined; + let requestedSessionID: Session.ID | undefined; + let phase = "session_create"; + return yield* Effect.gen(function* () { + requestedSessionID = Session.ID.create(); + session = yield* ctx.session.create({ id: requestedSessionID, location, title: args.title, agent: agent ? Agent.ID.make(agent) : undefined, model: model ? Schema.decodeUnknownSync(Model.Ref)(model) : undefined }); + yield* invocation.progress({ sessionID: session.id, directory: session.location.directory, phase: "session_created" }); + if (session.projectID !== ctx.location.project.id || session.location.directory !== location.directory || session.location.workspaceID !== location.workspaceID) { + return yield* Effect.fail(new Error("OpenCode created the session in an unexpected project or location")); + } + phase = "prompt"; + if (!args.prompt) return { status: "created", admitted: false, session: summary(session) }; + const admitted = yield* ctx.session.prompt({ sessionID: session.id, text: args.prompt, delivery: "steer" }); + return { status: "started", admitted: true, messageID: admitted.id, session: summary(session) }; + }).pipe(Effect.catch((error) => Effect.succeed({ + status: "partial_failure", admitted: false, phase, error: message(error), + requestedSessionID, session: session ? summary(session) : undefined, + directory: location.directory, + recovery: "The session is preserved. Read the returned session ID before retrying a prompt.", + }))); + })), + session_read: tool("session_read", "Read a bounded snapshot of a current-project session's current model context, including recent messages. This is not paginated full history.", z.object({ + sessionID: sessionIDSchema, + turnLimit: z.number().int().positive().max(100).optional(), + includeOutputs: z.boolean().optional(), + maxOutputCharsPerItem: z.number().int().positive().optional(), + }), (args) => read(args.sessionID, args)), + session_send: tool("session_send", "Admit another prompt to a current-project session, with steer or queue delivery and optional agent/model changes.", z.object({ + sessionID: sessionIDSchema, prompt: z.string().min(1), delivery: deliverySchema, + agent: z.string().min(1).optional(), model: modelSchema.optional(), + }), Effect.fn("navigator.session_send")(function* (args, invocation) { + const session = yield* target(args.sessionID, invocation.sessionID); + yield* validateSelection(session.location, args.agent, args.model); + if (args.agent) yield* ctx.session.switchAgent({ sessionID: session.id, agent: Agent.ID.make(args.agent) }); + if (args.model) yield* ctx.session.switchModel({ sessionID: session.id, model: Schema.decodeUnknownSync(Model.Ref)(args.model) }); + const admitted = yield* ctx.session.prompt({ sessionID: session.id, text: args.prompt, delivery: args.delivery }); + return { sessionID: session.id, admitted: true, messageID: admitted.id, delivery: args.delivery }; + })), + session_wait: tool("session_wait", "Wait for the first target session to become idle using OpenCode's native wait. Timeout or cancellation ends only the wait, not the target sessions. Completion includes the session outcome and bounded context.", z.object({ + targets: z.array(z.object({ sessionID: sessionIDSchema })).min(1).max(8), + timeoutMs: z.number().int().positive().optional(), + }), Effect.fn("navigator.session_wait")(function* (args, invocation) { + const ids = args.targets.map((item) => item.sessionID); + if (new Set(ids).size !== ids.length) return yield* Effect.fail(new Error("Navigator wait targets must be unique")); + const sessions = yield* Effect.all(ids.map((id) => target(id, invocation.sessionID)), { concurrency: "unbounded" }); + const completed = yield* Effect.raceAllFirst(sessions.map((session) => ctx.session.wait({ sessionID: session.id }).pipe(Effect.as(session.id)))).pipe( + Effect.timeoutOption(Math.min(args.timeoutMs ?? limits.maxWaitMs, limits.maxWaitMs)), + ); + if (Option.isNone(completed)) return { status: "timeout", targets: args.targets }; + return { status: "completed", completed: yield* read(completed.value) }; + })), + session_interrupt: tool("session_interrupt", "Interrupt a current-project session through OpenCode's native lifecycle API.", z.object({ sessionID: sessionIDSchema }), + Effect.fn("navigator.session_interrupt")(function* (args, invocation) { + const session = yield* target(args.sessionID, invocation.sessionID); + const result = yield* ctx.session.interrupt({ sessionID: session.id, continue: false }); + return { sessionID: session.id, ...result }; + })), + }; + return V2_NAVIGATOR_TOOL_NAMES.filter((name) => config.tools[name].enabled !== false).map((name) => definitions[name]); +} diff --git a/packages/opencode/navigator.ts b/packages/opencode/navigator.ts index 8f79a60..d036f97 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, @@ -74,6 +73,7 @@ export interface MessageSummary { type NavigatorToolName = | "worktree_list" + | "worktree_create" | "session_create" | "session_list" | "session_read" @@ -237,33 +237,6 @@ async function listManagedWorktrees(client: NavigatorClient, checkout: string, p return [...unique.values()]; } -async function removeCreatedWorkspace(client: NavigatorClient, checkout: string, workspace: NativeWorktree) { - if (workspace.type === "rift") { - const api = workspaceApi(client); - if (!api?.workspace?.remove || !workspace.id) throw new Error("OpenCode Rift workspace removal is unavailable"); - const response = await api.workspace.remove({ id: workspace.id, directory: checkout }); - if (response.error !== undefined) failResponse(response.error, `OpenCode Rift workspace rollback for ${workspace.directory}`); - return; - } - responseData(await client.worktree.remove({ - directory: checkout, - worktreeRemoveInput: { directory: workspace.directory }, - }), `OpenCode worktree rollback for ${workspace.directory}`); -} - -async function rollbackCreatedWorkspaces(client: NavigatorClient, checkout: string, workspaces: NativeWorktree[]) { - const unique = new Map(workspaces.map((item) => [normalizeDirectory(item.directory), item])); - const failures: string[] = []; - for (const workspace of unique.values()) { - try { - await removeCreatedWorkspace(client, checkout, workspace); - } catch (error) { - failures.push(`${workspace.directory}: ${error instanceof Error ? error.message : String(error)}`); - } - } - return failures; -} - async function activeSessionIDs(client: NavigatorClient, navigator: NavigatorContext) { if (navigator.protocol === "v1") { const worktrees = await listManagedWorktrees(client, navigator.checkout, navigator.projectID); @@ -563,11 +536,44 @@ export function createNavigatorTools( input: NavigatorContext & { client: NavigatorClient }, ): Record { const { client, ...navigator } = input; + async function createManagedWorktree(args: { name?: string; startCommand?: string }): Promise { + const api = workspaceApi(client); + if (!args.startCommand && await hasRiftAdapter(client, navigator.checkout)) { + if (!api?.workspace?.create) throw new Error("Rift workspace creation is unavailable"); + const workspace = responseData(await api.workspace.create({ + directory: navigator.checkout, type: "rift", extra: args.name ? { name: args.name } : undefined, + }), "OpenCode Rift workspace create"); + const worktree = normalizeRiftWorkspace(workspace); + if (!worktree) throw new Error("OpenCode Rift workspace create returned no directory"); + return worktree; + } + return normalizeWorktree(responseData(await client.worktree.create({ + directory: navigator.checkout, + worktreeCreateInput: { + ...(args.name ? { name: args.name } : {}), + ...(args.startCommand ? { startCommand: args.startCommand } : {}), + }, + }), "OpenCode worktree create")); + } const agentNames = [...new Set(navigator.agentNames ?? [])].sort(); const agentSchema = agentNames.length ? tool.schema.enum(agentNames as [string, ...string[]]) : tool.schema.string().min(1); return { + worktree_create: tool({ + description: `${explicitNavigatorUse} Create a managed worktree without a session. Pass its returned directory to session_create. Prefers Rift when available; startCommand selects Git.`, + args: { + name: tool.schema.string().min(1).optional(), + startCommand: tool.schema.string().min(1).optional(), + }, + async execute(args) { + const worktree = await createManagedWorktree(args); + if (worktree.projectID && worktree.projectID !== navigator.projectID) { + throw new Error(`Created workspace ${worktree.directory} belongs to another project; it was preserved for recovery`); + } + return json({ status: "created", worktree }); + }, + }), worktree_list: tool({ description: `${explicitNavigatorUse} List the current OpenCode project checkout and its managed native worktrees.`, args: {}, @@ -580,21 +586,10 @@ export function createNavigatorTools( }), session_create: tool({ - description: `${explicitNavigatorUse} Create and asynchronously prompt a native OpenCode session in the checkout or a managed worktree.`, + description: `${explicitNavigatorUse} Create a native session. directory defaults to the calling session's current directory. To use a new worktree, call worktree_create first and pass its returned directory. Optional prompt starts execution immediately; omit it to create an idle session and use session_send later.`, args: { - prompt: tool.schema.string().min(1), - environment: tool.schema.discriminatedUnion("type", [ - tool.schema.object({ type: tool.schema.literal("checkout") }), - tool.schema.object({ - type: tool.schema.literal("existing_worktree"), - directory: tool.schema.string().min(1), - }), - tool.schema.object({ - type: tool.schema.literal("new_worktree"), - name: tool.schema.string().min(1).optional(), - startCommand: tool.schema.string().min(1).optional(), - }), - ]), + prompt: tool.schema.string().min(1).optional(), + directory: tool.schema.string().min(1).optional(), agent: agentSchema.describe("Registered OpenCode agent; omit to inherit the caller/default agent").optional(), model: tool.schema.object({ providerID: tool.schema.string().min(1), @@ -603,14 +598,18 @@ export function createNavigatorTools( }).optional(), }, async execute(args, context) { - const active = await activeSessionIDs(client, navigator); + if ("environment" in args) throw new Error("environment is no longer supported; call worktree_create separately and pass directory"); + if (navigator.protocol === "v1" && !args.prompt && (args.agent || args.model)) { + throw new Error("The legacy session API selects agent/model when prompting. Supply prompt or pass those overrides to session_send later."); + } + const active = args.prompt ? await activeSessionIDs(client, navigator) : new Set(); const activeSessions = await Promise.all([...active].map((sessionID) => getSession(client, sessionID))); const activeOwnedCount = activeSessions.filter((session) => session.projectID === navigator.projectID).length; if (activeOwnedCount >= navigator.config.maxConcurrentSessions) { throw new Error(`Navigator allows at most ${navigator.config.maxConcurrentSessions} concurrent sessions`); } - const caller = await getOwnedSession(client, navigator.projectID, context.sessionID).catch(() => undefined); + const caller = await getOwnedSession(client, navigator.projectID, context.sessionID); const selectedAgent = args.agent ?? caller?.agent; const selectedModel = args.model ?? (caller?.model ? { @@ -620,10 +619,11 @@ export function createNavigatorTools( } : undefined); - let location: NavigatorLocation = { directory: navigator.checkout }; - let createdWorktree: NativeWorktree | undefined; - if (args.environment.type === "existing_worktree") { - const requestedDirectory = args.environment.directory; + let location: NavigatorLocation = caller.location; + const requestedDirectory = args.directory ? path.resolve(caller.location.directory, args.directory) : caller.location.directory; + if (!sameDirectory(requestedDirectory, caller.location.directory) && sameDirectory(requestedDirectory, navigator.checkout)) { + location = { directory: navigator.checkout }; + } else if (!sameDirectory(requestedDirectory, caller.location.directory)) { const worktrees = await listManagedWorktrees(client, navigator.checkout, navigator.projectID); const requested = worktrees.find((item) => sameDirectory(item.directory, requestedDirectory)); if (!requested) throw new Error("The requested directory is not a managed OpenCode workspace for this project"); @@ -631,60 +631,6 @@ export function createNavigatorTools( directory: requested.directory, ...(requested.workspaceID ? { workspaceID: requested.workspaceID } : {}), }; - } else if (args.environment.type === "new_worktree") { - const worktreesBefore = await listManagedWorktrees(client, navigator.checkout, navigator.projectID); - try { - const api = workspaceApi(client); - const useRift = !args.environment.startCommand && await hasRiftAdapter(client, navigator.checkout); - if (useRift) { - if (!api?.workspace?.create) { - throw new Error("OpenCode advertises the Rift workspace adapter but workspace creation is unavailable"); - } - const workspace = responseData(await api.workspace.create({ - directory: navigator.checkout, - type: "rift", - extra: args.environment.name ? { name: args.environment.name } : undefined, - }), "OpenCode Rift workspace create"); - const normalized = normalizeRiftWorkspace(workspace); - if (!normalized) throw new Error("OpenCode Rift workspace create returned no directory"); - createdWorktree = normalized; - if (normalized.projectID && normalized.projectID !== navigator.projectID) { - throw new Error( - `OpenCode registered workspace ${normalized.id} to project ${normalized.projectID}; expected ${navigator.projectID}`, - ); - } - } else { - const worktree = responseData( - await client.worktree.create({ - directory: navigator.checkout, - worktreeCreateInput: { - ...(args.environment.name ? { name: args.environment.name } : {}), - ...(args.environment.startCommand ? { startCommand: args.environment.startCommand } : {}), - }, - }), - "OpenCode worktree create", - ); - createdWorktree = normalizeWorktree(worktree); - } - location = { - directory: createdWorktree.directory, - ...(createdWorktree.workspaceID ? { workspaceID: createdWorktree.workspaceID } : {}), - }; - } catch (error) { - const worktreesAfter = await listManagedWorktrees(client, navigator.checkout, navigator.projectID).catch(() => []); - const before = new Set(worktreesBefore.map((item) => normalizeDirectory(item.directory))); - const partial = worktreesAfter.filter((item) => !before.has(normalizeDirectory(item.directory))); - const created = createdWorktree ? [createdWorktree, ...partial] : partial; - const rollbackFailures = await rollbackCreatedWorkspaces(client, navigator.checkout, created); - throw new Error( - `Navigator could not create the requested workspace: ${error instanceof Error ? error.message : String(error)}` + - (created.length - ? rollbackFailures.length - ? `. Rollback failed for ${rollbackFailures.join("; ")}` - : ". The partially created workspace was removed" - : ""), - ); - } } let session: SessionV2Info | { id: string; projectID: string }; @@ -717,16 +663,8 @@ export function createNavigatorTools( "OpenCode session create", ); } catch (error) { - const rollbackFailures = createdWorktree - ? await rollbackCreatedWorkspaces(client, navigator.checkout, [createdWorktree]) - : []; throw new Error( - `Navigator session creation failed: ${error instanceof Error ? error.message : String(error)}` + - (createdWorktree - ? rollbackFailures.length - ? `. Workspace created at ${createdWorktree.directory}, but rollback failed: ${rollbackFailures.join("; ")}` - : `. Workspace ${createdWorktree.directory} was rolled back` - : ""), + `Navigator session creation failed: ${error instanceof Error ? error.message : String(error)}`, ); } const createdLocation = navigator.protocol === "v2" @@ -744,20 +682,12 @@ export function createNavigatorTools( ? `belongs to project ${session.projectID}; expected ${navigator.projectID}` : undefined; if (validationError) { - const rollbackFailures = createdWorktree - ? await rollbackCreatedWorkspaces(client, navigator.checkout, [createdWorktree]) - : []; throw new Error( - `Created session ${session.id} ${validationError}. It was not prompted` + - (createdWorktree - ? rollbackFailures.length - ? `. Workspace rollback failed: ${rollbackFailures.join("; ")}` - : `. Workspace ${createdWorktree.directory} was rolled back` - : ""), + `Created session ${session.id} ${validationError}. It was not prompted`, ); } - try { + if (args.prompt) try { if (navigator.protocol === "v1") { const response = await navigator.legacyClient(location.directory).session.promptAsync({ path: { id: session.id }, @@ -785,18 +715,16 @@ export function createNavigatorTools( } } catch (error) { throw new Error( - `Navigator prompt admission failed: ${error instanceof Error ? error.message : String(error)}. Session created: ${session.id}` + - (createdWorktree ? `. Worktree created: ${createdWorktree.name} (${createdWorktree.directory})` : ""), + `Navigator prompt admission failed: ${error instanceof Error ? error.message : String(error)}. Session created: ${session.id}`, ); } return json({ + status: args.prompt ? "started" : "created", + admitted: Boolean(args.prompt), sessionID: session.id, directory: location.directory, ...(location.workspaceID ? { workspaceID: location.workspaceID } : {}), - ...(createdWorktree - ? { worktree: { created: true, type: createdWorktree.type, name: createdWorktree.name, ...(createdWorktree.id ? { id: createdWorktree.id } : {}), ...(createdWorktree.workspaceID ? { workspaceID: createdWorktree.workspaceID } : {}), ...(createdWorktree.branch ? { branch: createdWorktree.branch } : {}) } } - : {}), }); }, }), diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b1c0bca..d91445a 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -46,16 +46,15 @@ "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/plugin": "0.0.0-beta-19381", + "@opencode/schema": "0.0.0-beta-19381", + "@opencode-ai/sdk": "^1.18.7", + "effect": "4.0.0-rc.112", + "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/rift-workspace.ts b/packages/opencode/rift-workspace.ts index 69ec040..21e6249 100644 --- a/packages/opencode/rift-workspace.ts +++ b/packages/opencode/rift-workspace.ts @@ -1,4 +1,5 @@ import path from "node:path"; +import { createRiftSnapshot, loadRift, type RiftBackend } from "./rift.ts"; type WorkspaceInfo = { id: string; @@ -22,12 +23,7 @@ type WorkspaceAdapter = { target(config: WorkspaceInfo): WorkspaceTarget | Promise; }; -type RiftModule = { - init(options?: { at?: string }): null; - create(options?: { from?: string; name?: string; copyAll?: boolean; hooks?: boolean }): string; - remove(options?: { at?: string; all?: false }): void; - list(options?: { of?: string }): string[]; -}; +type RiftModule = Pick; type RiftAdapterOptions = { sourceDirectory: string; @@ -46,8 +42,6 @@ type Logger = { warn(message: string, extra?: Record): void; }; -const importModule = new Function("specifier", "return import(specifier)") as (specifier: string) => Promise; - function slugify(input: string) { return input .trim() @@ -138,12 +132,9 @@ export function createRiftWorkspaceAdapter(rift: RiftModule, options: RiftAdapte requireManagedDirectory(sourceDirectory, source, "creation source"); } const expected = requireManagedDirectory(sourceDirectory, config.directory, "creation target"); - rift.init({ at: source }); - const created = path.resolve(rift.create({ + const created = path.resolve(createRiftSnapshot(rift, { from: source, name: config.name, - // Use Rift's APFS full-clone path; the filtered walker can fail on protected macOS xattrs. - copyAll: true, })); if (created !== expected) { try { @@ -195,7 +186,7 @@ export async function registerRiftWorkspaceAdapter(input: ExperimentalWorkspaceI let rift: RiftModule; try { - rift = await importModule("rift-snapshot") as RiftModule; + rift = await loadRift(); } catch (error) { logger.info("Rift workspace adapter not registered", { reason: "Install rift-snapshot to enable the experimental Rift workspace adapter", diff --git a/packages/opencode/rift-worktree.ts b/packages/opencode/rift-worktree.ts new file mode 100644 index 0000000..28a4981 --- /dev/null +++ b/packages/opencode/rift-worktree.ts @@ -0,0 +1,98 @@ +import { Worktree } from "@opencode/plugin/effect"; +import type { WorktreeDefinition } from "@opencode/plugin/effect/worktree"; +import { Effect, Predicate } from "effect"; +import { execFile } from "node:child_process"; +import { realpath } from "node:fs/promises"; +import path from "node:path"; +import { promisify } from "node:util"; +import { createRiftSnapshot, loadRift, type RiftBackend } from "./rift.ts"; + +const execFileAsync = promisify(execFile); +const resolveDirectory = (directory: string) => Effect.tryPromise({ try: () => realpath(directory), catch: (error) => error }); + +export function createRiftWorktreeStrategy( + backend: Effect.Effect = Effect.tryPromise({ try: loadRift, catch: (error) => error }), +): WorktreeDefinition { + return { + id: "rift", + create: Effect.fn("rift.create")(function* (input) { + if (input.branch !== undefined) { + return yield* Effect.fail(new Worktree.OperationError({ + message: "Rift snapshots the source checkout and cannot select a starting branch or ref. Use the git strategy for a starting ref.", + })); + } + const rift = yield* backend; + const name = path.basename(input.directory); + // Rift's FFI operation is synchronous; capture its failures in the Effect + // error channel and return the created directory before yielding again. + return yield* Effect.try({ try: () => { + for (let attempt = 1; ; attempt++) { + try { + const directory = createRiftSnapshot(rift, { + from: input.sourceDirectory, + name: attempt === 1 ? name : `${name}-${attempt}`, + }); + return { directory: path.resolve(directory) }; + } catch (error) { + // OpenCode checks its suggested destination; Rift owns a different + // storage directory, so resolve collisions reported by Rift there. + if (!(error instanceof Error) || !Predicate.hasProperty(error, "code")) throw error; + const targetsSource = error.code === "inside_source" && Predicate.hasProperty(error, "path") + && Predicate.isString(error.path) && path.resolve(error.path) === path.resolve(input.sourceDirectory); + if ((error.code !== "already_exists" && !targetsSource) || attempt >= 100) throw error; + } + } + }, catch: (error) => error }); + }), + list: Effect.fn("rift.list")(function* (sourceDirectory) { + const rift = yield* backend; + const ancestors = yield* Effect.try({ try: () => rift.ancestors({ of: sourceDirectory }), catch: (error) => error }).pipe( + Effect.catch((error) => error instanceof Error && Predicate.hasProperty(error, "code") && error.code === "workspace_not_initialized" + ? Effect.succeed(undefined) : Effect.fail(error)), + ); + if (!ancestors) return []; + const root = yield* resolveDirectory(ancestors.at(-1) ?? sourceDirectory); + const entries: Array<{ directory: string; type: "root" | "worktree" }> = [{ directory: root, type: "root" }]; + const seen = new Set([root]); + // Rift lists immediate children; OpenCode needs siblings and descendants. + for (const entry of entries) { + const children = yield* Effect.try({ try: () => rift.list({ of: entry.directory }), catch: (error) => error }); + for (const child of children) { + const directory = yield* resolveDirectory(child).pipe(Effect.catch((error) => Predicate.hasProperty(error, "code") && error.code === "ENOENT" + ? Effect.succeed(undefined) : Effect.fail(error))); + if (!directory || seen.has(directory)) continue; + seen.add(directory); + entries.push({ directory, type: "worktree" }); + } + } + return entries; + }), + remove: Effect.fn("rift.remove")(function* (input) { + const rift = yield* backend; + const directory = yield* resolveDirectory(input.directory); + yield* Effect.try({ try: () => { + const parent = rift.ancestors({ of: directory })[0]; + if (!parent || !rift.list({ of: parent }).some((child) => path.resolve(child) === directory)) { + throw new Worktree.OperationError({ message: `Not a Rift snapshot root: ${directory}` }); + } + }, catch: (error) => error }); + if (!input.force) { + const { stdout } = yield* Effect.tryPromise({ + try: (signal) => execFileAsync("git", ["-C", directory, "status", "--porcelain", "--untracked-files=all"], { signal }), + catch: (error) => error, + }); + if (stdout.trim()) { + return yield* Effect.fail(new Worktree.OperationError({ message: "Rift snapshot has uncommitted changes.", forceRequired: true })); + } + } + // Check children and remove in one synchronous operation, after Git's + // async status call, to avoid cascading into newly created snapshots. + yield* Effect.try({ try: () => { + if (rift.list({ of: directory }).length) { + throw new Worktree.OperationError({ message: "Remove this Rift snapshot's child snapshots first." }); + } + rift.remove({ at: directory }); + }, catch: (error) => error }); + }), + }; +} diff --git a/packages/opencode/rift.ts b/packages/opencode/rift.ts new file mode 100644 index 0000000..8eb4075 --- /dev/null +++ b/packages/opencode/rift.ts @@ -0,0 +1,18 @@ +import type * as Rift from "rift-snapshot"; + +export type RiftBackend = Pick & { + remove(options?: Rift.RemoveOptions & { all?: false }): void; +}; + +export function loadRift(): Promise { + return import("rift-snapshot"); +} + +export function createRiftSnapshot( + rift: Pick, + options: Rift.CreateOptions, +) { + rift.init({ at: options.from }); + // Keep dependencies and working-tree state. On APFS this uses the full clone path. + return rift.create({ ...options, copyAll: true }); +} diff --git a/packages/opencode/scripts/build.ts b/packages/opencode/scripts/build.ts index 300c46b..f0320e1 100644 --- a/packages/opencode/scripts/build.ts +++ b/packages/opencode/scripts/build.ts @@ -14,8 +14,10 @@ const packageReadme = path.join(packageRoot, "README.md"); const runtimeDirs = ["agents", "commands", "components"] as const; const bundleExternals = [ - "@opencode-ai/plugin", - "@opencode-ai/plugin/tool", + "@opencode/plugin", + "@opencode/schema", + "rift-snapshot", + "effect", "@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..9bf6ffc --- /dev/null +++ b/packages/opencode/shared-tools.ts @@ -0,0 +1,310 @@ +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, + type ToolExecutionContext, +} 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: Context): Promise; +}) { + return input; +} + +tool.schema = z; + +export type ToolDefinition = ReturnType>; +export type CoreToolDefinition = ReturnType>; + +const execFileAsync = promisify(execFile); + +type OpenCodeToolCreator = ( + config: MergedKompassConfig, + shell: Shell, +) => CoreToolDefinition; + +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; + #signal?: AbortSignal; + + constructor(command: string, cwd: string, signal?: AbortSignal) { + this.#command = command; + this.#cwd = cwd; + this.#signal = signal; + } + + 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() { + this.#signal?.throwIfAborted(); + try { + const { stdout, stderr } = await execFileAsync("/bin/bash", ["-lc", this.#command], { + cwd: this.#cwd, + encoding: "utf8", + maxBuffer: 10 * 1024 * 1024, + signal: this.#signal, + }); + + this.write(stdout, stderr); + return shellResult(stdout, stderr, 0); + } catch (error) { + // Cancellation must stop the workflow even for a nothrow command. + this.#signal?.throwIfAborted(); + 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, signal?: AbortSignal): 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, signal) 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: ToolExecutionContext) => 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: ToolExecutionContext) => 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: ToolExecutionContext) => 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: ToolExecutionContext) => 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: ToolExecutionContext) => 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: ToolExecutionContext) => definition.execute(args, context), + }); + }, +}; + +export function createCoreOpenCodeToolDefinitions( + config: MergedKompassConfig, + projectRoot: string, + signal?: AbortSignal, +): Record { + const shell = createNodeShell(projectRoot, signal); + 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/navigator-v2.test.ts b/packages/opencode/test/navigator-v2.test.ts new file mode 100644 index 0000000..16641c8 --- /dev/null +++ b/packages/opencode/test/navigator-v2.test.ts @@ -0,0 +1,325 @@ +import { describe, test } from "node:test"; +import assert from "node:assert/strict"; +import { Agent, Location, Model, type Plugin } from "@opencode/plugin/effect"; +import { Session } from "@opencode/schema/session"; +import { SessionMessage } from "@opencode/schema/session-message"; +import { SessionInbox } from "@opencode/schema/session-inbox"; +import { Tool } from "@opencode/schema/tool"; +import { Deferred, Effect, Fiber, Schema, type Types } from "effect"; +import { z } from "zod"; +import { mergeWithDefaults } from "../../core/index.ts"; +import { createNavigatorV2Tools, summarizeV2Messages } from "../navigator-v2.ts"; + +const model = Schema.decodeUnknownSync(Model.Ref)({ providerID: "test", id: "model", variant: "high" }); +function session(id = "ses_caller", directory = "/repo", projectID = "project") { + return Schema.decodeUnknownSync(Session.Info)({ + id, projectID, location: { directory }, agent: "worker", model, + title: "Task", cost: 0, tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + time: { created: 1, updated: 2 }, + }); +} + +function fixture() { + const calls: Array<{ name: string; input: unknown }> = []; + const sessions = new Map([ + ["ses_caller", session()], ["ses_target", session("ses_target", "/snapshots/task")], + ["ses_other", session("ses_other", "/snapshots/other")], ["ses_foreign", session("ses_foreign", "/foreign", "foreign")], + ]); + const config = mergeWithDefaults({}); + const location = Schema.decodeUnknownSync(Location.Info)({ directory: "/repo", project: { id: "project", directory: "/repo", canonical: "/repo" } }); + const methods: Types.Mutable> = { + get: ({ sessionID }) => Effect.suspend(() => { + const value = sessions.get(sessionID); + return value ? Effect.succeed(value) : Effect.fail(new Error("Missing session")); + }), + create: (input) => Effect.sync(() => { + calls.push({ name: "create", input }); + const created = { ...session(input!.id!, input!.location!.directory), agent: input?.agent, model: input?.model, title: input?.title }; + sessions.set(created.id, created); + return created; + }), + prompt: (input) => Effect.sync(() => { + calls.push({ name: "prompt", input }); + return Schema.decodeUnknownSync(SessionInbox.User)({ id: "msg_prompt", sessionID: input.sessionID, timeCreated: 3, type: "user", delivery: input.delivery, payload: { text: input.text } }); + }), + context: () => Effect.succeed([]), + wait: ({ sessionID }) => Effect.sync(() => { calls.push({ name: "wait", input: sessionID }); }), + interrupt: (input) => Effect.sync(() => { calls.push({ name: "interrupt", input }); return { interrupted: true }; }), + switchAgent: (input) => Effect.sync(() => { calls.push({ name: "switchAgent", input }); }), + switchModel: (input) => Effect.sync(() => { calls.push({ name: "switchModel", input }); }), + }; + const worktree: Types.Mutable> = { + create: (input) => Effect.sync(() => { + calls.push({ name: "worktree", input }); + return Schema.decodeUnknownSync(Location.Ref)({ directory: "/snapshots/new" }); + }), + list: () => Effect.succeed([ + { directory: location.directory }, + { directory: Schema.decodeUnknownSync(Location.Ref)({ directory: "/snapshots/task" }).directory, strategy: "rift" }, + ]), + }; + const ctx = { + location, session: methods, worktree, + agent: { get: ({ agentID }: { agentID: Agent.ID }) => agentID === "missing" + ? Effect.fail(new Error("Unknown agent")) : Effect.succeed({ location, data: Agent.Info.default(agentID) }) }, + catalog: { model: { list: () => Effect.succeed({ location, data: [{ id: model.id, providerID: model.providerID, enabled: true, variants: [{ id: "high" }] }] }) } }, + } as unknown as Plugin.Context; + const invocation = { + sessionID: Session.ID.make("ses_caller"), agent: Agent.ID.make("worker"), + messageID: SessionMessage.ID.make("msg_caller"), id: Tool.CallID.make("call"), + progress: (input) => Effect.sync(() => { calls.push({ name: "progress", input }); }), + } satisfies Tool.Context; + function execute(name: string, args: unknown) { + const tool = createNavigatorV2Tools(ctx, config).find((entry) => entry.name === `kompass_${name}`)!; + return tool.execute((tool.input as z.ZodType).parse(args), invocation).pipe(Effect.map((result) => JSON.parse(String(result.content)))); + } + return { ctx, config, calls, sessions, methods, worktree, invocation, execute }; +} + +describe("native V2 Navigator", () => { + test("creates a standalone worktree without creating or prompting a session", async () => { + const f = fixture(); + const result = await Effect.runPromise(f.execute("worktree_create", { name: "task" })); + assert.equal(result.status, "created"); + assert.equal(result.worktree.directory, "/snapshots/new"); + assert.deepEqual(f.calls.map((call) => call.name), ["worktree"]); + await assert.rejects(Effect.runPromise(f.execute("worktree_create", { branch: "main" })), /requires strategy/); + }); + + test("creates an idle session then admits a separate prompt", async () => { + const f = fixture(); + const result = await Effect.runPromise(f.execute("session_create", {})); + assert.equal(result.status, "created"); + assert.equal(result.admitted, false); + assert.equal(f.calls.some((call) => call.name === "prompt"), false); + const sent = await Effect.runPromise(f.execute("session_send", { sessionID: result.session.sessionID, prompt: "Begin" })); + assert.equal(sent.admitted, true); + }); + + test("native removal validates inventory and caller paths and forwards force", async () => { + const f = fixture(); + const calls: unknown[] = []; + Object.assign(f.worktree, { remove: (input: unknown) => Effect.sync(() => { calls.push(input); }) }); + for (const directory of ["/repo", "/", "/unknown"]) { + await assert.rejects(Effect.runPromise(f.execute("worktree_remove", { directory }))); + } + assert.deepEqual(calls, []); + const result = await Effect.runPromise(f.execute("worktree_remove", { directory: "/snapshots/task" })); + assert.equal(result.status, "removed"); + assert.deepEqual(calls[0], { location: f.sessions.get("ses_caller")!.location, directory: "/snapshots/task", force: false }); + await Effect.runPromise(f.execute("worktree_remove", { directory: "/snapshots/task", force: true })); + assert.equal((calls[1] as { force: boolean }).force, true); + f.sessions.set("ses_caller", session("ses_caller", "/snapshots/task/subdir")); + await assert.rejects(Effect.runPromise(f.execute("worktree_remove", { directory: "/snapshots/task", force: true })), /calling session/); + assert.equal(calls.length, 2); + }); + test("registers supported tools with aliases, individual disables, and the feature toggle", () => { + const f = fixture(); + assert.deepEqual(createNavigatorV2Tools(f.ctx, f.config).map((tool) => tool.name), [ + "kompass_worktree_list", "kompass_worktree_create", "kompass_worktree_remove", "kompass_session_create", "kompass_session_read", "kompass_session_send", "kompass_session_wait", "kompass_session_interrupt", + ]); + f.config.tools.session_create.name = "start_work"; + f.config.tools.session_read.enabled = false; + const names = createNavigatorV2Tools(f.ctx, f.config).map((tool) => tool.name); + assert.ok(names.includes("start_work")); + assert.ok(!names.includes("kompass_session_read")); + f.config.adapters.opencode.navigator.enabled = false; + assert.deepEqual(createNavigatorV2Tools(f.ctx, f.config), []); + }); + + test("creates a default-strategy worktree, inherits context, and admits its prompt", async () => { + const f = fixture(); + const created = await Effect.runPromise(f.execute("worktree_create", { name: "task" })); + f.worktree.list = () => Effect.succeed([{ directory: created.worktree.directory, strategy: "rift" }]); + const result = await Effect.runPromise(f.execute("session_create", { + prompt: "Implement the change", title: "Implementation", directory: created.worktree.directory, + })); + assert.equal(result.status, "started"); + assert.equal(result.admitted, true); + assert.equal(result.session.directory, "/snapshots/new"); + assert.equal(result.session.agent, "worker"); + assert.deepEqual(result.session.model, model); + assert.equal(result.session.title, "Implementation"); + assert.deepEqual(f.calls.map((call) => call.name), ["worktree", "create", "progress", "prompt"]); + assert.deepEqual(f.calls[0]!.input, { location: f.sessions.get("ses_caller")!.location, name: "task", strategy: undefined, branch: undefined }); + assert.deepEqual(f.calls.at(-1)!.input, { sessionID: result.session.sessionID, text: "Implement the change", delivery: "steer" }); + assert.equal(f.calls.some((call) => call.name === "wait"), false); + }); + + test("creates directly in an existing worktree or checkout", async () => { + for (const directory of ["/repo", "/snapshots/task"]) { + const f = fixture(); + const result = await Effect.runPromise(f.execute("session_create", { prompt: "Review", directory })); + assert.equal(result.session.directory, directory); + assert.equal(f.calls.some((call) => call.name === "worktree"), false); + } + }); + + test("omitted directory uses the caller's location, including a worktree subdirectory", async () => { + const f = fixture(); + f.sessions.set("ses_caller", session("ses_caller", "/snapshots/task/src")); + const result = await Effect.runPromise(f.execute("session_create", { prompt: "Review" })); + assert.equal(result.session.directory, "/snapshots/task/src"); + assert.equal(f.calls.some((call) => call.name === "worktree"), false); + const checkout = await Effect.runPromise(f.execute("session_create", { directory: "/repo" })); + assert.equal(checkout.session.directory, "/repo"); + }); + + test("rejects the removed environment field rather than silently using the current directory", () => { + const f = fixture(); + assert.throws(() => f.execute("session_create", { environment: { type: "new_worktree" } })); + assert.deepEqual(f.calls, []); + }); + + test("rejects unmanaged directories, foreign callers, unknown agents and models before mutation", async () => { + const inputs = [ + { directory: "/arbitrary" }, + { agent: "missing" }, + { model: { providerID: "unknown", id: "unknown" } }, + { model: { ...model, variant: "unknown" } }, + ]; + for (const input of inputs) { + const f = fixture(); + await assert.rejects(Effect.runPromise(f.execute("session_create", { prompt: "Work", ...input }))); + assert.deepEqual(f.calls, []); + } + const f = fixture(); + f.sessions.set("ses_caller", session("ses_caller", "/foreign", "foreign")); + await assert.rejects(Effect.runPromise(f.execute("session_create", { prompt: "Work" })), /another OpenCode project/); + assert.deepEqual(f.calls, []); + }); + + test("passes an explicitly selected Git strategy and starting ref", async () => { + const f = fixture(); + await Effect.runPromise(f.execute("worktree_create", { strategy: "git", branch: "main" })); + assert.equal((f.calls[0]!.input as { strategy: string }).strategy, "git"); + assert.equal((f.calls[0]!.input as { branch: string }).branch, "main"); + }); + + test("preserves resources and returns recovery details when prompt admission fails", async () => { + const f = fixture(); + f.methods.prompt = () => Effect.fail(new Error("Provider unavailable")); + const result = await Effect.runPromise(f.execute("session_create", { prompt: "Work", directory: "/snapshots/task" })); + assert.equal(result.status, "partial_failure"); + assert.equal(result.phase, "prompt"); + assert.equal(result.admitted, false); + assert.equal(result.session.directory, "/snapshots/task"); + assert.ok(f.sessions.has(result.session.sessionID)); + assert.equal(result.requestedSessionID, result.session.sessionID); + }); + + test("reports session creation failure without mutating worktrees", async () => { + const f = fixture(); + f.methods.create = () => Effect.fail(new Error("Creation failed")); + const result = await Effect.runPromise(f.execute("session_create", { prompt: "Work", directory: "/snapshots/task" })); + assert.equal(result.phase, "session_create"); + assert.equal(result.session, undefined); + assert.match(result.requestedSessionID, /^ses_/); + assert.equal(result.directory, "/snapshots/task"); + assert.deepEqual(f.calls, []); + }); + + test("never prompts a session created in an unexpected location", async () => { + const f = fixture(); + f.methods.create = () => Effect.succeed(session("ses_wrong", "/unexpected")); + const result = await Effect.runPromise(f.execute("session_create", { prompt: "Work" })); + assert.equal(result.status, "partial_failure"); + assert.equal(result.session.sessionID, "ses_wrong"); + assert.equal(f.calls.some((call) => call.name === "prompt"), false); + }); + + test("supports follow-up queueing and explicit agent/model changes", async () => { + const f = fixture(); + const result = await Effect.runPromise(f.execute("session_send", { sessionID: "ses_target", prompt: "Next task", delivery: "queue", agent: "reviewer", model })); + assert.equal(result.admitted, true); + assert.deepEqual(f.calls.map((call) => call.name), ["switchAgent", "switchModel", "prompt"]); + assert.equal((f.calls.at(-1)!.input as { delivery: string }).delivery, "queue"); + }); + + test("rejects self-targeting and foreign sessions across lifecycle tools", async () => { + for (const sessionID of ["ses_caller", "ses_foreign"]) { + for (const [name, input] of [ + ["session_send", { sessionID, prompt: "Work" }], + ["session_wait", { targets: [{ sessionID }] }], + ["session_interrupt", { sessionID }], + ] as const) { + const f = fixture(); + await assert.rejects(Effect.runPromise(f.execute(name, input))); + assert.deepEqual(f.calls, []); + } + } + const f = fixture(); + await assert.rejects(Effect.runPromise(f.execute("session_read", { sessionID: "ses_foreign" })), /another OpenCode project/); + }); + + test("returns native interrupt results", async () => { + const f = fixture(); + const result = await Effect.runPromise(f.execute("session_interrupt", { sessionID: "ses_target" })); + assert.deepEqual(result, { sessionID: "ses_target", interrupted: true }); + assert.deepEqual(f.calls[0]!.input, { sessionID: "ses_target", continue: false }); + }); + + test("native wait completes and releases losing waits", async () => { + const f = fixture(); + const released: string[] = []; + const started = Deferred.makeUnsafe(); + f.methods.wait = ({ sessionID }) => sessionID === "ses_target" + ? Deferred.await(started) + : Effect.acquireUseRelease(Effect.void, + () => Effect.gen(function* () { yield* Effect.yieldNow; yield* Deferred.succeed(started, undefined); yield* Effect.never; }), + () => Effect.sync(() => { released.push(sessionID); }), + ); + const result = await Effect.runPromise(f.execute("session_wait", { targets: [{ sessionID: "ses_target" }, { sessionID: "ses_other" }] })); + assert.equal(result.status, "completed"); + assert.equal(result.completed.session.sessionID, "ses_target"); + assert.deepEqual(released, ["ses_other"]); + }); + + test("native wait timeouts are capped and release subscriptions without interrupting sessions", async () => { + const f = fixture(); + f.config.adapters.opencode.navigator.maxWaitMs = 5; + let released = false; + f.methods.wait = () => Effect.never.pipe(Effect.ensuring(Effect.sync(() => { released = true; }))); + const result = await Effect.runPromise(f.execute("session_wait", { targets: [{ sessionID: "ses_target" }], timeoutMs: 100000 })); + assert.equal(result.status, "timeout"); + assert.equal(released, true); + assert.equal(f.calls.some((call) => call.name === "interrupt"), false); + }); + + test("cancelling the tool releases native waits", async () => { + const f = fixture(); + let released = false; + const started = Deferred.makeUnsafe(); + f.methods.wait = () => Effect.gen(function* () { yield* Deferred.succeed(started, undefined); yield* Effect.never; }).pipe( + Effect.ensuring(Effect.sync(() => { released = true; })), + ); + await Effect.runPromise(Effect.gen(function* () { + const fiber = yield* Effect.forkChild(f.execute("session_wait", { targets: [{ sessionID: "ses_target" }] })); + yield* Deferred.await(started); + yield* Fiber.interrupt(fiber); + })); + assert.equal(released, true); + assert.equal(f.calls.some((call) => call.name === "interrupt"), false); + }); + + test("session reads bound current context and optional tool output", () => { + const f = fixture(); + const messages = Schema.decodeUnknownSync(Schema.Array(SessionMessage.Info))([ + { type: "user", id: "msg_old", time: { created: 1 }, text: "old" }, + { type: "assistant", id: "msg_latest", time: { created: 2 }, agent: "worker", model, content: [ + { type: "text", text: "x".repeat(100) }, + { type: "tool", id: "tool", name: "shell", time: { created: 2 }, state: { status: "completed", input: {}, content: [{ type: "text", text: "y".repeat(100) }] } }, + ] }, + ]); + f.config.adapters.opencode.navigator.maxOutputCharsPerItem = 10; + const result = summarizeV2Messages(messages, f.config.adapters.opencode.navigator, { turnLimit: 1, includeOutputs: true }); + assert.equal(result.messages.length, 1); + assert.ok(JSON.stringify(result.messages).includes("yyyyyyyyyy")); + assert.ok(!JSON.stringify(result.messages).includes("yyyyyyyyyyy")); + assert.equal(result.truncation.truncated, true); + const withoutOutputs = summarizeV2Messages(messages, f.config.adapters.opencode.navigator); + assert.ok(!JSON.stringify(withoutOutputs.messages).includes("yyyy")); + f.config.adapters.opencode.navigator.maxReadChars = 200; + assert.ok(JSON.stringify(summarizeV2Messages(messages, f.config.adapters.opencode.navigator).messages).length <= 200); + }); +}); diff --git a/packages/opencode/test/navigator.test.ts b/packages/opencode/test/navigator.test.ts index 7b5fb14..0fe3118 100644 --- a/packages/opencode/test/navigator.test.ts +++ b/packages/opencode/test/navigator.test.ts @@ -98,6 +98,37 @@ function context(sessionID = "caller", abort = new AbortController().signal) { } describe("Kompass Navigator", () => { + test("defaults to the caller directory and creates idle sessions on both protocols", async () => { + for (const protocol of ["v1", "v2"] as const) { + let prompts = 0; + const client = createClient({ + session: { + get: async ({ sessionID }: any) => response({ data: session(sessionID, "/repo-worktree/src") }), + prompt: async () => { prompts++; return response({ data: {} }); }, + }, + legacySession: { promptAsync: async () => { prompts++; return response(undefined); } }, + }); + const result = JSON.parse(await tools(client, navigatorConfig, protocol).session_create.execute({}, context()) as string); + assert.equal(result.directory, "/repo-worktree/src"); + assert.equal(result.status, "created"); + assert.equal(result.admitted, false); + assert.equal(prompts, 0); + const checkout = JSON.parse(await tools(client, navigatorConfig, protocol).session_create.execute({ directory: "/repo" }, context()) as string); + assert.equal(checkout.directory, "/repo"); + } + }); + + test("legacy idle sessions reject agent/model options that the API cannot persist", async () => { + const navigator = tools(createClient(), navigatorConfig, "v1"); + await assert.rejects(navigator.session_create.execute({ agent: "worker" }, context()), /when prompting/); + await assert.rejects(navigator.session_create.execute({ environment: { type: "new_worktree" } }, context()), /no longer supported/); + }); + test("creates standalone worktrees without creating a session", async () => { + const client = createClient({ session: { create: async () => { throw new Error("Must not create a session"); } } }); + const output = JSON.parse(await tools(client).worktree_create.execute({ name: "task" }, context()) as string); + assert.equal(output.status, "created"); + assert.equal(output.worktree.directory, "/repo-new"); + }); test("reserves Navigator tools for explicit native-session workflows", () => { for (const definition of Object.values(tools())) { assert.match(definition.description, /explicitly asks/); @@ -247,7 +278,7 @@ describe("Kompass Navigator", () => { await assert.rejects( (tools().session_create as any).execute({ prompt: "work", - environment: { type: "existing_worktree", directory: "/tmp/arbitrary" }, + directory: "/tmp/arbitrary", }, context()), /not a managed OpenCode workspace/, ); @@ -277,7 +308,7 @@ describe("Kompass Navigator", () => { }); const output = JSON.parse(await (tools(client).session_create as any).execute({ prompt: "implement it", - environment: { type: "existing_worktree", directory: "/repo-worktree" }, + directory: "/repo-worktree", }, context())); assert.equal(output.directory, "/repo-worktree"); @@ -305,7 +336,7 @@ describe("Kompass Navigator", () => { await (tools(client).session_create as any).execute({ prompt: "review it", - environment: { type: "checkout" }, + directory: "/repo", }, context()); assert.equal(creates[0].agent, "reviewer"); @@ -317,7 +348,7 @@ describe("Kompass Navigator", () => { }); }); - test("validates an unknown V2 agent in a newly created worktree and rolls it back", async () => { + test("validates an unknown agent without mutating worktrees", async () => { let worktreeCreates = 0; let worktreeRemoves = 0; let sessionCreates = 0; @@ -334,12 +365,12 @@ describe("Kompass Navigator", () => { (tools(client).session_create as any).execute({ prompt: "review it", agent: "review", - environment: { type: "new_worktree" }, + directory: "/repo-worktree", }, context()), /Unknown OpenCode agent "review".*reviewer/, ); - assert.equal(worktreeCreates, 1); - assert.equal(worktreeRemoves, 1); + assert.equal(worktreeCreates, 0); + assert.equal(worktreeRemoves, 0); assert.equal(sessionCreates, 0); }); @@ -352,12 +383,12 @@ describe("Kompass Navigator", () => { await assert.rejects((tools(client).session_create as any).execute({ prompt: "work", model: { providerID: "openai", modelID: "missing" }, - environment: { type: "checkout" }, + directory: "/repo", }, context()), /Unknown or disabled OpenCode model.*Available openai models/); await assert.rejects((tools(client).session_create as any).execute({ prompt: "work", model: { providerID: "openai", modelID: "gpt-5.6-sol", variant: "missing" }, - environment: { type: "checkout" }, + directory: "/repo", }, context()), /Unknown variant.*Available variants: xhigh/); assert.equal(sessionCreates, 0); }); @@ -462,11 +493,10 @@ describe("Kompass Navigator", () => { }); test("returns new worktree details", async () => { - const output = JSON.parse(await (tools().session_create as any).execute({ - prompt: "implement it", - environment: { type: "new_worktree", name: "feature" }, + const output = JSON.parse(await (tools().worktree_create as any).execute({ + name: "feature", }, context())); - assert.deepEqual(output.worktree, { created: true, type: "worktree", name: "new", branch: "new" }); + assert.deepEqual(output.worktree, { directory: "/repo-new", type: "worktree", name: "new", branch: "new" }); }); test("prefers Rift workspace adapter for new workspaces when available", async () => { @@ -499,16 +529,15 @@ describe("Kompass Navigator", () => { }, }; - const output = JSON.parse(await (tools(client).session_create as any).execute({ - prompt: "implement it", - environment: { type: "new_worktree", name: "Parser Fix" }, + const output = JSON.parse(await (tools(client).worktree_create as any).execute({ + name: "Parser Fix", }, context())); assert.deepEqual(workspaceCreates, [{ directory: "/repo", type: "rift", extra: { name: "Parser Fix" } }]); - assert.deepEqual(sessionCreates[0].location, { directory: "/repo-rift", workspaceID: "wrk_rift" }); - assert.equal(output.workspaceID, "wrk_rift"); + assert.deepEqual(sessionCreates, []); assert.deepEqual(output.worktree, { - created: true, + directory: "/repo-rift", + projectID: "project-1", type: "rift", name: "parser-fix", id: "wrk_rift", @@ -545,7 +574,7 @@ describe("Kompass Navigator", () => { await (tools(client).session_create as any).execute({ prompt: "continue", - environment: { type: "existing_worktree", directory: "/repo-rift" }, + directory: "/repo-rift", }, context()); assert.deepEqual(creates[0].location, { directory: "/repo-rift", workspaceID: "wrk_existing" }); @@ -566,14 +595,13 @@ describe("Kompass Navigator", () => { workspace: { adapter: { list: async () => response([{ type: "rift" }]) }, syncList: async () => response(undefined), - list: async () => response([]), - create: async () => response({ + list: async () => response([{ id: "wrk_expected", type: "rift", name: "new", directory: "/repo-rift", projectID: "project-1", - }), + }]), remove: async () => { removes += 1; return response(undefined); }, }, }; @@ -581,12 +609,12 @@ describe("Kompass Navigator", () => { await assert.rejects( (tools(client).session_create as any).execute({ prompt: "work", - environment: { type: "new_worktree" }, + directory: "/repo-rift", }, context()), - /workspace ID wrk_wrong; expected wrk_expected.*not prompted.*rolled back/, + /workspace ID wrk_wrong; expected wrk_expected.*not prompted/, ); assert.equal(prompts, 0); - assert.equal(removes, 1); + assert.equal(removes, 0); }); test("does not remove an existing Rift after returned location validation fails", async () => { @@ -611,7 +639,7 @@ describe("Kompass Navigator", () => { await assert.rejects((tools(client).session_create as any).execute({ prompt: "work", - environment: { type: "existing_worktree", directory: "/repo-rift" }, + directory: "/repo-rift", }, context()), /uses directory \/wrong.*not prompted/); assert.equal(removes, 0); }); @@ -621,13 +649,13 @@ describe("Kompass Navigator", () => { await assert.rejects( (tools(client).session_create as any).execute({ prompt: "implement it", - environment: { type: "new_worktree" }, + directory: "/repo-worktree", }, context()), - /Session created: created.*Worktree created: new \(\/repo-new\)/, + /Session created: created/, ); }); - test("reports a worktree created before a session failure", async () => { + test("session creation failure preserves existing worktrees", async () => { const removes: any[] = []; const client = createClient({ worktree: { remove: async (args: any) => { removes.push(args); return response(true); } }, @@ -636,11 +664,11 @@ describe("Kompass Navigator", () => { await assert.rejects( (tools(client).session_create as any).execute({ prompt: "implement it", - environment: { type: "new_worktree" }, + directory: "/repo-worktree", }, context()), - /Workspace \/repo-new was rolled back/, + /session creation failed.*create failed/, ); - assert.equal(removes[0].worktreeRemoveInput.directory, "/repo-new"); + assert.deepEqual(removes, []); }); test("discovers a worktree left behind by a failed native create", async () => { @@ -655,15 +683,14 @@ describe("Kompass Navigator", () => { }, }); await assert.rejects( - (tools(client).session_create as any).execute({ - prompt: "implement it", - environment: { type: "new_worktree", startCommand: "false" }, + (tools(client).worktree_create as any).execute({ + startCommand: "false", }, context()), - /partially created workspace was removed/, + /start command failed/, ); }); - test("rejects a foreign Rift workspace before creating a session and rolls it back", async () => { + test("reports a foreign Rift workspace and preserves it for recovery", async () => { const removes: any[] = []; let sessionCreates = 0; const client = createClient({ @@ -687,14 +714,11 @@ describe("Kompass Navigator", () => { }; await assert.rejects( - (tools(client).session_create as any).execute({ - prompt: "implement it", - environment: { type: "new_worktree" }, - }, context()), - /registered workspace wrk_foreign to project project-2; expected project-1.*partially created workspace was removed/, + (tools(client).worktree_create as any).execute({}, context()), + /another project.*preserved for recovery/, ); assert.equal(sessionCreates, 0); - assert.deepEqual(removes, [{ id: "wrk_foreign", directory: "/repo" }]); + assert.deepEqual(removes, []); }); test("rejects self send, wait, and interrupt", async () => { diff --git a/packages/opencode/test/plugin-entry.test.ts b/packages/opencode/test/plugin-entry.test.ts index 8682437..7ff3d76 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.effect, mod.OpenCodeCompassPluginV2.effect); + assert.equal(mod.default.id, "kompass"); + assert.equal(typeof mod.OpenCodeCompassPluginV2.effect, "function"); }); }); diff --git a/packages/opencode/test/rift-worktree.test.ts b/packages/opencode/test/rift-worktree.test.ts new file mode 100644 index 0000000..cc9313f --- /dev/null +++ b/packages/opencode/test/rift-worktree.test.ts @@ -0,0 +1,184 @@ +import { describe, test } from "node:test"; +import assert from "node:assert/strict"; +import { execFile } from "node:child_process"; +import { mkdir, mkdtemp, realpath, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { promisify } from "node:util"; +import { Worktree } from "@opencode/plugin/effect"; +import { Deferred, Effect, Fiber } from "effect"; +import { createRiftWorktreeStrategy } from "../rift-worktree.ts"; +import type { RiftBackend } from "../rift.ts"; + +const execFileAsync = promisify(execFile); + +function backend(overrides: Partial = {}): RiftBackend { + return { + init: () => null, + create: () => { throw new Error("Unexpected create"); }, + remove: () => { throw new Error("Unexpected remove"); }, + list: () => [], + ancestors: () => [], + ...overrides, + }; +} + +describe("Rift V2 worktree strategy", () => { + test("uses OpenCode's source and name while letting Rift choose the destination", async () => { + const calls: unknown[] = []; + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ + init(options) { calls.push(options); return null; }, + create(options) { calls.push(options); return "/snapshots/actual"; }, + }))); + assert.deepEqual(await Effect.runPromise(strategy.create({ sourceDirectory: "/source", directory: "/custom/task" })), { + directory: "/snapshots/actual", + }); + assert.deepEqual(calls, [ + { at: "/source" }, + { from: "/source", name: "task", copyAll: true }, + ]); + }); + + test("suffixes name collisions in Rift's own storage", async () => { + const names: Array = []; + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ + create(options) { + names.push(options?.name); + if (names.length === 1) throw Object.assign(new Error("Snapshot exists"), { code: "already_exists" }); + return `/projects/.rifts/source/${options?.name}`; + }, + }))); + assert.deepEqual(await Effect.runPromise(strategy.create({ sourceDirectory: "/source", directory: "/suggested/task" })), { + directory: "/projects/.rifts/source/task-2", + }); + assert.deepEqual(names, ["task", "task-2"]); + }); + + test("suffixes a snapshot name that would target the source snapshot itself", async () => { + const sourceDirectory = "/projects/.rifts/source/task"; + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ + create(options) { + if (options?.name === "task") { + throw Object.assign(new Error("Cannot copy into itself"), { code: "inside_source", path: sourceDirectory }); + } + return `/projects/.rifts/source/${options?.name}`; + }, + }))); + assert.deepEqual(await Effect.runPromise(strategy.create({ sourceDirectory, directory: "/suggested/task" })), { + directory: "/projects/.rifts/source/task-2", + }); + }); + + test("rejects starting refs before loading Rift", async () => { + const strategy = createRiftWorktreeStrategy(Effect.die("Must not load")); + await assert.rejects(Effect.runPromise(strategy.create({ sourceDirectory: "/source", directory: "/task", branch: "main" })), + (error) => error instanceof Worktree.OperationError && /starting branch or ref/.test(error.message)); + }); + + test("interruption releases backend acquisition without performing native operations", async () => { + for (const operation of ["create", "list", "remove"] as const) { + const started = Deferred.makeUnsafe(); + let released = false; + const strategy = createRiftWorktreeStrategy(Effect.gen(function* () { + yield* Deferred.succeed(started, undefined); + return yield* Effect.never; + }).pipe(Effect.ensuring(Effect.sync(() => { released = true; })))); + const effect = operation === "create" ? strategy.create({ sourceDirectory: "/source", directory: "/task" }) + : operation === "list" ? strategy.list("/source") : strategy.remove({ directory: "/task", force: true }); + await Effect.runPromise(Effect.gen(function* () { + const fiber = yield* Effect.forkChild(effect); + yield* Deferred.await(started); + yield* Fiber.interrupt(fiber); + })); + assert.equal(released, true); + } + }); + + test("propagates creation errors without retrying", async () => { + let attempts = 0; + const failure = new Error("copy-on-write unavailable"); + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ create() { attempts++; throw failure; } }))); + await assert.rejects(Effect.runPromise(strategy.create({ sourceDirectory: "/source", directory: "/task" })), (error) => error === failure); + assert.equal(attempts, 1); + }); + + test("uninitialized roots have no inventory; registry errors are surfaced", async () => { + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ + ancestors() { throw Object.assign(new Error("Not initialized"), { code: "workspace_not_initialized" }); }, + }))); + assert.deepEqual(await Effect.runPromise(strategy.list("/source")), []); + const failure = new Error("registry damaged"); + const broken = createRiftWorktreeStrategy(Effect.succeed(backend({ ancestors() { throw failure; } }))); + await assert.rejects(Effect.runPromise(broken.list("/source")), (error) => error === failure); + }); + + test("discovers siblings and descendants from a snapshot, skipping missing entries", async (t) => { + const root = await realpath(await mkdtemp(path.join(os.tmpdir(), "kompass-rift-list-"))); + t.after(() => rm(root, { recursive: true, force: true })); + const source = path.join(root, "source"); + const first = path.join(root, "first"); + const second = path.join(root, "second"); + const nested = path.join(root, "nested"); + await Promise.all([source, first, second, nested].map((directory) => mkdir(directory))); + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ + ancestors: () => [source], + list: ({ of } = {}) => of === source ? [first, second, first, path.join(root, "missing")] : of === first ? [nested] : [], + }))); + assert.deepEqual(await Effect.runPromise(strategy.list(first)), [ + { directory: source, type: "root" }, + { directory: first, type: "worktree" }, + { directory: second, type: "worktree" }, + { directory: nested, type: "worktree" }, + ]); + }); + + test("removes only a snapshot root and never cascades to children, even with force", async (t) => { + const root = await realpath(await mkdtemp(path.join(os.tmpdir(), "kompass-rift-remove-"))); + t.after(() => rm(root, { recursive: true, force: true })); + const child = path.join(root, "child"); + await mkdir(child); + const removed: string[] = []; + let children: string[] = []; + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ + ancestors: ({ of } = {}) => of === root ? [] : [root], + list: ({ of } = {}) => of === root ? [child] : children, + remove: (options) => { removed.push(options!.at!); }, + }))); + await assert.rejects(Effect.runPromise(strategy.remove({ directory: root, force: true })), /Not a Rift snapshot root/); + const subdirectory = path.join(child, "subdirectory"); + await mkdir(subdirectory); + await assert.rejects(Effect.runPromise(strategy.remove({ directory: subdirectory, force: true })), /Not a Rift snapshot root/); + children = ["descendant"]; + await assert.rejects(Effect.runPromise(strategy.remove({ directory: child, force: true })), /child snapshots first/); + assert.deepEqual(removed, []); + children = []; + await Effect.runPromise(strategy.remove({ directory: child, force: true })); + assert.deepEqual(removed, [child]); + }); + + test("requests force for uncommitted files and allows a clean snapshot's Rift marker", async (t) => { + const root = await realpath(await mkdtemp(path.join(os.tmpdir(), "kompass-rift-dirty-"))); + t.after(() => rm(root, { recursive: true, force: true })); + const snapshot = path.join(root, "snapshot"); + await mkdir(snapshot); + await execFileAsync("git", ["init", snapshot]); + // Rift itself excludes its marker in the snapshot's Git metadata. + await writeFile(path.join(snapshot, ".git", "info", "exclude"), ".rift\n"); + await writeFile(path.join(snapshot, ".rift"), "rift-marker"); + await writeFile(path.join(snapshot, "user.txt"), "uncommitted"); + let removed = 0; + const strategy = createRiftWorktreeStrategy(Effect.succeed(backend({ + ancestors: () => [root], + list: ({ of } = {}) => of === root ? [snapshot] : [], + remove: () => { removed++; }, + }))); + await assert.rejects(Effect.runPromise(strategy.remove({ directory: snapshot, force: false })), + (error) => error instanceof Worktree.OperationError && error.forceRequired === true); + assert.equal(removed, 0); + await Effect.runPromise(strategy.remove({ directory: snapshot, force: true })); + assert.equal(removed, 1); + await rm(path.join(snapshot, "user.txt")); + await Effect.runPromise(strategy.remove({ directory: snapshot, force: false })); + assert.equal(removed, 2); + }); +}); diff --git a/packages/opencode/test/tool-registration.test.ts b/packages/opencode/test/tool-registration.test.ts index d9f8562..7b7aa8f 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); @@ -129,7 +129,7 @@ async function createTempGitRepo() { describe("createOpenCodeTools", () => { test("Rift workspace adapter maps OpenCode workspaces to Rift snapshots", async () => { - const calls: Array<{ name: string; options?: Record }> = []; + const calls: Array<{ name: string; options?: unknown }> = []; const sourceDirectory = path.join(os.tmpdir(), "kompass-rift-source"); const adapter = createRiftWorkspaceAdapter({ init: (options) => { @@ -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..830b659 --- /dev/null +++ b/packages/opencode/test/v2-plugin.test.ts @@ -0,0 +1,122 @@ +import { describe, test } from "node:test"; +import assert from "node:assert/strict"; +import { Agent, Location, type Plugin } from "@opencode/plugin/effect"; +import type { CommandDefinition } from "@opencode/plugin/effect/command"; +import type { Transform } from "@opencode/plugin/effect/registration"; +import type { WorktreeDefinition } from "@opencode/plugin/effect/worktree"; +import { Session } from "@opencode/schema/session"; +import { Tool } from "@opencode/schema/tool"; +import { Effect, Schema } from "effect"; +import { z } from "zod"; +import { OpenCodeCompassPluginV2, setupOpenCodeV2 } from "../index.ts"; +import { loadResolvedCommands } from "../cache.ts"; +import { createOpenCodeV2Tools } from "../v2.ts"; +import { mergeWithDefaults } from "../../core/index.ts"; + +function fixture() { + const agents = new Map(); + const commands = new Map(); + const tools = new Map(); + const worktrees: WorktreeDefinition[] = []; + const prompts: Array> = []; + const createdSessions: Array> = []; + const switchedAgents: string[] = []; + const disposed: string[] = []; + function transform(name: string, editor: Editor): Transform { + return (callback) => Effect.gen(function* () { + callback(editor); + let active = true; + const dispose = Effect.sync(() => { if (active) disposed.push(name); active = false; }); + yield* Effect.addFinalizer(() => dispose); + return { dispose }; + }); + } + const ctx = { + location: Schema.decodeUnknownSync(Location.Info)({ directory: process.cwd(), project: { id: "project", directory: process.cwd(), canonical: process.cwd() } }), + agent: { transform: transform("agent", { + update: (id: string, update: (agent: Agent.Info) => void) => { + const agent = agents.get(id) ?? Agent.Info.default(Agent.ID.make(id)); + update(agent); + agents.set(id, agent); + }, + }) }, + command: { transform: transform("command", { add: (command: CommandDefinition) => commands.set(command.name, command) }) }, + tool: { transform: transform("tool", { add: (tool: Tool.Info) => tools.set(tool.name, tool) }) }, + worktree: { transform: transform("worktree", { add: (strategy: WorktreeDefinition) => worktrees.push(strategy) }) }, + session: { + create: (input: Record) => Effect.sync(() => { + createdSessions.push(input); + return { id: Session.ID.make("ses_child") }; + }), + switchAgent: ({ agent }: { agent: string }) => Effect.sync(() => { switchedAgents.push(agent); }), + prompt: (input: Record) => Effect.sync(() => { prompts.push(input); }), + }, + }; + return { ctx, context: ctx as unknown as Plugin.Context, agents, commands, tools, worktrees, prompts, createdSessions, switchedAgents, disposed }; +} + +describe("OpenCode v2 plugin", () => { + test("exports the native Effect setup directly", () => { + assert.equal(OpenCodeCompassPluginV2.id, "kompass"); + assert.equal(OpenCodeCompassPluginV2.effect, setupOpenCodeV2); + }); + + test("registers agents, commands, all tools, and Rift on the host scope", async () => { + const f = fixture(); + await Effect.runPromise(Effect.scoped(Effect.gen(function* () { + yield* setupOpenCodeV2(f.context); + assert.ok(f.agents.has("worker")); + assert.ok(f.commands.has("dev")); + assert.deepEqual(f.worktrees.map((strategy) => strategy.id), ["rift"]); + assert.equal(Effect.isEffect(f.worktrees[0]!.list(process.cwd())), true); + assert.deepEqual([...f.tools.keys()].sort(), [ + "kompass_changes_load", "kompass_pr_load", "kompass_pr_load_review", "kompass_pr_sync", + "kompass_session_create", "kompass_session_interrupt", "kompass_session_read", "kompass_session_send", "kompass_session_wait", + "kompass_ticket_load", "kompass_ticket_sync", "kompass_worktree_create", "kompass_worktree_list", "kompass_worktree_remove", + ]); + assert.equal(typeof (f.tools.get("kompass_changes_load")!.input as z.ZodType)["~standard"].validate, "function"); + assert.deepEqual(f.disposed, []); + }))); + assert.deepEqual(f.disposed.sort(), ["agent", "command", "tool", "worktree"]); + }); + + test("native commands always run inline and preserve arguments, attachments, and delivery", async () => { + const f = fixture(); + const resolved = await loadResolvedCommands(process.cwd()); + const text = "Keep literal $& $$ $1 $ARGUMENTS and !`printf should-not-run`"; + const files = [{ uri: "file:///project/example.ts" }]; + const sessionID = Session.ID.make("ses_current"); + await Effect.runPromise(Effect.scoped(Effect.gen(function* () { + yield* setupOpenCodeV2(f.context); + assert.equal(f.commands.size, Object.keys(resolved).length); + yield* f.commands.get("dev")!.execute({ sessionID, prompt: { text, files }, delivery: "queue" }); + assert.equal(f.prompts[0]?.text, resolved.dev!.template.replaceAll("$ARGUMENTS", () => text)); + assert.equal(f.prompts[0]?.files, files); + assert.equal(f.prompts[0]?.delivery, "queue"); + assert.equal(f.prompts[0]?.sessionID, sessionID); + assert.equal(f.createdSessions.length, 0); + assert.equal(f.switchedAgents.at(-1), "worker"); + yield* f.commands.get("learn")!.execute({ sessionID, prompt: { text: "remember this" }, delivery: "steer" }); + assert.equal(f.switchedAgents.at(-1), "worker"); + assert.equal(f.prompts[1]?.sessionID, sessionID); + assert.equal(f.prompts[1]?.text, resolved.learn!.template.replaceAll("$ARGUMENTS", "remember this")); + }))); + }); + + test("failed setup releases prior native registrations", async () => { + const f = fixture(); + f.ctx.tool.transform = () => Effect.die(new Error("Registration failed")); + await assert.rejects(Effect.runPromise(Effect.scoped(setupOpenCodeV2(f.context))), /Registration failed/); + assert.deepEqual(f.disposed.sort(), ["agent", "command"]); + assert.deepEqual(f.worktrees, []); + }); + + test("shared core failures enter the native Tool.Error channel", async () => { + const tools = createOpenCodeV2Tools(process.cwd(), mergeWithDefaults({})); + const tool = tools.find((entry) => entry.name === "kompass_ticket_load")!; + const invocation = { sessionID: Session.ID.make("ses_current") } as Tool.Context; + const error = await Effect.runPromise(Effect.flip(tool.execute({ source: process.cwd() }, invocation))); + assert.ok(error instanceof Tool.Error); + assert.ok(error.message.length > 0); + }); +}); 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..acee239 --- /dev/null +++ b/packages/opencode/v2.ts @@ -0,0 +1,112 @@ +import { Agent, Plugin } from "@opencode/plugin/effect"; +import { Tool } from "@opencode/schema/tool"; +import { Effect } from "effect"; +import { z } from "zod"; + +import { + getEnabledToolNames, + type MergedKompassConfig, + 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"; +import { createRiftWorktreeStrategy } from "./rift-worktree.ts"; +import { createNavigatorV2Tools } from "./navigator-v2.ts"; + +export function createOpenCodeV2Tools(projectRoot: string, config: MergedKompassConfig): Tool.Info[] { + const definitions = createCoreOpenCodeToolDefinitions(config, projectRoot); + return getEnabledToolNames(config.tools).flatMap((toolName): Tool.Info[] => { + const definition = definitions[toolName]; + if (!definition) return []; + return [{ + name: getConfiguredOpenCodeToolName(toolName, config.tools[toolName].name), + description: definition.description, + input: z.object(definition.args), + options: { codemode: false }, + execute: (args) => Effect.tryPromise({ + // The shared core uses Promises. Give each invocation its own shell and + // Effect's cancellation signal at that boundary. + try: async (signal) => { + const current = createCoreOpenCodeToolDefinitions(config, projectRoot, signal)[toolName]!; + const result = await current.execute(args, { worktree: projectRoot, directory: projectRoot }); + const content = typeof result === "string" ? result : result.output; + return { output: JSON.parse(content), content }; + }, + catch: (error) => new Tool.Error({ message: error instanceof Error ? error.message : String(error) }), + }), + }]; + }); +} + +function applyAgents( + draft: Parameters[0]>[0], + agents: Record, +) { + for (const [name, definition] of Object.entries(agents)) { + 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; + }); + } +} + +function applyCommands( + draft: Parameters[0]>[0], + commands: Record, + ctx: Plugin.Context, +) { + for (const [name, definition] of Object.entries(commands)) { + draft.add({ + name, + description: definition.description, + execute: Effect.fn(`kompass.command.${name}`)(function* ({ sessionID, prompt, delivery }) { + // V2 commands always execute in the invoking session, regardless of + // the shared definition's V1 subtask setting. + if (definition.agent) { + yield* ctx.session.switchAgent({ sessionID, agent: Agent.ID.make(definition.agent) }); + } + yield* ctx.session.prompt({ + ...prompt, + sessionID, + text: definition.template.includes("$ARGUMENTS") + ? definition.template.replaceAll("$ARGUMENTS", () => prompt.text) + : `${definition.template}\n\n${prompt.text}`.trim(), + delivery, + }); + }), + }); + } +} + +export const setupOpenCodeV2 = Effect.fn("kompass.setupV2")(function* (ctx: Plugin.Context) { + const projectRoot = ctx.location.directory; + const [config, agents, commands] = yield* Effect.all([ + Effect.promise(() => loadMergedKompassConfig(projectRoot)), + Effect.promise(() => loadResolvedAgents(projectRoot)), + Effect.promise(() => loadResolvedCommands(projectRoot)), + ], { concurrency: "unbounded" }); + const tools = [...createOpenCodeV2Tools(projectRoot, config), ...createNavigatorV2Tools(ctx, config)]; + + // Native transforms attach their registrations to the plugin scope, including + // when setup fails partway through. The host owns their disposal. + yield* ctx.agent.transform((draft) => applyAgents(draft, agents)); + yield* ctx.command.transform((draft) => applyCommands(draft, commands, ctx)); + yield* ctx.tool.transform((draft) => { + for (const tool of tools) draft.add(tool); + }); + yield* ctx.worktree.transform((draft) => { + draft.add(createRiftWorktreeStrategy()); + }); + createPluginLogger(projectRoot).info("Initialized Kompass v2 plugin", { directory: projectRoot, tools: tools.length }); +}); + +export const OpenCodeCompassPluginV2 = Plugin.define({ id: "kompass", effect: setupOpenCodeV2 }); diff --git a/packages/web/src/components/CommandShowcase.astro b/packages/web/src/components/CommandShowcase.astro index 08ce90c..f9587c2 100644 --- a/packages/web/src/components/CommandShowcase.astro +++ b/packages/web/src/components/CommandShowcase.astro @@ -395,7 +395,7 @@ const scenarios: CommandScenario[] = [ id: 'dev', label: '/dev', command: '/dev update the command docs', - agentName: 'Navigator', + agentName: 'Worker', task: 'Implement a request and prepare a PR handoff', group: 'orchestration', steps: [ diff --git a/packages/web/src/content/docs/docs/adapters/opencode.mdx b/packages/web/src/content/docs/docs/adapters/opencode.mdx index 74597aa..ca80f3e 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 1 handles command expansion natively. On OpenCode 2, the plugin inserts the raw input at `$ARGUMENTS`, or appends it when that placeholder is absent. Numbered placeholders and shell interpolation in custom command templates are not supported by the v2 adapter. OpenCode 2 agent transforms create missing agent IDs, including renamed Kompass agents. + ## What it provides Inside OpenCode, the adapter exposes: @@ -14,7 +18,8 @@ Inside OpenCode, the adapter exposes: - Kompass commands - Kompass agents - Kompass tools -- Kompass Navigator session and worktree orchestration +- Kompass Navigator session orchestration on both OpenCode versions, with eight native V2 tools +- a native Rift worktree strategy for OpenCode 2 - project override loading from local config files ## Project overrides @@ -31,9 +36,53 @@ You can use overrides to: - adjust adapter behavior such as `agentMode` - enable and limit Navigator -## Kompass Navigator +## OpenCode 2 worktrees + +Kompass registers `rift` through V2's native `ctx.worktree.transform` API. Registration selects it as the location's default for calls that omit a strategy. Desktop's current New worktree button explicitly selects Git; use Kompass's `worktree_create` tool for the registered default, then `session_create` to start work in the returned directory. The adapter targets `@opencode/plugin` beta `19381`, including support for a strategy's returned directory. + +Load Kompass in OpenCode's `opencode.jsonc`; no destination setting is needed: + +```jsonc +{ + "plugins": ["@kompassdev/opencode"] +} +``` + +- Snapshots preserve dependencies and uncommitted files through Rift's full copy-on-write path. Rift's creation hooks run, followed by OpenCode's project setup. +- Rift controls storage using its default `.rifts//` layout beside the source root. The strategy uses OpenCode's suggested name and suffixes collisions in Rift's storage, but does not use OpenCode's destination override or `worktree.directory` setting. OpenCode records the actual directory Rift returns. Inventory includes sibling and nested snapshots from Rift's registry. +- Starting branch/ref overrides are rejected because Rift snapshots the source checkout. Use `strategy: "git"` explicitly when creating from a ref. +- Removal requests force confirmation for uncommitted files. Child snapshots must be removed first, even with force, because Rift removal otherwise deletes an entire subtree. Removed snapshots go to Rift's trash; Rift's `gc` reclaims the storage. +- The last active strategy registration wins. Unloading Kompass restores the previous default. Existing snapshots retain their Rift owner and require the plugin for removal. Errors never trigger a Git fallback. + +The bundled `rift-snapshot` native bindings load on first use and require a supported copy-on-write filesystem. Native calls are synchronous and cannot be interrupted mid-operation; the adapter checks cancellation between calls. The V2 strategy is independent of the V1 Navigator settings. + +## OpenCode 2 session orchestration + +V2 registers eight Navigator tools: `worktree_create`, `worktree_list`, `worktree_remove`, `session_create`, `session_read`, `session_send`, `session_wait`, and `session_interrupt`. Default names have the `kompass_` prefix. The V2 plugin uses native Effect setup, commands, tools, and Rift operations. Registrations belong to the host's plugin scope. Shared Promise-based core tools run at an explicit Effect boundary that passes cancellation to their shell commands. + +Ask the agent to create a worktree and start work there. It calls `kompass_worktree_create` with `{ "name": "parser-fix" }`, then passes the returned directory to `kompass_session_create`: + +```json +{ + "prompt": "Implement the parser fix and run tests.", + "title": "Parser fix", + "directory": "/projects/.rifts/repo/parser-fix" +} +``` + +Session creation uses the supplied directory or defaults to the calling session's current location. It inherits the caller's agent/model/variant unless overridden and admits the optional prompt. It returns the session ID and directory immediately. `session_send` uses the target session's stored location and accepts `delivery: "steer"` or `"queue"`. Reads return bounded current model context. Waits use OpenCode's native wait, race up to eight targets, and release subscriptions on timeout or cancellation without interrupting the target sessions. + +Worktree creation is always a separate call to `worktree_create({ name?, strategy?, branch? })`. V2 `session_create` has no environment options and never creates or removes a worktree. Omit `prompt` to create an idle session. `worktree_remove({ directory, force? })` uses native removal and does not delete sessions. It protects the checkout and calling location. V2 cannot discover other active sessions, so finish known work before removal. + +Navigator follows the existing feature toggle and tool aliases/disables. `maxReadChars`, `maxOutputCharsPerItem`, and `maxWaitMs` apply. V2 `timeoutMs` is positive and capped by `maxWaitMs`. Session listing and `maxConcurrentSessions` remain V1-only because V2 plugins lack session discovery. Known session IDs still work after reloads. Partial failures preserve resources and return recovery details. + +See the [Navigator tool reference](/docs/reference/tools/navigator/) for V2 arguments and examples. + +## OpenCode 1 Navigator + +V1 session creation also accepts optional `directory` and `prompt`. Worktree creation is a separate call; the `environment` argument has been removed. With no directory, creation uses the calling session's current location. With no prompt, it creates an idle session. The legacy session protocol cannot persist agent/model selection before prompting; provide those overrides when sending the first prompt instead. -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. +The OpenCode 1 adapter exposes nine Navigator tools, including standalone worktree creation. Ordinary delegation uses OpenCode's built-in `task` tool. Navigator follows Desktop's protocol detection so session operations stay on one compatible API. The V1 adapter polls the active-session API locally when using the V2 session protocol. It requires OpenCode `1.17.12` or newer and is enabled by default. ```jsonc { @@ -55,11 +104,11 @@ Navigator is for explicitly requested native-session and managed-worktree workfl } ``` -Navigator registers all eight logical tools unless the feature or an individual tool is disabled. Default names receive the `kompass_` prefix; configured aliases such as `start_workflow_session` are registered exactly. +The V1 adapter registers all nine logical tools unless the feature or an individual tool is disabled. Default names receive the `kompass_` prefix; configured aliases such as `start_workflow_session` are registered exactly. Navigator is scoped to the current project. New sessions inherit the calling session's agent, model, and variant unless explicitly overridden. Session IDs are ownership-checked, directories must be native managed worktrees, and lifecycle calls cannot target the calling session. `session_send` can switch the target session's agent or model before admitting a steered prompt. Waits default to `maxWaitMs`, cap requested timeouts at `maxWaitMs`, and use `timeoutMs: 0` for an immediate snapshot. Removal rejects the checkout, unmanaged worktrees, and worktrees containing active sessions. Navigator does not force removal, merge branches, create pull requests, or maintain a separate job database. -When OpenCode exposes experimental workspace adapters, Kompass registers a `rift` workspace adapter for OpenCode's workspace API using its bundled `rift-snapshot` dependency. Navigator automatically uses Rift for `new_worktree` sessions when no `startCommand` is requested, falling back to Git worktrees otherwise. +On OpenCode 1, when experimental workspace adapters are available, Kompass registers a `rift` workspace adapter using its bundled `rift-snapshot` dependency. `worktree_create` uses Rift when no `startCommand` is requested, using Git worktrees otherwise. Pass the returned directory to `session_create`. ## Agent model @@ -67,7 +116,7 @@ Most command execution runs on the bundled `worker` agent. Kompass also provides the `worker`, `planner`, and `reviewer` agent roles for workflow execution, planning, and review-specific execution. -Commands run as subtasks outside CI unless their definition opts out. The `/inline` variants of `/branch`, `/commit`, `/commit-and-push`, `/pr/create`, and `/ship`, plus `/learn`, opt out so they can use the invoking session directly. +On V2, all commands run inline in the invoking session, regardless of the shared `subtask` setting. The adapter switches to the command's agent and submits its expanded template in that session. On V1, commands run as subtasks outside CI unless their definition opts out. The `/inline` variants of `/branch`, `/commit`, `/commit-and-push`, `/pr/create`, and `/ship`, plus `/learn`, opt out on V1. ## Useful OpenCode notes diff --git a/packages/web/src/content/docs/docs/config/schema.mdx b/packages/web/src/content/docs/docs/config/schema.mdx index 69e2bc6..59425fe 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 @@ -54,7 +54,9 @@ Deprecated array fields still exist in the schema for compatibility, but the new `adapters.opencode.navigator` supports: - `enabled` -- `maxConcurrentSessions` from 1 through 8 +- `maxConcurrentSessions` from 1 through 8, enforced by the OpenCode 1 adapter only - positive `maxReadChars` - positive `maxOutputCharsPerItem`, no greater than `maxReadChars` - positive `maxWaitMs` + +OpenCode 2 registers eight native Navigator tools. `session_list` and the concurrency limit require session discovery APIs that V2 plugins do not expose. V2 worktree removal uses native force semantics without an active-session check. All other Navigator settings and supported tool aliases/disables apply to V2. 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..69abdf2 100644 --- a/packages/web/src/content/docs/docs/reference/tools/index.mdx +++ b/packages/web/src/content/docs/docs/reference/tools/index.mdx @@ -14,6 +14,7 @@ Current built-in tools: - `ticket_load` - `ticket_sync` - `worktree_list` (OpenCode Navigator) +- `worktree_create` (OpenCode Navigator) - `session_create` (OpenCode Navigator) - `session_list` (OpenCode Navigator) - `session_read` (OpenCode Navigator) @@ -50,7 +51,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 is enabled by default on both OpenCode versions. V1 exposes all nine tools below. V2 exposes eight native tools, omitting `session_list` because the plugin API lacks session discovery. Use Navigator for explicitly requested native-session workflows; ordinary delegation uses OpenCode's built-in subagent mechanism. Default runtime names use the `kompass_` prefix. See [Kompass Navigator](./navigator/) for version-specific arguments and limits. ### `worktree_list` @@ -58,23 +59,25 @@ Lists the current checkout explicitly and all native OpenCode-managed worktrees. ### `session_create` -Creates a native session in the checkout, an existing managed workspace, or a newly created workspace; inherits the calling session's agent, model, and variant unless overridden, admits its initial prompt, and returns immediately. New workspaces prefer the registered Rift adapter when available and fall back to Git worktrees. +On V2, the initial `prompt` is optional. Omit it to create an idle session, then start execution with `session_send`. + +Both adapters create a native session in an optional `directory`, defaulting to the calling session's current location. An optional prompt starts execution immediately. Create new worktrees separately with `worktree_create`. The legacy session protocol selects agent/model only when prompting; pass overrides to `session_send` when starting an idle session. ### `session_list` -Lists paginated sessions filtered to the current OpenCode project, with location, agent, model, timestamps, and active or idle state. +V1 only. Lists paginated sessions filtered to the current OpenCode project, with location, agent, model, timestamps, and active or idle state. ### `session_read` -Reads a bounded page of recent projected messages. Tool outputs are excluded by default, and per-item and total truncation metadata is returned. +Reads bounded recent messages with optional tool output. V2 reads current model context; V1 supports paginated history. Configured per-item and total message limits apply. ### `session_send` -Sends a steered follow-up through the same session protocol selected by OpenCode Desktop, optionally switching the target agent or model first. +Admits a follow-up, optionally switching agent/model first. V2 uses native `steer` or `queue` delivery; V1 sends through the protocol selected by Desktop. ### `session_wait` -Polls the active-session API for the detected protocol until the first of up to eight sessions becomes idle, then returns bounded recent output. The timeout defaults to configured `maxWaitMs`, is capped by `maxWaitMs`, and `0` returns an immediate snapshot. +Waits for the first of up to eight sessions to become idle and returns bounded context. V2 uses native Effect waits and releases subscriptions on timeout or cancellation; V1 polls active state. Timeouts default to and are capped by `maxWaitMs`. V2 requires a positive timeout; V1 also supports `0` for an immediate snapshot. ### `session_interrupt` @@ -82,4 +85,8 @@ Interrupts active native execution for another session in the current project. ### `worktree_remove` -Removes an idle native managed worktree. It rejects the main checkout, unmanaged paths, and worktrees containing active sessions; force removal is not available. +Removes a native managed worktree. V2 uses native removal with optional `force`, protects the checkout and calling location, and cannot discover other active sessions. V1 checks for active sessions and has no force option. + +### `worktree_create` + +Creates a managed worktree without a session. V2 accepts `name`, `strategy`, and `branch`. V1 accepts `name` and `startCommand`. On either version, pass the returned directory to `session_create`. 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..5cec92c 100644 --- a/packages/web/src/content/docs/docs/reference/tools/navigator.mdx +++ b/packages/web/src/content/docs/docs/reference/tools/navigator.mdx @@ -3,28 +3,72 @@ 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. - -| Logical tool | Purpose | -| --- | --- | -| `worktree_list` | List the checkout and native managed worktrees. | -| `session_create` | Create a native session in the checkout, an existing managed workspace, or a newly created workspace, inheriting the caller's agent, model, and variant unless overridden, and admit its first prompt asynchronously. New workspaces prefer Rift when available. | -| `session_list` | List paginated sessions filtered to the current project. | -| `session_read` | Read a bounded page of recent messages with optional tool outputs. | -| `session_send` | Send a steered follow-up through the detected session API, optionally switching agent or model first. | -| `session_wait` | Poll the detected active-session API until the first of one to eight sessions becomes idle. The timeout defaults to and is capped by `maxWaitMs`; zero returns an immediate snapshot. | -| `session_interrupt` | Interrupt another session's active execution. | -| `worktree_remove` | Remove an idle native managed worktree without force. | +Navigator is enabled by default on both OpenCode versions. V1 exposes nine tools; V2 exposes eight tools through native session and worktree APIs, using the [Rift strategy](/docs/adapters/opencode/#opencode-2-worktrees) for default worktree creation. Use Navigator for explicitly requested native-session and multi-session workflows. Ordinary delegation uses OpenCode's built-in subagent mechanism. Default runtime names use the `kompass_` prefix; `adapters.opencode.navigator.enabled: false` disables Navigator. Individual logical names can be disabled or aliased through `tools`. + +| Logical tool | Versions | Purpose | +| --- | --- | --- | +| `worktree_list` | V1, V2 | List the checkout and native managed worktrees. | +| `worktree_create` | V1, V2 | Create a managed worktree without a session. | +| `session_create` | V1, V2 | Create a native session, inherit agent/model/variant unless overridden, admit its first prompt, and return immediately. | +| `session_list` | V1 | List paginated current-project sessions. | +| `session_read` | V1, V2 | Read bounded messages. V2 reads current model context; V1 supports paginated history. | +| `session_send` | V1, V2 | Send a follow-up, optionally switching agent/model. V2 supports steer or queue delivery. | +| `session_wait` | V1, V2 | Wait for the first of up to eight targets to become idle. V2 uses native waits with cancellation; V1 polls active-session state. | +| `session_interrupt` | V1, V2 | Interrupt another session's active execution. | +| `worktree_remove` | V1, V2 | Remove a managed worktree. V2 supports native force semantics; V1 checks active sessions and has no force option. | + +## Independent worktree lifecycle + +On V2, `worktree_create({ name?, strategy?, branch? })` returns `{ status: "created", worktree: { directory } }`. It uses the calling session's location as source. Omit `strategy` for the registered default, normally Rift. A starting `branch` requires `strategy: "git"`. Pass the returned directory to `session_create` in a separate call. + +Use `worktree_remove({ directory, force? })` to remove a managed directory. `force` defaults to false and permits discarding uncommitted files when true. Rift rejects snapshots with children even with force. The checkout and worktrees containing the calling session or plugin cannot be removed. + +V2 cannot discover all active sessions. Finish or interrupt known sessions using the directory before removal. The tool does not delete or interrupt sessions. V1 retains its active-session checks and has no force option. V1 standalone creation accepts `{ name?, startCommand? }`. + +## V2 create and follow up + +First call `kompass_worktree_create` with `{ "name": "parser-fix" }`. Then invoke `kompass_session_create` with the returned directory: + +```json +{ + "prompt": "Implement the parser fix and run tests.", + "title": "Parser fix", + "directory": "/projects/.rifts/repo/parser-fix" +} +``` + +V2 `session_create` accepts `directory?`, `prompt?`, `title?`, `agent?`, and `model?`. Without a directory, it uses the calling session's current location, even inside a worktree or subdirectory. An explicit directory can select the project checkout or a managed worktree. Relative paths resolve from the caller's directory. The removed `environment` field is rejected. V2 uses OpenCode's project setup rather than V1's `startCommand` argument. + +Session creation never creates or removes worktrees. Both adapters accept optional `directory` and `prompt` and have removed the `environment` argument. V1's legacy session protocol cannot persist agent/model selection for an idle session; provide those overrides to `session_send` with its first prompt. V1 returns `sessionID` and `directory` at the top level; V2 returns them inside `session`. + +Optional `agent` is a registered agent ID. V2's optional `model` uses `{ "providerID": "...", "id": "...", "variant": "..." }`; V1 uses `modelID` instead of `id`. + +With a prompt, `status` is `started`, `admitted` is `true`, and `session.sessionID` and `session.directory` identify the native session. Omit `prompt` on V2 to create an idle session with `status: "created"` and `admitted: false`. Start it later with `session_send`. Creation does not wait for an answer. + +Use the returned ID with `kompass_session_send`: + +```json +{ "sessionID": "ses_...", "prompt": "Also add regression coverage.", "delivery": "queue" } +``` + +`delivery` defaults to `steer`. Send targets a session ID and uses that session's stored location; it does not take a directory or move the session. Agent/model overrides are validated before switching. A later failure may leave a successfully applied agent/model change in place. + +Read with `session_read({ sessionID, turnLimit?, includeOutputs?, maxOutputCharsPerItem? })`. V2 defaults to the latest 20 current-context messages, excludes tool outputs, and caps requested output limits at configured limits. It does not expose a history cursor. + +Wait with `session_wait({ targets: [{ sessionID }], timeoutMs? })`. V2 requires a positive timeout, defaulting to and capped by `maxWaitMs`. `completed` means a target became idle; inspect `completed.session.outcome` for success, failure, or interruption. A timeout returns the target IDs without claiming they are all still active. Timeout, cancellation, and a winning target release the other native wait subscriptions without stopping their sessions. V1 also supports `timeoutMs: 0` for an immediate active-state snapshot. + +## V2 API limits + +The plugin context has no session list or active-session discovery API. V2 therefore omits `session_list`, cannot check other active sessions during removal, and does not enforce `maxConcurrentSessions`. Use known session IDs from tool results or Desktop. The feature toggle, aliases, `maxReadChars`, `maxOutputCharsPerItem`, and `maxWaitMs` apply on both versions. ## Safety and limits - Every session ID is validated against the current OpenCode project. - Existing-worktree paths must appear in OpenCode's native worktree list. - Send, wait, and interrupt reject the calling session. -- Reads default to ten recent messages, exclude tool outputs, and enforce configured per-item and total character limits. -- Navigator uses the same V1-or-V2 protocol choice as OpenCode Desktop so created sessions remain visible there. -- V2 create and send operations reject unknown agent names before admitting a prompt. -- Worktree removal rejects the main checkout, unmanaged worktrees, and worktrees containing active sessions. -- Partial creation errors identify resources already created and never automatically delete them. +- V1 uses Desktop's protocol detection; V2 calls the native plugin context directly. +- V2 create/send validate agent and model overrides at the destination location. +- V1 worktree removal rejects the main checkout, unmanaged worktrees, and worktrees containing active sessions. +- V2 partial creation failures return `status: "partial_failure"`, a failure phase, known session/worktree details, and recovery guidance. Progress updates report created resources before prompting. No automatic deletion occurs. -Navigator does not keep a separate job database. Existing native sessions and worktrees remain discoverable after plugin restarts. +Navigator does not keep a separate job database. V2 worktrees are discoverable after reloads and known native session IDs remain usable. Desktop's current New worktree button explicitly selects Git; the V2 Navigator create tool can use Rift without that UI change. diff --git a/packages/web/src/pages/index.astro b/packages/web/src/pages/index.astro index 3250888..93ff333 100644 --- a/packages/web/src/pages/index.astro +++ b/packages/web/src/pages/index.astro @@ -12,7 +12,7 @@ const heroHighlights = [ }, { eyebrow: "Adapters", - title: "OpenCode workflows and native session navigation" + title: "OpenCode workflows, independent worktrees and sessions" }, { eyebrow: "Integrations",