From 62de422ff7e10346489edbae4972bc7278a9ce49 Mon Sep 17 00:00:00 2001 From: Ian Pascoe Date: Thu, 17 Sep 2026 09:58:08 -0400 Subject: [PATCH 1/4] Add a Pi extension that checks successful edits (#43) --- .changeset/pi-edit-checks.md | 5 + CONTEXT-MAP.md | 2 + bun.lock | 330 ++++++++++++++++++++++++ package.json | 2 +- packages/pi/CONTEXT.md | 13 + packages/pi/README.md | 13 + packages/pi/package.json | 30 +++ packages/pi/src/extension.ts | 373 ++++++++++++++++++++++++++++ packages/pi/tests/extension.test.ts | 359 ++++++++++++++++++++++++++ packages/pi/tsconfig.json | 19 ++ tsconfig.json | 3 +- 11 files changed, 1147 insertions(+), 2 deletions(-) create mode 100644 .changeset/pi-edit-checks.md create mode 100644 packages/pi/CONTEXT.md create mode 100644 packages/pi/README.md create mode 100644 packages/pi/package.json create mode 100644 packages/pi/src/extension.ts create mode 100644 packages/pi/tests/extension.test.ts create mode 100644 packages/pi/tsconfig.json diff --git a/.changeset/pi-edit-checks.md b/.changeset/pi-edit-checks.md new file mode 100644 index 0000000..add1538 --- /dev/null +++ b/.changeset/pi-edit-checks.md @@ -0,0 +1,5 @@ +--- +"@antisprawl/pi": minor +--- + +Add a Pi extension that checks successful edits for Probable duplicates. diff --git a/CONTEXT-MAP.md b/CONTEXT-MAP.md index aef2dcb..c18829e 100644 --- a/CONTEXT-MAP.md +++ b/CONTEXT-MAP.md @@ -4,7 +4,9 @@ - [Antisprawl](./docs/CONTEXT.md): shared product language for duplicate-evidence analysis - [CLI](./packages/cli/CONTEXT.md): executable delivery of Antisprawl analysis +- [Pi](./packages/pi/CONTEXT.md): Pi harness mapping onto Antisprawl ## Relationships - **CLI → Antisprawl**: the CLI implements Antisprawl analysis and adds no terms of its own. +- **Pi → Antisprawl**: maps a Pi session to Agent session and a successful edit or write to one Edit batch; does not own Project or Index. diff --git a/bun.lock b/bun.lock index 1ff5aba..40f4739 100644 --- a/bun.lock +++ b/bun.lock @@ -28,8 +28,72 @@ "web-tree-sitter": "0.27.0", }, }, + "packages/pi": { + "name": "@antisprawl/pi", + "version": "0.0.0", + "devDependencies": { + "@earendil-works/pi-coding-agent": "0.85.1", + }, + "peerDependencies": { + "@earendil-works/pi-coding-agent": "*", + }, + }, }, "packages": { + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.123.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1", "standardwebhooks": "^1.0.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-Y9oX9mPNGZClHQOFqrWRk43Srcu/UHuPq3rfxxOq7JgW0gi+lJA2MAOK4Ul3k/+AUrwRWFJvd0tK3oC0Pw25dw=="], + + "@antisprawl/pi": ["@antisprawl/pi@workspace:packages/pi"], + + "@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-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/eventstream-handler-node": "^3.972.16", "@aws-sdk/middleware-eventstream": "^3.972.12", "@aws-sdk/middleware-websocket": "^3.972.19", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.978.0", "", { "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-2yX9LUmxPklVjSGTb8dfnWRJSiFQ3TeH2nn7G1mdKHTfnabzF0+gfrS8rYfLWmZrQ8A3mEcxMJjRc51dL5KWaA=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.71", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-JN+JHruYZw3GUZB8YGAlDk4wTDPOEAEEdEzj5nS0xodWR4smzHsN7PnK2j6IeOsDIj2aqua5DSbhXl9Gtf90FQ=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.73", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@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-uyYYnJOnlis8uQzaYGPd7N1JoioCoNpXgnkXYixsWJXHXgXyYi8WXJSDfofxJeWfQIGWLe2Nwyq60Uc7MZdVOg=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.16", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/credential-provider-env": "^3.972.71", "@aws-sdk/credential-provider-http": "^3.972.73", "@aws-sdk/credential-provider-login": "^3.972.78", "@aws-sdk/credential-provider-process": "^3.972.71", "@aws-sdk/credential-provider-sso": "^3.973.15", "@aws-sdk/credential-provider-web-identity": "^3.972.77", "@aws-sdk/nested-clients": "^3.997.45", "@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-i++ly+0Uxa+u3ebSSyr0S/3CFhFJDxCXT3+Zj+mW2bXenEx5bKGCdTIKFu39SgXBNhWDjex/8cXUx9MUTMCrTw=="], + + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.78", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/nested-clients": "^3.997.45", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-eUtswnXu0+Ii9ieRK+0L7aPFV3Z/dnW2VntJzjBP9xs8s+8p5nBNuymIXtXwZ+5r5+XJP3e32nMkuZ/r0HozEA=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.83", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.71", "@aws-sdk/credential-provider-http": "^3.972.73", "@aws-sdk/credential-provider-ini": "^3.973.16", "@aws-sdk/credential-provider-process": "^3.972.71", "@aws-sdk/credential-provider-sso": "^3.973.15", "@aws-sdk/credential-provider-web-identity": "^3.972.77", "@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-jdso7ejzfRnatxMUZK4S/U6KbaDPCvfIV4XL+IQAPFDBt5rj5Fq595euqlK8Le4lNCMFR9oUpt+1l0aMgaayOQ=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.71", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-lYmXJa4gvq4xN1lrT5NiP5vIYYKcGWAdj8y+8o6dlcateB5eF3Dn8DtmjjHKfMBrTPAMr2pebIiX/UOj8c1/UA=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.15", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/nested-clients": "^3.997.45", "@aws-sdk/token-providers": "3.1129.0", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-6Jhcf4v0pSFdjk1EW2kvzuEBKD+UZ2uNcHUIglKKLndD20YhvkL2kdmDOV5/j4mYuWWwe/a1FQ1aomU86/Cg5Q=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.77", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/nested-clients": "^3.997.45", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-uylIQSUWpfLuH2LovxEEfwzJGM/SabLOfLMg6YXu/E8jJEKUdpdILCVCQCdFvHyu/7dLJOHPMfrSwduxO56NkQ=="], + + "@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.972.34", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-cTeVzpu1xEAkryTZBYhGwnQ6gOGyp8ZYZvmn0Sg/nI/ABmy/CRHHxPDJDUi9PxwxUtGGaatvfRUB3FCgT/rSWw=="], + + "@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.972.29", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-dlRzHCgyB8W6hLuDC5pcT5q+ziPt00n4QGgGBE17ucLVU4zMa6lsbuUdQ2Pm75Z5VA8GF+R/+SgrRcaTdIzSIQ=="], + + "@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.972.53", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-bIrDaMENQmYRBHntOiOheqkiw5+fhKW4Lqb+mS1uqF0VwvdWI22fW2HFgWrng66CmYd+4k8ePlpj38sEfTuMLQ=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.45", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@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-mooq9Q+jLa18VoM7HouczmslZU60iiB0aKc/Ztnq/luIL1ud0z4DnYprLR/ZO1gp331S9tJctM1HZr7u6YKBXQ=="], + + "@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.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="], + + "@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/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@8.1.1", "", { "dependencies": { "@changesets/config": "^4.0.1", "@changesets/format": "^0.1.2", "@changesets/git": "^4.0.1", "@changesets/should-skip-package": "^1.0.0", "@changesets/types": "^7.0.0", "import-meta-resolve": "^4.2.0", "jsonc-parser": "^3.3.1", "semver": "^7.8.1" } }, "sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q=="], "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@7.0.0", "", { "dependencies": { "@changesets/errors": "^1.0.0", "@changesets/get-dependents-graph": "^3.0.0", "@changesets/should-skip-package": "^1.0.0", "@changesets/types": "^7.0.0", "semver": "^7.8.1" } }, "sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg=="], @@ -64,6 +128,18 @@ "@clack/prompts": ["@clack/prompts@1.8.1", "", { "dependencies": { "@clack/core": "1.5.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ=="], + "@earendil-works/chord": ["@earendil-works/chord@0.85.1", "", { "dependencies": { "esbuild": "0.28.1" } }, "sha512-VDlkEC3dhCzQ5fcyH1OhG19dq+6jCn+rqc/iXFivwDYGR5anwo2RCiXij9PpHhqNR5GuhhE+Er69Zi1Sn4eY6w=="], + + "@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@0.85.1", "", { "dependencies": { "@earendil-works/chord": "^0.85.1", "@earendil-works/pi-ai": "^0.85.1", "@earendil-works/pi-telemetry": "^0.85.1", "diff": "8.0.4", "ignore": "7.0.5", "typebox": "1.3.7", "yaml": "2.9.0" } }, "sha512-hIXIP3eAWueAYiAl8aMvWCvvZ8Q5gT3Dip5bE5uJyIGh4+YlWRjtMLI4BaeoXoSs93zndjue61u1B/vhefLnuA=="], + + "@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.85.1", "", { "dependencies": { "@anthropic-ai/sdk": "0.123.0", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@earendil-works/pi-telemetry": "^0.85.1", "@google/genai": "1.52.0", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", "openai": "6.40.0", "partial-json": "0.1.7", "typebox": "1.3.7" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-+VgVIJDkDO2efYJKEEqvPTH4zmnIaXdAppGbO+vKFA9qy5PdhFiAenuFAkU+oiCSfOC4dMHDyrjdQeL4ZoC5CQ=="], + + "@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@0.85.1", "", { "dependencies": { "@earendil-works/chord": "^0.85.1", "@earendil-works/pi-agent-core": "^0.85.1", "@earendil-works/pi-ai": "^0.85.1", "@earendil-works/pi-tui": "^0.85.1", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", "diff": "8.0.4", "grok-mermaid": "0.2.2", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.5", "jiti": "2.7.0", "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", "typebox": "1.3.7", "undici": "8.9.0", "yaml": "2.9.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "pi": "dist/bundle/cli.js" } }, "sha512-FGRN+OHbWaefBPGaTggAdLjrIHW+s2PzLyglz/5dfLzb9of7uuXMXYC0fJIeZTw+shS32o2cuQ9jF7YSDuL/oQ=="], + + "@earendil-works/pi-telemetry": ["@earendil-works/pi-telemetry@0.85.1", "", {}, "sha512-Bg/YN6kA7Swja/NQxka8xFdecb4E/auIEGF2G5A25EaQXhRnPj300/7/KpgsDDMYUzHTDAv4RyUxaQPJKW81Rw=="], + + "@earendil-works/pi-tui": ["@earendil-works/pi-tui@0.85.1", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "18.0.5" } }, "sha512-OIzw9efInmO4WOBnD4TxcTdBjmzvYJpzslkgoUro946nEGoYWg5rwv1p4fDt3/JvMx9QybryUCUwlm7j8Dreig=="], + "@effect/platform-bun": ["@effect/platform-bun@4.0.0-rc.115", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-rc.115" }, "peerDependencies": { "effect": "^4.0.0-rc.115" } }, "sha512-ilTFcs66ghInP8WHCJYa4bx/yzohYM9dRR//qjmVBBpSKu1hrCTiRWpEcc1N6zrnOEPk2kyVaKg5ykSMgiBMqQ=="], "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-rc.115", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.21.3" }, "peerDependencies": { "effect": "^4.0.0-rc.115" } }, "sha512-rcBwhDIfb82akJoTv7Vwh8K5x9vC/XfuePS7JjOr+z+dORXzsmKqI1BUQF27ARCN9fTAbDh7I6YvBlWhLjnqNg=="], @@ -86,6 +162,60 @@ "@effect/tsgo-win32-x64": ["@effect/tsgo-win32-x64@0.45.0", "", { "os": "win32", "cpu": "x64" }, "sha512-1Hsr/sT5dmIafoY9L0ASJo4VqGyPNzM1nwTb4Wj1Lmjq6kGuwO/PVozFg3WZZ0piChFmgiU3nx1AKZfyqgjGUw=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + + "@google/genai": ["@google/genai@1.52.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q=="], + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], "@manypkg/find-root": ["@manypkg/find-root@3.1.0", "", { "dependencies": { "@manypkg/tools": "^2.1.0" } }, "sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA=="], @@ -94,6 +224,28 @@ "@manypkg/tools": ["@manypkg/tools@2.1.2", "", { "dependencies": { "jju": "^1.4.0", "tinyglobby": "^0.2.13", "yaml": "^2.9.0" } }, "sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ=="], + "@mariozechner/clipboard": ["@mariozechner/clipboard@0.3.9", "", { "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.9", "@mariozechner/clipboard-darwin-universal": "0.3.9", "@mariozechner/clipboard-darwin-x64": "0.3.9", "@mariozechner/clipboard-linux-arm64-gnu": "0.3.9", "@mariozechner/clipboard-linux-arm64-musl": "0.3.9", "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-musl": "0.3.9", "@mariozechner/clipboard-win32-arm64-msvc": "0.3.9", "@mariozechner/clipboard-win32-x64-msvc": "0.3.9" } }, "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA=="], + + "@mariozechner/clipboard-darwin-arm64": ["@mariozechner/clipboard-darwin-arm64@0.3.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ=="], + + "@mariozechner/clipboard-darwin-universal": ["@mariozechner/clipboard-darwin-universal@0.3.9", "", { "os": "darwin" }, "sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ=="], + + "@mariozechner/clipboard-darwin-x64": ["@mariozechner/clipboard-darwin-x64@0.3.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg=="], + + "@mariozechner/clipboard-linux-arm64-gnu": ["@mariozechner/clipboard-linux-arm64-gnu@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw=="], + + "@mariozechner/clipboard-linux-arm64-musl": ["@mariozechner/clipboard-linux-arm64-musl@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ=="], + + "@mariozechner/clipboard-linux-riscv64-gnu": ["@mariozechner/clipboard-linux-riscv64-gnu@0.3.9", "", { "os": "linux", "cpu": "none" }, "sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw=="], + + "@mariozechner/clipboard-linux-x64-gnu": ["@mariozechner/clipboard-linux-x64-gnu@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw=="], + + "@mariozechner/clipboard-linux-x64-musl": ["@mariozechner/clipboard-linux-x64-musl@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ=="], + + "@mariozechner/clipboard-win32-arm64-msvc": ["@mariozechner/clipboard-win32-arm64-msvc@0.3.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ=="], + + "@mariozechner/clipboard-win32-x64-msvc": ["@mariozechner/clipboard-win32-x64-msvc@0.3.9", "", { "os": "win32", "cpu": "x64" }, "sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA=="], + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.68.0", "", { "os": "android", "cpu": "arm" }, "sha512-dhfYPbzv/h9JgHjNkl2R6sOjUfxDyLGOZVb3g8/ScaTNwwJcYgmHh8kcYFDUhinuy1QAoANCWUvw1jlk+z6gAg=="], "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.68.0", "", { "os": "android", "cpu": "arm64" }, "sha512-v3Njdi6qY0O/5eGfg01ww2w6gTn2mUvZ72Bnx1/UN53A9wruh3Nk6otc3WkgJLkXD4Qgz1SOcVQieH1oD03V9Q=="], @@ -186,10 +338,52 @@ "@pnpm/deps.graph-sequencer": ["@pnpm/deps.graph-sequencer@1100.0.1", "", {}, "sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A=="], + "@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=="], + + "@silvia-odwyer/photon-node": ["@silvia-odwyer/photon-node@0.3.4", "", {}, "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA=="], + + "@smithy/core": ["@smithy/core@3.34.1", "", { "dependencies": { "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-dLcOUxz8YCv1RZUMKq6GbyUf95pLbrqh34bPvpCZ1+CByFF31BEAFewZjsGCnVsZTKdThNENfGyAgk2TJqVwSw=="], + + "@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/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.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="], + + "@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.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], + + "@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=="], + + "@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=="], + + "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="], + "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="], "@types/node": ["@types/node@26.5.1", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g=="], + "@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], @@ -232,16 +426,48 @@ "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "antisprawl": ["antisprawl@workspace:packages/cli"], + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + + "bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="], + + "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="], "cac": ["cac@7.0.0", "", {}, "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "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" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], + + "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.115", "", {}, "sha512-ogYulZ5ffeOzrJqQrG0XOkDO5lKn2s9KNHhoxJy/6wKUkF0i12dlVXWA9TgsgQ+zV/JzizyG0+XMPepZEQX6vw=="], + "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="], + "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], @@ -250,44 +476,122 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + + "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.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="], + + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + + "google-auth-library": ["google-auth-library@10.9.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw=="], + + "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "grok-mermaid": ["grok-mermaid@0.2.2", "", {}, "sha512-XcJEP5dDC8liHBh52mlLjU18fNvu1ckFsu0QpIG3+APZ270fsj9wxpiA6cOURmbUEuoMVgjbC2+UYgTdCqqgzA=="], + + "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], + + "hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="], + + "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=="], + "human-id": ["human-id@4.2.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw=="], "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + "jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="], + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + "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=="], + "launch-editor": ["launch-editor@2.14.1", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.4" } }, "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA=="], "lint-staged": ["lint-staged@17.5.1", "", { "dependencies": { "picomatch": "^4.0.7", "string-argv": "^0.3.2", "tinyexec": "^1.3.1" }, "optionalDependencies": { "yaml": "^2.9.0" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-7EDuco1xnBMeVpvAbeMq1U5KXwJGLmY8q6l+Ye78r36C4mPc+Vg1Z2SK8gHyRTyvPro90A0q5/FAZ+Au23d6QQ=="], + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], + + "marked": ["marked@18.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="], + + "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "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=="], + + "openai": ["openai@6.40.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"] }, "sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA=="], + "oxfmt": ["oxfmt@0.68.0", "", { "dependencies": { "tinypool": "2.1.2" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.68.0", "@oxfmt/binding-android-arm64": "0.68.0", "@oxfmt/binding-darwin-arm64": "0.68.0", "@oxfmt/binding-darwin-x64": "0.68.0", "@oxfmt/binding-freebsd-x64": "0.68.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.68.0", "@oxfmt/binding-linux-arm-musleabihf": "0.68.0", "@oxfmt/binding-linux-arm64-gnu": "0.68.0", "@oxfmt/binding-linux-arm64-musl": "0.68.0", "@oxfmt/binding-linux-ppc64-gnu": "0.68.0", "@oxfmt/binding-linux-riscv64-gnu": "0.68.0", "@oxfmt/binding-linux-riscv64-musl": "0.68.0", "@oxfmt/binding-linux-s390x-gnu": "0.68.0", "@oxfmt/binding-linux-x64-gnu": "0.68.0", "@oxfmt/binding-linux-x64-musl": "0.68.0", "@oxfmt/binding-openharmony-arm64": "0.68.0", "@oxfmt/binding-win32-arm64-msvc": "0.68.0", "@oxfmt/binding-win32-ia32-msvc": "0.68.0", "@oxfmt/binding-win32-x64-msvc": "0.68.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-Z0XMofcXCGUXbcpBHnWyUiX93BGiw1B+lcHNbQDWEtOhX06ewoFfu4zXkyiLhRrNnMq0twqXRHUcJetf+GsiQQ=="], "oxlint": ["oxlint@1.82.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.82.0", "@oxlint/binding-android-arm64": "1.82.0", "@oxlint/binding-darwin-arm64": "1.82.0", "@oxlint/binding-darwin-x64": "1.82.0", "@oxlint/binding-freebsd-x64": "1.82.0", "@oxlint/binding-linux-arm-gnueabihf": "1.82.0", "@oxlint/binding-linux-arm-musleabihf": "1.82.0", "@oxlint/binding-linux-arm64-gnu": "1.82.0", "@oxlint/binding-linux-arm64-musl": "1.82.0", "@oxlint/binding-linux-ppc64-gnu": "1.82.0", "@oxlint/binding-linux-riscv64-gnu": "1.82.0", "@oxlint/binding-linux-riscv64-musl": "1.82.0", "@oxlint/binding-linux-s390x-gnu": "1.82.0", "@oxlint/binding-linux-x64-gnu": "1.82.0", "@oxlint/binding-linux-x64-musl": "1.82.0", "@oxlint/binding-openharmony-arm64": "1.82.0", "@oxlint/binding-win32-arm64-msvc": "1.82.0", "@oxlint/binding-win32-ia32-msvc": "1.82.0", "@oxlint/binding-win32-x64-msvc": "1.82.0" }, "peerDependencies": { "oxlint-tsgolint": ">=7.0.2001", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-+iFM1BGw1ntYJt3QngbJmjbrGxPaKMUADOXOijpWGnYcBPq8YZnQftSS1C+pVcDYy9YxqDVJKQqQkTazTQMboQ=="], "oxlint-tsgolint": ["oxlint-tsgolint@7.0.2001", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "7.0.2001", "@oxlint-tsgolint/darwin-x64": "7.0.2001", "@oxlint-tsgolint/linux-arm64": "7.0.2001", "@oxlint-tsgolint/linux-x64": "7.0.2001", "@oxlint-tsgolint/win32-arm64": "7.0.2001", "@oxlint-tsgolint/win32-x64": "7.0.2001" }, "bin": { "tsgolint": "./bin/tsgolint.js" } }, "sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg=="], + "p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="], + "package-manager-detector": ["package-manager-detector@1.8.0", "", {}, "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A=="], + "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + + "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=="], + + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "shell-quote": ["shell-quote@1.10.0", "", {}, "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], "skills": ["skills@1.6.0", "", { "dependencies": { "tar": "^7.5.20", "yaml": "^2.8.3" }, "bin": { "skills": "bin/cli.mjs", "add-skill": "bin/cli.mjs" } }, "sha512-E2tN8PM2ReYtt8xxXFLTIm/4OWjLeuhudgAejN0N/g5Yr6BYdv+YP2PK5Vg9L3Oz3aRgu67ovWzAO3Y6Z3JJWQ=="], + "standardwebhooks": ["standardwebhooks@1.1.1", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ=="], + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], "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=="], @@ -298,16 +602,42 @@ "tinypool": ["tinypool@2.1.2", "", {}, "sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww=="], + "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typebox": ["typebox@1.3.7", "", {}, "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg=="], + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + "undici": ["undici@8.9.0", "", {}, "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA=="], + "undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "web-tree-sitter": ["web-tree-sitter@0.27.0", "", {}, "sha512-XK08gj6RwTMQatAG7uVRP8MunqotL/XC19vHgkSPKmELgbGPBj4ECvB8haHOUnyj6ls2B8t42UTro14zxGgAHg=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "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=="], "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "yaml": ["yaml@2.9.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="], + + "@aws-sdk/credential-provider-http/@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=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1129.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.0", "@aws-sdk/nested-clients": "^3.997.45", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Sbl3rpzQdsG4ZK2zh0JWUYyZPKKorJlVOddA2T0DVbKJFrsW8J6wgnslxxUH04+WaBMr4A1HzJZvZX0xUvkniA=="], + + "@aws-sdk/nested-clients/@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=="], + + "@earendil-works/pi-agent-core/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + + "@earendil-works/pi-coding-agent/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "@earendil-works/pi-coding-agent/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + + "p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], } } diff --git a/package.json b/package.json index 1eebc05..70a7782 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "lint:staged": "lint-staged", "prepare": "husky && effect-tsgo patch --typescript --oxlint && ./scripts/sync-reference-repos.sh", "test": "bun test", - "typecheck": "tsc --noEmit", + "typecheck": "tsc --noEmit && tsc --noEmit -p packages/pi", "verify": "bun run format:check && bun run lint && bun run typecheck && bun run test", "version": "changeset version" }, diff --git a/packages/pi/CONTEXT.md b/packages/pi/CONTEXT.md new file mode 100644 index 0000000..025f2cc --- /dev/null +++ b/packages/pi/CONTEXT.md @@ -0,0 +1,13 @@ +# Pi + +Maps the Pi coding-agent harness onto Antisprawl. Does not own Project or Index. + +## Language + +**Successful edit or write**: +One Edit batch. +_Avoid_: Turn, tool call, commit + +**Pi session**: +One Agent session. +_Avoid_: Process, command invocation diff --git a/packages/pi/README.md b/packages/pi/README.md new file mode 100644 index 0000000..6456f24 --- /dev/null +++ b/packages/pi/README.md @@ -0,0 +1,13 @@ +# @antisprawl/pi + +After a successful Pi `edit` or `write`, run `antisprawl check` on that path and append bounded Findings to the same tool result. + +## Manual smoke + +From this repo, in a Project that already has an Index: + +``` +pi -e ./packages/pi/src/extension.ts +``` + +Edit a TypeScript source file. The original tool result stays; Findings, if any, append after it. diff --git a/packages/pi/package.json b/packages/pi/package.json new file mode 100644 index 0000000..9e2ff68 --- /dev/null +++ b/packages/pi/package.json @@ -0,0 +1,30 @@ +{ + "name": "@antisprawl/pi", + "version": "0.0.0", + "description": "Pi extension that checks successful edits for Probable duplicates.", + "keywords": [ + "pi-package" + ], + "type": "module", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@earendil-works/pi-coding-agent": "0.85.1" + }, + "peerDependencies": { + "@earendil-works/pi-coding-agent": "*" + }, + "devEngines": { + "packageManager": { + "name": "bun", + "version": "1.4.2", + "onFail": "error" + } + }, + "pi": { + "extensions": [ + "./src/extension.ts" + ] + } +} diff --git a/packages/pi/src/extension.ts b/packages/pi/src/extension.ts new file mode 100644 index 0000000..47d173d --- /dev/null +++ b/packages/pi/src/extension.ts @@ -0,0 +1,373 @@ +import { + isEditToolResult, + isWriteToolResult, + type ExecResult, + type ExtensionAPI, + type ExtensionContext, + type ToolResultEvent, +} from "@earendil-works/pi-coding-agent"; + +const checkTimeoutMs = 5000; + +const silentCodes = new Set(["project_not_found", "source_path_unsupported"]); + +const indexCodes = new Set(["index_missing", "index_incompatible"]); + +const configCodes = new Set(["config_invalid", "config_invalid_jsonc", "config_unreadable"]); + +type Json = string | number | boolean | null | readonly Json[] | JsonObject; + +type JsonObject = { readonly [key: string]: Json }; + +type Location = { + readonly path: string; + readonly qualifiedName: string; + readonly range: { readonly start: { readonly line: number; readonly column: number } }; +}; + +type Finding = { + readonly id: string; + readonly edited: Location; + readonly candidate: Location; + readonly structuralEvidence: { + readonly strictHashEqual: boolean; + readonly normalizedHashEqual: boolean; + readonly qgramSimilarity: number; + readonly orderedTokenSimilarity: number; + }; + readonly semanticEvidence: { readonly cosineSimilarity: number } | undefined; + readonly guidance: string; +}; + +type Diagnostic = { + readonly code: string; + readonly path: string | undefined; + readonly message: string | undefined; +}; + +type CheckOutput = { + readonly findings: readonly Finding[]; + readonly diagnostics: readonly Diagnostic[]; +}; + +type ToolResultPatch = { + readonly content: ToolResultEvent["content"]; +}; + +const jsonTag = (value: Json | undefined): string => Object.prototype.toString.call(value); + +const asObject = (value: Json | undefined): JsonObject | undefined => { + if (value === undefined || jsonTag(value) !== "[object Object]") return; + + // SAFETY: JSON objects are string-key maps. + return value as JsonObject; +}; + +const asString = (value: Json | undefined): string | undefined => { + if (jsonTag(value) !== "[object String]") return; + + // SAFETY: JSON string primitives tag as [object String]. + return value as string; +}; + +const asBoolean = (value: Json | undefined): boolean | undefined => { + if (jsonTag(value) !== "[object Boolean]") return; + + return value === true; +}; + +const asFinite = (value: Json | undefined): number | undefined => { + if (jsonTag(value) !== "[object Number]" || !Number.isFinite(value)) return; + + // SAFETY: JSON numbers that pass Number.isFinite are finite numbers. + return value as number; +}; + +const asArray = (value: Json | undefined): readonly Json[] | undefined => + Array.isArray(value) ? value : undefined; + +const parseJson = (text: string): Json | undefined => { + try { + // SAFETY: JSON.parse returns JSON values. + return JSON.parse(text) as Json; + } catch { + return; + } +}; + +const asLocation = (value: Json | undefined): Location | undefined => { + const object = asObject(value); + + if (object === undefined) return; + + const path = asString(object.path); + const qualifiedName = asString(object.qualifiedName); + const range = asObject(object.range); + const start = range === undefined ? undefined : asObject(range.start); + const line = start === undefined ? undefined : asFinite(start.line); + const column = start === undefined ? undefined : asFinite(start.column); + + if ( + path === undefined || + qualifiedName === undefined || + line === undefined || + column === undefined + ) { + return; + } + + return { path, qualifiedName, range: { start: { line, column } } }; +}; + +const asFinding = (value: Json): Finding | undefined => { + const object = asObject(value); + + if (object === undefined) return; + + const id = asString(object.id); + const edited = asLocation(object.edited); + const candidate = asLocation(object.candidate); + const structural = asObject(object.structuralEvidence); + const guidance = asString(object.guidance); + + const strictHashEqual = + structural === undefined ? undefined : asBoolean(structural.strictHashEqual); + + const normalizedHashEqual = + structural === undefined ? undefined : asBoolean(structural.normalizedHashEqual); + + const qgramSimilarity = + structural === undefined ? undefined : asFinite(structural.qgramSimilarity); + + const orderedTokenSimilarity = + structural === undefined ? undefined : asFinite(structural.orderedTokenSimilarity); + + if ( + id === undefined || + edited === undefined || + candidate === undefined || + guidance === undefined || + strictHashEqual === undefined || + normalizedHashEqual === undefined || + qgramSimilarity === undefined || + orderedTokenSimilarity === undefined + ) { + return; + } + + let semanticEvidence: Finding["semanticEvidence"]; + + if (object.semanticEvidence !== undefined) { + const cosineSimilarity = asFinite(asObject(object.semanticEvidence)?.cosineSimilarity); + + if (cosineSimilarity === undefined) return; + + semanticEvidence = { cosineSimilarity }; + } + + return { + id, + edited, + candidate, + structuralEvidence: { + strictHashEqual, + normalizedHashEqual, + qgramSimilarity, + orderedTokenSimilarity, + }, + semanticEvidence, + guidance, + }; +}; + +const asDiagnostic = (value: Json): Diagnostic | undefined => { + const object = asObject(value); + + if (object === undefined) return; + + const code = asString(object.code); + + if (code === undefined) return; + + return { + code, + path: object.path === undefined ? undefined : asString(object.path), + message: object.message === undefined ? undefined : asString(object.message), + }; +}; + +const parseCheck = (stdout: string): CheckOutput | undefined => { + const object = asObject(parseJson(stdout)); + + if (object === undefined) return; + + if (asFinite(object.protocolVersion) !== 1 || asString(object.command) !== "check") return; + + const findingsJson = asArray(object.findings); + const diagnosticsJson = asArray(object.diagnostics); + + if (findingsJson === undefined || diagnosticsJson === undefined) return; + + const findings: Finding[] = []; + + for (const item of findingsJson) { + const finding = asFinding(item); + + if (finding === undefined) return; + + findings.push(finding); + } + + const diagnostics: Diagnostic[] = []; + + for (const item of diagnosticsJson) { + const diagnostic = asDiagnostic(item); + + if (diagnostic === undefined) return; + + diagnostics.push(diagnostic); + } + + return { findings, diagnostics }; +}; + +const errorCode = (stderr: string): string | undefined => /error\[([^\]]+)\]:/.exec(stderr)?.[1]; + +const formatLocation = (location: Location): string => + `${location.qualifiedName} (${location.path}:${location.range.start.line}:${location.range.start.column})`; + +const renderFinding = (finding: Finding): string => { + const lines = [ + `id: ${finding.id}`, + `Edited: ${formatLocation(finding.edited)}`, + `Candidate: ${formatLocation(finding.candidate)}`, + `strictHashEqual: ${finding.structuralEvidence.strictHashEqual}`, + `normalizedHashEqual: ${finding.structuralEvidence.normalizedHashEqual}`, + `qgramSimilarity: ${finding.structuralEvidence.qgramSimilarity}`, + `orderedTokenSimilarity: ${finding.structuralEvidence.orderedTokenSimilarity}`, + ]; + + if (finding.semanticEvidence !== undefined) { + lines.push(`cosineSimilarity: ${finding.semanticEvidence.cosineSimilarity}`); + } + + lines.push(finding.guidance); + + return lines.join("\n"); +}; + +const renderDiagnostic = (diagnostic: Diagnostic): string => { + const parts = [diagnostic.code]; + + if (diagnostic.path !== undefined) parts.push(diagnostic.path); + + if (diagnostic.message !== undefined) parts.push(diagnostic.message); + + return parts.join(" "); +}; + +const appendText = ( + event: ToolResultEvent, + lines: readonly string[], +): ToolResultPatch | undefined => { + if (lines.length === 0) return; + + return { + content: [...event.content, { type: "text", text: lines.join("\n") }], + }; +}; + +const note = (seen: Set, key: string, text: string, lines: string[]): void => { + if (seen.has(key)) return; + + seen.add(key); + lines.push(text); +}; + +const pathArgument = (event: ToolResultEvent): string | undefined => { + // SAFETY: edit/write path arguments are JSON strings when present. + const path = asString(event.input.path as Json | undefined); + + if (path === undefined || path.length === 0) return; + + return path.startsWith("@") ? path.slice(1) : path; +}; + +const onToolResult = async ( + event: ToolResultEvent, + ctx: Pick, + exec: ExtensionAPI["exec"], + seen: Set, +): Promise => { + if (event.isError || !(isEditToolResult(event) || isWriteToolResult(event))) return; + + const path = pathArgument(event); + + if (path === undefined) return; + + const result: ExecResult = await exec("antisprawl", ["check", path], { + cwd: ctx.cwd, + signal: ctx.signal, + timeout: checkTimeoutMs, + }); + + if (result.killed) return; + + const code = errorCode(result.stderr); + const lines: string[] = []; + + if (result.code !== 0) { + if (code !== undefined) { + if (silentCodes.has(code)) return; + + if (indexCodes.has(code)) note(seen, "index", "Run antisprawl index.", lines); + else if (configCodes.has(code)) { + note(seen, "config", "Antisprawl configuration is invalid.", lines); + } + + return appendText(event, lines); + } + + if (result.stderr.trim() === "") { + note(seen, "missing", "antisprawl is not on PATH.", lines); + } + + return appendText(event, lines); + } + + const check = parseCheck(result.stdout); + + if (check === undefined) { + note(seen, "protocol", "Update antisprawl and @antisprawl/pi to compatible versions.", lines); + + return appendText(event, lines); + } + + const shown = check.findings.slice(0, 3); + + for (const finding of shown) lines.push(renderFinding(finding)); + + const omitted = check.findings.length - shown.length; + + if (omitted > 0) lines.push(`(+${omitted} more Findings)`); + + for (const diagnostic of check.diagnostics) { + if (seen.has(diagnostic.code)) continue; + + seen.add(diagnostic.code); + lines.push(renderDiagnostic(diagnostic)); + } + + return appendText(event, lines); +}; + +export default function (pi: ExtensionAPI): void { + const seen = new Set(); + + pi.on("session_start", () => { + seen.clear(); + }); + pi.on("tool_result", (event, ctx) => + onToolResult(event, ctx, (command, args, options) => pi.exec(command, args, options), seen), + ); +} diff --git a/packages/pi/tests/extension.test.ts b/packages/pi/tests/extension.test.ts new file mode 100644 index 0000000..0f5b514 --- /dev/null +++ b/packages/pi/tests/extension.test.ts @@ -0,0 +1,359 @@ +import { expect, test } from "bun:test"; +import type { ExecResult, ExtensionAPI, ToolResultEvent } from "@earendil-works/pi-coding-agent"; +import antisprawlPi from "../src/extension.ts"; + +const original = [{ type: "text" as const, text: "edited" }]; + +const location = (path: string, qualifiedName: string, line: number, column: number) => ({ + path, + qualifiedName, + range: { + start: { line, column }, + end: { line, column }, + }, +}); + +const finding = (id: string, edited: string) => ({ + id, + type: "probable_duplicate", + language: "typescript", + edited: location("src/edit.ts", edited, 1, 1), + candidate: location("src/jobs.ts", "collectReadyJobs", 10, 1), + structuralEvidence: { + strictHashEqual: false, + normalizedHashEqual: true, + qgramSimilarity: 0.91, + orderedTokenSimilarity: 0.88, + }, + semanticEvidence: { cosineSimilarity: 0.99 }, + guidance: "Inspect whether the Edited symbol can reuse the Candidate symbol before keeping both.", +}); + +const checkOutput = (findings: ReturnType[], diagnostics: object[] = []) => ({ + protocolVersion: 1, + command: "check", + findings, + diagnostics, +}); + +const execResult = (overrides: Partial = {}): ExecResult => ({ + stdout: "", + stderr: "", + code: 0, + killed: false, + ...overrides, +}); + +const editEvent = ( + overrides: { + isError?: boolean; + path?: string; + omitPath?: boolean; + } = {}, +): ToolResultEvent => { + const event = { + type: "tool_result" as const, + toolCallId: "call-1", + toolName: "edit" as const, + isError: overrides.isError ?? false, + input: overrides.omitPath ? {} : { path: overrides.path ?? "src/edit.ts" }, + content: original, + details: undefined, + }; + + return event; +}; + +const writeEvent = (): ToolResultEvent => ({ + type: "tool_result", + toolCallId: "call-1", + toolName: "write", + isError: false, + input: { path: "src/edit.ts" }, + content: original, + details: undefined, +}); + +const load = (exec: ExtensionAPI["exec"]) => { + let sessionStart = () => undefined; + + let onResult: ( + event: ToolResultEvent, + ctx: { cwd: string; signal: AbortSignal | undefined }, + ) => Promise<{ content?: ToolResultEvent["content"] } | undefined> = async () => undefined; + + const pi = { + exec, + on(event: "session_start" | "tool_result", handler: typeof sessionStart | typeof onResult) { + if (event === "session_start") { + // SAFETY: session_start is registered with a void handler. + sessionStart = handler as typeof sessionStart; + + return; + } + + // SAFETY: tool_result is registered with the result handler. + onResult = handler as typeof onResult; + }, + }; + + // SAFETY: the fake implements the exec/on subset the extension uses. + antisprawlPi(pi as ExtensionAPI); + + return { + sessionStart: () => sessionStart(), + toolResult: (event: ToolResultEvent, cwd = "/project") => + onResult(event, { cwd, signal: undefined }), + }; +}; + +const appendedText = ( + patch: { content?: ToolResultEvent["content"] } | undefined, +): string | undefined => { + const last = patch?.content?.at(-1); + + return last?.type === "text" ? last.text : undefined; +}; + +test("failed edit or write does not invoke antisprawl", async () => { + const extension = load(async () => { + throw new Error("pi.exec should not run"); + }); + + expect(await extension.toolResult(editEvent({ isError: true }))).toBeUndefined(); + expect(await extension.toolResult({ ...writeEvent(), isError: true })).toBeUndefined(); +}); + +test("successful edit invokes check with the exact path, cwd, signal, and timeout", async () => { + const calls: Array<{ + command: string; + args: string[]; + options: { cwd?: string; signal?: AbortSignal; timeout?: number } | undefined; + }> = []; + + const extension = load(async (command, args, options) => { + calls.push({ command, args, options }); + + return execResult({ stdout: JSON.stringify(checkOutput([])) }); + }); + + expect(await extension.toolResult(editEvent())).toBeUndefined(); + expect(calls).toEqual([ + { + command: "antisprawl", + args: ["check", "src/edit.ts"], + options: { cwd: "/project", signal: undefined, timeout: 5000 }, + }, + ]); +}); + +test("leading @ path syntax is stripped before check", async () => { + let args: string[] = []; + + const extension = load(async (_command, received) => { + args = received; + + return execResult({ stdout: JSON.stringify(checkOutput([])) }); + }); + + await extension.toolResult(editEvent({ path: "@src/edit.ts" })); + expect(args).toEqual(["check", "src/edit.ts"]); +}); + +test("successful write is one Edit batch", async () => { + let args: string[] = []; + + const extension = load(async (_command, received) => { + args = received; + + return execResult({ stdout: JSON.stringify(checkOutput([])) }); + }); + + await extension.toolResult(writeEvent()); + expect(args).toEqual(["check", "src/edit.ts"]); +}); + +test("missing path does not invoke antisprawl", async () => { + const extension = load(async () => { + throw new Error("pi.exec should not run"); + }); + + expect(await extension.toolResult(editEvent({ omitPath: true }))).toBeUndefined(); +}); + +test("zero Findings leave the tool result untouched", async () => { + const extension = load(async () => execResult({ stdout: JSON.stringify(checkOutput([])) })); + + expect(await extension.toolResult(editEvent())).toBeUndefined(); +}); + +test("a successful check appends at most three Findings and an omitted count", async () => { + const findings = [ + finding("id-1", "alpha"), + finding("id-2", "beta"), + finding("id-3", "gamma"), + finding("id-4", "delta"), + ]; + + const extension = load(async () => execResult({ stdout: JSON.stringify(checkOutput(findings)) })); + const patch = await extension.toolResult(editEvent()); + + expect(patch?.content?.slice(0, -1)).toEqual(original); + expect(appendedText(patch)).toBe( + [ + "id: id-1", + "Edited: alpha (src/edit.ts:1:1)", + "Candidate: collectReadyJobs (src/jobs.ts:10:1)", + "strictHashEqual: false", + "normalizedHashEqual: true", + "qgramSimilarity: 0.91", + "orderedTokenSimilarity: 0.88", + "cosineSimilarity: 0.99", + "Inspect whether the Edited symbol can reuse the Candidate symbol before keeping both.", + "id: id-2", + "Edited: beta (src/edit.ts:1:1)", + "Candidate: collectReadyJobs (src/jobs.ts:10:1)", + "strictHashEqual: false", + "normalizedHashEqual: true", + "qgramSimilarity: 0.91", + "orderedTokenSimilarity: 0.88", + "cosineSimilarity: 0.99", + "Inspect whether the Edited symbol can reuse the Candidate symbol before keeping both.", + "id: id-3", + "Edited: gamma (src/edit.ts:1:1)", + "Candidate: collectReadyJobs (src/jobs.ts:10:1)", + "strictHashEqual: false", + "normalizedHashEqual: true", + "qgramSimilarity: 0.91", + "orderedTokenSimilarity: 0.88", + "cosineSimilarity: 0.99", + "Inspect whether the Edited symbol can reuse the Candidate symbol before keeping both.", + "(+1 more Findings)", + ].join("\n"), + ); +}); + +test("project_not_found and source_path_unsupported are silent", async () => { + for (const code of ["project_not_found", "source_path_unsupported"]) { + const extension = load(async () => execResult({ code: 1, stderr: `error[${code}]: nope` })); + + expect(await extension.toolResult(editEvent()), code).toBeUndefined(); + } +}); + +test("index_missing and index_incompatible ask to run antisprawl index once", async () => { + let stderr = "error[index_missing]: No Index exists."; + const extension = load(async () => execResult({ code: 1, stderr })); + + expect(appendedText(await extension.toolResult(editEvent()))).toBe("Run antisprawl index."); + stderr = "error[index_incompatible]: The existing Index schema is incompatible."; + expect(appendedText(await extension.toolResult(editEvent()))).toBeUndefined(); +}); + +test("missing antisprawl on PATH is reported once", async () => { + const extension = load(async () => execResult({ code: 1 })); + + expect(appendedText(await extension.toolResult(editEvent()))).toBe("antisprawl is not on PATH."); + expect(appendedText(await extension.toolResult(editEvent()))).toBeUndefined(); +}); + +test("invalid configuration is reported once", async () => { + const extension = load(async () => execResult({ code: 1, stderr: "error[config_invalid]: bad" })); + + expect(appendedText(await extension.toolResult(editEvent()))).toBe( + "Antisprawl configuration is invalid.", + ); + expect(appendedText(await extension.toolResult(editEvent()))).toBeUndefined(); +}); + +test("malformed or incompatible check output asks for a compatible update once", async () => { + const extension = load(async () => execResult({ stdout: "{" })); + + expect(appendedText(await extension.toolResult(editEvent()))).toBe( + "Update antisprawl and @antisprawl/pi to compatible versions.", + ); + expect(appendedText(await extension.toolResult(editEvent()))).toBeUndefined(); +}); + +test("a broken Finding makes the payload incompatible", async () => { + const extension = load(async () => + execResult({ + stdout: JSON.stringify({ + protocolVersion: 1, + command: "check", + findings: [{ id: "x" }], + diagnostics: [], + }), + }), + ); + + expect(appendedText(await extension.toolResult(editEvent()))).toBe( + "Update antisprawl and @antisprawl/pi to compatible versions.", + ); +}); + +test("timeout and unexpected failures stay silent", async () => { + const killed = load(async () => execResult({ killed: true, code: 1 })); + + const unexpected = load(async () => + execResult({ code: 1, stderr: "error: The command failed." }), + ); + + expect(await killed.toolResult(editEvent())).toBeUndefined(); + expect(await unexpected.toolResult(editEvent())).toBeUndefined(); +}); + +test("successful diagnostics are injected once per code", async () => { + const extension = load(async () => + execResult({ + stdout: JSON.stringify( + checkOutput([], [{ code: "vector_search_fallback", path: "src/edit.ts" }]), + ), + }), + ); + + expect(appendedText(await extension.toolResult(editEvent()))).toBe( + "vector_search_fallback src/edit.ts", + ); + expect(appendedText(await extension.toolResult(editEvent()))).toBeUndefined(); +}); + +test("session_start resets diagnostic deduplication", async () => { + const extension = load(async () => execResult({ code: 1 })); + + expect(appendedText(await extension.toolResult(editEvent()))).toBe("antisprawl is not on PATH."); + extension.sessionStart(); + expect(appendedText(await extension.toolResult(editEvent()))).toBe("antisprawl is not on PATH."); +}); + +test("Finding ids are not suppressed after later edits", async () => { + const extension = load(async () => + execResult({ stdout: JSON.stringify(checkOutput([finding("same", "alpha")])) }), + ); + + expect(appendedText(await extension.toolResult(editEvent()))).toContain("id: same"); + expect(appendedText(await extension.toolResult(editEvent()))).toContain("id: same"); +}); + +test("parallel edit checks are not serialized", async () => { + let started = 0; + const waiters: Array<() => void> = []; + + const extension = load(async () => { + started += 1; + await new Promise((resolve) => { + waiters.push(resolve); + }); + + return execResult({ stdout: JSON.stringify(checkOutput([])) }); + }); + + const first = extension.toolResult(editEvent()); + const second = extension.toolResult(editEvent()); + + await Promise.resolve(); + expect(started).toBe(2); + + for (const release of waiters) release(); + await Promise.all([first, second]); +}); diff --git a/packages/pi/tsconfig.json b/packages/pi/tsconfig.json new file mode 100644 index 0000000..4b18ba5 --- /dev/null +++ b/packages/pi/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "strict": true, + "skipLibCheck": true, + "noEmit": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noUncheckedIndexedAccess": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "types": ["bun"] + }, + "include": ["src", "tests"] +} diff --git a/tsconfig.json b/tsconfig.json index d3abd2c..112c55d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -57,5 +57,6 @@ } } ] - } + }, + "exclude": ["packages/pi"] } From 711336977bc7566175e905ffac39353647902f94 Mon Sep 17 00:00:00 2001 From: Ian Pascoe Date: Thu, 17 Sep 2026 10:07:14 -0400 Subject: [PATCH 2/4] Load the Pi extension from project settings --- .pi/settings.json | 3 +++ packages/pi/README.md | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 .pi/settings.json diff --git a/.pi/settings.json b/.pi/settings.json new file mode 100644 index 0000000..203b4d0 --- /dev/null +++ b/.pi/settings.json @@ -0,0 +1,3 @@ +{ + "packages": ["../packages/pi"] +} diff --git a/packages/pi/README.md b/packages/pi/README.md index 6456f24..c109c90 100644 --- a/packages/pi/README.md +++ b/packages/pi/README.md @@ -4,10 +4,6 @@ After a successful Pi `edit` or `write`, run `antisprawl check` on that path and ## Manual smoke -From this repo, in a Project that already has an Index: +This repo loads the extension from `.pi/settings.json`. Trust the project if Pi asks, run `antisprawl index` if there is no Index, then `pi` and edit a TypeScript source file. -``` -pi -e ./packages/pi/src/extension.ts -``` - -Edit a TypeScript source file. The original tool result stays; Findings, if any, append after it. +Or: `pi -e ./packages/pi/src/extension.ts` From fe88561dbea40e4fbf59e1755573a684d87fe0e9 Mon Sep 17 00:00:00 2001 From: Ian Pascoe Date: Thu, 17 Sep 2026 11:12:36 -0400 Subject: [PATCH 3/4] Add GitHub Actions verify with five-target cross-compile PR and main run format, lint, typecheck, and tests on Ubuntu 24.04. The same job cross-compiles the five release Bun targets without executing them. --- .github/workflows/verify.yml | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/verify.yml diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..61c58ca --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,42 @@ +name: verify + +on: + pull_request: + push: + branches: + - main + +concurrency: + group: verify-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.4.2" + + - name: Install + run: bun install --frozen-lockfile --ignore-scripts + + - name: Patch TypeScript language tools + run: bunx effect-tsgo patch --typescript --oxlint + + - name: Verify + run: bun run verify + + - name: Cross-compile release targets + run: | + set -euo pipefail + mkdir -p dist + entry=packages/cli/src/main.ts + for target in bun-linux-x64 bun-linux-arm64 bun-darwin-x64 bun-darwin-arm64 bun-windows-x64; do + bun build --compile --target="$target" --outfile="dist/antisprawl-$target" "$entry" + done From eaa75be4c40f11b3bf81752a7eb2db31aba99c79 Mon Sep 17 00:00:00 2001 From: Ian Pascoe Date: Thu, 17 Sep 2026 11:36:43 -0400 Subject: [PATCH 4/4] Add generated npm packages and Changesets release workflow Cross-compile five CLI targets into optional platform packages plus a launcher, then publish Pi. First 0.0.0 is manual; later main merges publish via OIDC trusted publishing. --- .changeset/config.json | 2 +- .github/workflows/release.yml | 41 ++++++ .oxlintrc.json | 3 +- packages/cli/package.json | 4 +- packages/pi/package.json | 5 + scripts/release.ts | 263 ++++++++++++++++++++++++++++++++++ tsconfig.json | 2 +- 7 files changed, 314 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 scripts/release.ts diff --git a/.changeset/config.json b/.changeset/config.json index a9f0bd9..553b4cb 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -9,7 +9,7 @@ "updateInternalDependencies": "patch", "ignore": [], "privatePackages": { - "version": false, + "version": true, "tag": false } } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8103d13 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +name: release + +on: + push: + branches: + - main + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +permissions: + id-token: write + contents: write + pull-requests: write + +jobs: + release: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.4.2" + + - uses: actions/setup-node@v4 + with: + node-version: "24" + registry-url: "https://registry.npmjs.org" + + - name: Install + run: bun install --frozen-lockfile --ignore-scripts + + - uses: changesets/action@v1 + with: + version: bun run version + publish: bun scripts/release.ts --publish --github-release + title: Version packages + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.oxlintrc.json b/.oxlintrc.json index 5370b68..09ff982 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -13,7 +13,8 @@ ".pi/**", ".roo/**", ".windsurf/**", - "tools/oxlint/anti-slop/**" + "tools/oxlint/anti-slop/**", + "scripts/**" ], "jsPlugins": [ { "name": "anti-slop", "specifier": "./tools/oxlint/anti-slop/index.ts" }, diff --git a/packages/cli/package.json b/packages/cli/package.json index f12b936..dd6bbf0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,11 +1,9 @@ { "name": "antisprawl", "version": "0.0.0", + "private": true, "description": "Keep your agent from writing spaghetti code.", "type": "module", - "publishConfig": { - "access": "public" - }, "scripts": { "build": "bun build --compile src/main.ts --outfile dist/antisprawl" }, diff --git a/packages/pi/package.json b/packages/pi/package.json index 9e2ff68..dbedeb3 100644 --- a/packages/pi/package.json +++ b/packages/pi/package.json @@ -5,6 +5,11 @@ "keywords": [ "pi-package" ], + "files": [ + "src", + "README.md", + "CONTEXT.md" + ], "type": "module", "publishConfig": { "access": "public" diff --git a/scripts/release.ts b/scripts/release.ts new file mode 100644 index 0000000..f96a13c --- /dev/null +++ b/scripts/release.ts @@ -0,0 +1,263 @@ +#!/usr/bin/env bun +import { spawnSync } from "node:child_process"; +import { chmodSync, copyFileSync, cpSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import cliPackage from "../packages/cli/package.json" with { type: "json" }; +import piPackage from "../packages/pi/package.json" with { type: "json" }; + +type Target = { + readonly bun: string; + readonly npm: string; + readonly os: string; + readonly cpu: string; + readonly archive: "tar.gz" | "zip"; + readonly binary: string; +}; + +const launcher = `#!/usr/bin/env node +"use strict"; + +const { spawnSync } = require("node:child_process"); +const { existsSync } = require("node:fs"); +const { dirname, join } = require("node:path"); + +const packages = { + "linux x64": "@antisprawl/linux-x64", + "linux arm64": "@antisprawl/linux-arm64", + "darwin x64": "@antisprawl/darwin-x64", + "darwin arm64": "@antisprawl/darwin-arm64", + "win32 x64": "@antisprawl/windows-x64", +}; + +const name = packages[\`\${process.platform} \${process.arch}\`]; +if (!name) { + console.error(\`antisprawl does not support \${process.platform}/\${process.arch}.\`); + process.exit(1); +} + +const binary = process.platform === "win32" ? "antisprawl.exe" : "antisprawl"; +let directory; +try { + directory = dirname(require.resolve(name + "/package.json")); +} catch { + console.error("antisprawl: missing optional package " + name + ". Reinstall without --omit=optional."); + process.exit(1); +} + +const exe = join(directory, binary); +if (!existsSync(exe)) { + console.error("antisprawl: " + binary + " missing from " + name + "."); + process.exit(1); +} + +const result = spawnSync(exe, process.argv.slice(2), { stdio: "inherit", env: process.env, windowsHide: true }); +if (result.error) { + console.error(result.error.message); + process.exit(1); +} +if (result.signal) process.kill(process.pid, result.signal); +process.exit(result.status ?? 1); +`; + +const run = (command: string, args: string[], cwd: string) => { + const result = spawnSync(command, args, { cwd, stdio: "inherit" }); + + if (result.status !== 0) throw new Error(`${command} ${args.join(" ")} failed`); +}; + +const archiveOs = (os: string) => (os === "win32" ? "windows" : os); + +const main = () => { + const root = join(import.meta.dir, ".."); + const publish = process.argv.includes("--publish"); + const githubRelease = process.argv.includes("--github-release"); + const repository = "git+https://github.com/spiritledsoftware/antisprawl.git"; + const cliVersion = cliPackage.version; + const piVersion = piPackage.version; + + const targets: readonly Target[] = [ + { + bun: "bun-linux-x64", + npm: "@antisprawl/linux-x64", + os: "linux", + cpu: "x64", + archive: "tar.gz", + binary: "antisprawl", + }, + { + bun: "bun-linux-arm64", + npm: "@antisprawl/linux-arm64", + os: "linux", + cpu: "arm64", + archive: "tar.gz", + binary: "antisprawl", + }, + { + bun: "bun-darwin-x64", + npm: "@antisprawl/darwin-x64", + os: "darwin", + cpu: "x64", + archive: "tar.gz", + binary: "antisprawl", + }, + { + bun: "bun-darwin-arm64", + npm: "@antisprawl/darwin-arm64", + os: "darwin", + cpu: "arm64", + archive: "tar.gz", + binary: "antisprawl", + }, + { + bun: "bun-windows-x64", + npm: "@antisprawl/windows-x64", + os: "win32", + cpu: "x64", + archive: "zip", + binary: "antisprawl.exe", + }, + ]; + + const out = join(root, "dist/release"); + const npmRoot = join(out, "npm"); + const archives = join(out, "archives"); + const entry = join(root, "packages/cli/src/main.ts"); + const optionalDependencies: Record = {}; + + rmSync(out, { recursive: true, force: true }); + mkdirSync(archives, { recursive: true }); + + for (const target of targets) { + const pkgDir = join(npmRoot, target.npm); + const outfile = join(pkgDir, target.binary); + const archiveName = `antisprawl-v${cliVersion}-${archiveOs(target.os)}-${target.cpu}.${target.archive}`; + const archivePath = join(archives, archiveName); + + mkdirSync(pkgDir, { recursive: true }); + run( + "bun", + ["build", "--compile", `--target=${target.bun}`, `--outfile=${outfile}`, entry], + root, + ); + chmodSync(outfile, 0o755); + writeFileSync( + join(pkgDir, "package.json"), + `${JSON.stringify( + { + name: target.npm, + version: cliVersion, + description: `antisprawl ${target.os}/${target.cpu} executable`, + os: [target.os], + cpu: [target.cpu], + files: [target.binary], + publishConfig: { access: "public" }, + repository: { type: "git", url: repository }, + }, + null, + 2, + )}\n`, + ); + + optionalDependencies[target.npm] = cliVersion; + + if (target.archive === "zip") run("zip", ["-qj", archivePath, outfile], root); + else run("tar", ["-C", pkgDir, "-czf", archivePath, target.binary], root); + } + + const launcherDir = join(npmRoot, "antisprawl"); + + mkdirSync(join(launcherDir, "bin"), { recursive: true }); + writeFileSync(join(launcherDir, "bin/antisprawl"), launcher, { mode: 0o755 }); + writeFileSync( + join(launcherDir, "package.json"), + `${JSON.stringify( + { + name: "antisprawl", + version: cliVersion, + description: "Keep your agent from writing spaghetti code.", + bin: { antisprawl: "bin/antisprawl" }, + files: ["bin"], + optionalDependencies, + publishConfig: { access: "public" }, + repository: { type: "git", url: repository }, + engines: { node: ">=18" }, + }, + null, + 2, + )}\n`, + ); + writeFileSync( + join(launcherDir, "README.md"), + "# antisprawl\n\n`npm install -g antisprawl` installs the native executable for this platform.\n", + ); + + const piDir = join(npmRoot, "@antisprawl/pi"); + + mkdirSync(piDir, { recursive: true }); + cpSync(join(root, "packages/pi/src"), join(piDir, "src"), { recursive: true }); + copyFileSync(join(root, "packages/pi/README.md"), join(piDir, "README.md")); + copyFileSync(join(root, "packages/pi/CONTEXT.md"), join(piDir, "CONTEXT.md")); + writeFileSync( + join(piDir, "package.json"), + `${JSON.stringify( + { + name: "@antisprawl/pi", + version: piVersion, + description: "Pi extension that checks successful edits for Probable duplicates.", + keywords: ["pi-package"], + type: "module", + files: ["src", "README.md", "CONTEXT.md"], + publishConfig: { access: "public" }, + peerDependencies: { "@earendil-works/pi-coding-agent": "*" }, + pi: { extensions: ["./src/extension.ts"] }, + repository: { type: "git", url: repository }, + }, + null, + 2, + )}\n`, + ); + + const sumNames = targets.map( + (target) => `antisprawl-v${cliVersion}-${archiveOs(target.os)}-${target.cpu}.${target.archive}`, + ); + + const sums = spawnSync("sha256sum", ["--", ...sumNames], { cwd: archives, encoding: "utf8" }); + + if (sums.status !== 0) throw new Error("sha256sum failed"); + + writeFileSync( + join(archives, "SHA256SUMS"), + `${sums.stdout.split("\n").filter(Boolean).sort().join("\n")}\n`, + ); + + if (publish) { + for (const name of [...targets.map((target) => target.npm), "antisprawl", "@antisprawl/pi"]) { + run("npm", ["publish", "--access", "public"], join(npmRoot, name)); + } + } + + if (githubRelease) { + run( + "gh", + [ + "release", + "create", + `v${cliVersion}`, + "--title", + `v${cliVersion}`, + "--notes", + `Unsigned cross-compiled ${cliVersion} CLI archives and npm packages (antisprawl@${cliVersion}, @antisprawl/pi@${piVersion}). Not the v0.1.0 product release.`, + ...targets.map((target) => + join( + archives, + `antisprawl-v${cliVersion}-${archiveOs(target.os)}-${target.cpu}.${target.archive}`, + ), + ), + join(archives, "SHA256SUMS"), + ], + root, + ); + } +}; + +main(); diff --git a/tsconfig.json b/tsconfig.json index 112c55d..01c941a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -58,5 +58,5 @@ } ] }, - "exclude": ["packages/pi"] + "exclude": ["packages/pi", "scripts", "dist"] }