Compare commits

..
Author SHA1 Message Date
jatmn b43f059aa8 Fix plan update callback isolation 2026-05-31 21:33:18 -07:00
jatmn bc0127b0f2 Fix PR review findings 2026-05-31 21:14:36 -07:00
jatmn 7838988867 Split out agent plumbing from OpenTUI branch 2026-05-31 19:31:55 -07:00
19 changed files with 1832 additions and 1679 deletions

No files matched your search

+73 -192
View File
@@ -5,17 +5,20 @@
"": {
"name": "zero",
"dependencies": {
"@opentui/core": "^0.2.16",
"@opentui/solid": "^0.2.16",
"commander": "^15.0.0",
"execa": "^9.6.1",
"ink": "^7.0.4",
"ink-spinner": "^5.0.0",
"ink-text-input": "^6.0.0",
"openai": "^6.39.1",
"react": "^19.2.6",
"react-devtools-core": "^7.0.1",
"shiki": "^4.1.0",
"solid-js": "1.9.12",
"zod": "^4.4.3",
},
"devDependencies": {
"@types/bun": "latest",
"@types/react": "^19.2.15",
},
"peerDependencies": {
"typescript": "^5",
@@ -23,87 +26,7 @@
},
},
"packages": {
"@ampproject/remapping": ["@ampproject/[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
"@babel/code-frame": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="],
"@babel/compat-data": ["@babel/[email protected]", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="],
"@babel/core": ["@babel/[email protected]", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
"@babel/generator": ["@babel/[email protected]", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="],
"@babel/helper-annotate-as-pure": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="],
"@babel/helper-compilation-targets": ["@babel/[email protected]", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="],
"@babel/helper-create-class-features-plugin": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg=="],
"@babel/helper-globals": ["@babel/[email protected]", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="],
"@babel/helper-member-expression-to-functions": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg=="],
"@babel/helper-module-imports": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="],
"@babel/helper-module-transforms": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="],
"@babel/helper-optimise-call-expression": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong=="],
"@babel/helper-plugin-utils": ["@babel/[email protected]", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="],
"@babel/helper-replace-supers": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ=="],
"@babel/helper-skip-transparent-expression-wrappers": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ=="],
"@babel/helper-string-parser": ["@babel/[email protected]", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
"@babel/helper-validator-identifier": ["@babel/[email protected]", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
"@babel/helper-validator-option": ["@babel/[email protected]", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="],
"@babel/helpers": ["@babel/[email protected]", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="],
"@babel/parser": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="],
"@babel/plugin-syntax-jsx": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A=="],
"@babel/plugin-syntax-typescript": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA=="],
"@babel/plugin-transform-modules-commonjs": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ=="],
"@babel/plugin-transform-typescript": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/plugin-syntax-typescript": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw=="],
"@babel/preset-typescript": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="],
"@babel/template": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="],
"@babel/traverse": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="],
"@babel/types": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="],
"@jridgewell/gen-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/resolve-uri": ["@jridgewell/[email protected]", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/[email protected]", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@opentui/core": ["@opentui/[email protected]", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.16", "@opentui/core-darwin-x64": "0.2.16", "@opentui/core-linux-arm64": "0.2.16", "@opentui/core-linux-x64": "0.2.16", "@opentui/core-win32-arm64": "0.2.16", "@opentui/core-win32-x64": "0.2.16" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-4vWN15Zc3nsXJlOiHhhpqkBXD+wrNFKxCPtiTiillZYDRre+XsZogVTOOGUDwaBIC23OSxq7imezLmmtShVBEA=="],
"@opentui/core-darwin-arm64": ["@opentui/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aFb2Yp+oqDu3h6VCWi7xpQ9yjpKSQcROzGGfHgqC6Nd3U+uiLfPJBkmiI87iK0opCggCFj5TkKI004050DmGjg=="],
"@opentui/core-darwin-x64": ["@opentui/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-KimiHE0j7EsTB5P8doW0lr1eH5iZKLPKWQO+tmy1VcdYr/TzqhdHSvGuJXrZvfTFi9/rV57Eq0d7964Ri9O0vQ=="],
"@opentui/core-linux-arm64": ["@opentui/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-4fCwRCfTtUgS/5QcSEkSuBjgQymSOUWXgrXG2ycrf3Swi0QhKDA/pVjwLrUJ6eF+/8mQyQSEV72T8MxMO3M2qg=="],
"@opentui/core-linux-x64": ["@opentui/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-KgQBGjiucw4e7gM+R8qOzHWBFhjCY1IfCrGjW3Wzxv2hKUlL+mPhelaeJwnEqtNxMUdVTYjlwlu3IHxslXMJWQ=="],
"@opentui/core-win32-arm64": ["@opentui/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-C6WqEI3VkXatXraMgSFXZjEXq0pzURGjRpFAJZYmuVDmpqE57o7E80Np2UkdZ6m5kpJDt4mRyu3krc/P825iNQ=="],
"@opentui/core-win32-x64": ["@opentui/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-kCX3CMTns6DMCFDNTDV4sjmBKyA/iEvzaVhl/jYi4JRIVT2zcy1lo+lhXT5mPgYHmJZu8Uye6j3Zi3c7Z2Me5A=="],
"@opentui/solid": ["@opentui/[email protected]", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.16", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-2Q+v1PPpXXr+sALi9Aj6I5Jvo7xDfbmstYjRLL7lW3Hghh9i7ONQKpt/gyDDRbhSsYrhxKYTNenF9OxgoXkTHg=="],
"@alcalzone/ansi-tokenize": ["@alcalzone/[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-p+CMKJ93HFmLkjXKlXiVGlMQEuRb6H0MokBSwUsX+S6BRX8eV5naFZpQJFfJHjRZY0Hmnqy1/r6UWl3x+19zYA=="],
"@sec-ant/readable-stream": ["@sec-ant/[email protected]", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
@@ -133,99 +56,87 @@
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q=="],
"@types/unist": ["@types/[email protected]", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@ungap/structured-clone": ["@ungap/[email protected]", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
"ansi-escapes": ["[email protected]", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
"ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
"babel-plugin-jsx-dom-expressions": ["[email protected]", "", { "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/types": "^7.20.7", "html-entities": "2.3.3", "parse5": "^7.1.2" }, "peerDependencies": { "@babel/core": "^7.20.12" } }, "sha512-/O6JWUmjv03OI9lL2ry9bUjpD5S3PclM55RRJEyCdcFZ5W2SEA/59d+l2hNsk3gI6kiWRdRPdOtqZmsQzFN1pQ=="],
"ansi-styles": ["[email protected]", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"babel-plugin-module-resolver": ["babel-plugin-module-resolver@5.0.2", "", { "dependencies": { "find-babel-config": "^2.1.1", "glob": "^9.3.3", "pkg-up": "^3.1.0", "reselect": "^4.1.7", "resolve": "^1.22.8" } }, "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg=="],
"babel-preset-solid": ["[email protected]", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.6" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.12" }, "optionalPeers": ["solid-js"] }, "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg=="],
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"baseline-browser-mapping": ["[email protected]", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw=="],
"brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="],
"browserslist": ["[email protected]", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
"bun-ffi-structs": ["[email protected]", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-N/ZWtyN0piZlrXQT7TO0V+q952orYqkfhXRXM1Hcbb+R3QSiBH4vLnib187Mrs1H7pWIYECAmPeapGYDOMCl+w=="],
"auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
"caniuse-lite": ["[email protected]", "", {}, "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA=="],
"ccount": ["[email protected]", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
"chalk": ["[email protected]", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
"character-entities-html4": ["[email protected]", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
"character-entities-legacy": ["[email protected]", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
"cli-boxes": ["[email protected]", "", {}, "sha512-5IOn+jcCEHEraYolBPs/sT4BxYCe2nHg374OPiItB1O96KZFseS2gthU4twyYzeDcFew4DaUM/xwc5BQf08JJw=="],
"cli-cursor": ["[email protected]", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="],
"cli-spinners": ["[email protected]", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
"cli-truncate": ["[email protected]", "", { "dependencies": { "slice-ansi": "^9.0.0", "string-width": "^8.2.0" } }, "sha512-3+YKIUFsohD9MIoOFPFBldjAlnfCmCDcqe6aYGFqlDTRKg80p4wg35L+j83QQ63iOlKRccEkbn8IuM++HsgEjA=="],
"code-excerpt": ["[email protected]", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
"comma-separated-tokens": ["[email protected]", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
"commander": ["[email protected]", "", {}, "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg=="],
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
"cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"csstype": ["[email protected]", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"dequal": ["[email protected]", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
"devlop": ["[email protected]", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
"diff": ["[email protected]", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="],
"environment": ["[email protected]", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
"electron-to-chromium": ["[email protected]", "", {}, "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw=="],
"es-toolkit": ["[email protected]", "", {}, "sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw=="],
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
"entities": ["[email protected]", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
"es-errors": ["[email protected]", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"escalade": ["[email protected]", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escape-string-regexp": ["escape-string-regex[email protected].0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
"execa": ["[email protected]", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
"figures": ["[email protected]", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
"find-babel-config": ["[email protected]", "", { "dependencies": { "json5": "^2.2.3" } }, "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg=="],
"find-up": ["[email protected]", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="],
"fs.realpath": ["[email protected]", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
"function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"gensync": ["[email protected]", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
"get-east-asian-width": ["[email protected]", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
"get-stream": ["[email protected]", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
"glob": ["[email protected]", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
"hasown": ["[email protected]", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
"hast-util-to-html": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
"hast-util-whitespace": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
"html-entities": ["[email protected]", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="],
"html-void-elements": ["[email protected]", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
"human-signals": ["[email protected]", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
"is-core-module": ["[email protected]", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="],
"indent-string": ["[email protected]", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
"ink": ["[email protected]", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.3.0", "ansi-escapes": "^7.3.0", "ansi-styles": "^6.2.3", "auto-bind": "^5.0.1", "chalk": "^5.6.2", "cli-boxes": "^4.0.1", "cli-cursor": "^4.0.0", "cli-truncate": "^6.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.45.1", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "scheduler": "^0.27.0", "signal-exit": "^3.0.7", "slice-ansi": "^9.0.0", "stack-utils": "^2.0.6", "string-width": "^8.2.0", "terminal-size": "^4.0.1", "type-fest": "^5.5.0", "widest-line": "^6.0.0", "wrap-ansi": "^10.0.0", "ws": "^8.20.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.2.0", "react": ">=19.2.0", "react-devtools-core": ">=6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-4wsM/gMKOT2ZANNTJibI6I9IcwBfobqv/CgaDcwvOaCREZIQxo3iGQS7qPHa2hmA67NYltZWCMtBDELB/mcbJQ=="],
"ink-spinner": ["[email protected]", "", { "dependencies": { "cli-spinners": "^2.7.0" }, "peerDependencies": { "ink": ">=4.0.0", "react": ">=18.0.0" } }, "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA=="],
"ink-text-input": ["[email protected]", "", { "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" }, "peerDependencies": { "ink": ">=5", "react": ">=18" } }, "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw=="],
"is-fullwidth-code-point": ["[email protected]", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
"is-in-ci": ["[email protected]", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="],
"is-plain-obj": ["[email protected]", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
@@ -235,18 +146,6 @@
"isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"js-tokens": ["[email protected]", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"jsesc": ["[email protected]", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
"json5": ["[email protected]", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
"locate-path": ["[email protected]", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
"lru-cache": ["[email protected]", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
"marked": ["[email protected]", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="],
"mdast-util-to-hast": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
"micromark-util-character": ["[email protected]", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
@@ -259,90 +158,76 @@
"micromark-util-types": ["[email protected]", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
"minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="],
"minipass": ["[email protected]", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"node-releases": ["[email protected]", "", {}, "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ=="],
"mimic-fn": ["[email protected]", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"npm-run-path": ["[email protected]", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
"onetime": ["[email protected]", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"oniguruma-parser": ["[email protected]", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
"oniguruma-to-es": ["[email protected]", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="],
"openai": ["[email protected]", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-z3dO9fEWOXBzlXynVb/xZ/tujzUjFWQWn3C0n0mw6Vo0zJTbEkaN4b2cLWjhJ6haJQx8LlREoafHRl+Gu/Hl+A=="],
"p-limit": ["[email protected]", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
"p-try": ["[email protected]", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
"parse-ms": ["[email protected]", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
"parse5": ["[email protected]", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
"path-exists": ["[email protected]", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
"patch-console": ["[email protected]", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
"path-key": ["[email protected]", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-parse": ["[email protected]", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
"path-scurry": ["[email protected]", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"pkg-up": ["[email protected]", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="],
"pretty-ms": ["[email protected]", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="],
"property-information": ["[email protected]", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
"react": ["[email protected]", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
"react-devtools-core": ["[email protected]", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw=="],
"react-reconciler": ["[email protected]", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="],
"regex": ["[email protected]", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
"regex-recursion": ["[email protected]", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
"regex-utilities": ["[email protected]", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
"reselect": ["[email protected]", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="],
"restore-cursor": ["[email protected]", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
"resolve": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="],
"s-js": ["[email protected]", "", {}, "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ=="],
"semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"seroval": ["[email protected]", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
"seroval-plugins": ["[email protected]", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
"scheduler": ["[email protected].0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"shebang-command": ["[email protected]", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["[email protected]", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"shell-quote": ["[email protected]", "", {}, "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ=="],
"shiki": ["[email protected]", "", { "dependencies": { "@shikijs/core": "4.1.0", "@shikijs/engine-javascript": "4.1.0", "@shikijs/engine-oniguruma": "4.1.0", "@shikijs/langs": "4.1.0", "@shikijs/themes": "4.1.0", "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q=="],
"signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"solid-js": ["soli[email protected]", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw=="],
"slice-ansi": ["sli[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA=="],
"space-separated-tokens": ["[email protected]", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
"string-width": ["[email protected]", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
"stringify-entities": ["[email protected]", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
"strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
"strip-final-newline": ["[email protected]", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
"supports-preserve-symlinks-flag": ["[email protected]", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"tagged-tag": ["[email protected]", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
"terminal-size": ["[email protected]", "", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="],
"trim-lines": ["[email protected]", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
"type-fest": ["[email protected]", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="],
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["[email protected]", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
@@ -359,19 +244,17 @@
"unist-util-visit-parents": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
"update-browserslist-db": ["[email protected]", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
"vfile": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
"vfile-message": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
"web-tree-sitter": ["[email protected]", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="],
"which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"ws": ["[email protected].0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
"widest-line": ["[email protected].0", "", { "dependencies": { "string-width": "^8.1.0" } }, "sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA=="],
"yallist": ["[email protected]", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.3", "string-width": "^8.2.0", "strip-ansi": "^7.1.2" } }, "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ=="],
"ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
"yoctocolors": ["[email protected]", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
@@ -381,16 +264,14 @@
"zwitch": ["[email protected]", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
"ink/signal-exit": ["[email protected]", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"ink-text-input/type-fest": ["[email protected]", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"npm-run-path/path-key": ["[email protected]", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"parse5/entities": ["[email protected]", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
"react-devtools-core/ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA=="],
"path-scurry/lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"path-scurry/minipass": ["[email protected]", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
"string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
"restore-cursor/signal-exit": ["[email protected]", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
}
}
+9 -6
View File
@@ -7,24 +7,27 @@
"zero": "src/index.ts"
},
"scripts": {
"dev": "bun --preload @opentui/solid/preload src/index.ts",
"dev": "bun run src/index.ts",
"test": "bun test ./tests --timeout 15000",
"build": "bun run scripts/build.ts"
"build": "bun build src/index.ts --compile --outfile zero"
},
"devDependencies": {
"@types/bun": "latest"
"@types/bun": "latest",
"@types/react": "^19.2.15"
},
"peerDependencies": {
"typescript": "^5"
},
"dependencies": {
"@opentui/core": "^0.2.16",
"@opentui/solid": "^0.2.16",
"commander": "^15.0.0",
"execa": "^9.6.1",
"ink": "^7.0.4",
"ink-spinner": "^5.0.0",
"ink-text-input": "^6.0.0",
"openai": "^6.39.1",
"react": "^19.2.6",
"react-devtools-core": "^7.0.1",
"shiki": "^4.1.0",
"solid-js": "1.9.12",
"zod": "^4.4.3"
}
}
-16
View File
@@ -1,16 +0,0 @@
#!/usr/bin/env bun
import solidPlugin from '@opentui/solid/bun-plugin';
const result = await Bun.build({
entrypoints: ['./src/index.ts'],
target: 'bun',
plugins: [solidPlugin],
outdir: './dist',
});
if (!result.success) {
for (const log of result.logs) {
console.error(log);
}
process.exit(1);
}
+38 -22
View File
@@ -12,9 +12,9 @@ export interface AgentOptions {
onToolResult?: (result: ToolResult) => void;
onUsage?: (usage: { promptTokens: number; completionTokens: number }) => void;
onPlanUpdate?: (plan: PlanItem[]) => void;
toolsEnabled?: boolean;
debug?: boolean;
planMode?: boolean;
toolsEnabled?: boolean; // allows temporarily disabling tool calling for debugging
debug?: boolean; // when true, logs the exact payload sent to the provider
planMode?: boolean; // when true, the agent plans without modifying the codebase
}
interface PendingToolCall {
@@ -28,16 +28,16 @@ export async function runAgent(
provider: Provider,
options: AgentOptions = {}
): Promise<string> {
const {
maxTurns = 12,
onText,
onToolCall,
const {
maxTurns = 12,
onText,
onToolCall,
onToolResult,
onUsage,
onPlanUpdate,
toolsEnabled = true,
debug = false,
planMode = false,
planMode = false
} = options;
// Clear any previous plan when starting a new task
@@ -57,9 +57,16 @@ export async function runAgent(
for (let turn = 0; turn < maxTurns; turn++) {
const toolDefinitions = (toolsEnabled && tools.length > 0)
? tools.map(t => {
const jsonSchema = z.toJSONSchema(t.parameters, { target: 'draft-7' }) as any;
// Convert Zod schema to proper JSON Schema (critical for many providers).
// zod v4 ships this natively — no external package needed.
const jsonSchema = z.toJSONSchema(t.parameters, {
target: 'draft-7',
}) as any;
// Remove $schema if present (some providers dislike it)
delete jsonSchema.$schema;
// Make it strict by default (good practice)
if (jsonSchema.type === 'object' && !('additionalProperties' in jsonSchema)) {
jsonSchema.additionalProperties = false;
}
@@ -78,21 +85,30 @@ export async function runAgent(
if (debug) {
const red = '\x1b[31m';
const reset = '\x1b[0m';
const border = '-'.repeat(50);
const toolsList = toolDefinitions.map(t => t.name).join(', ');
const preview = String(messages[messages.length - 1]?.content || '').slice(0, 45);
const border = '─'.repeat(50);
console.log(`\n${red}+${border}+`);
console.log(`| SENDING TO PROVIDER${' '.repeat(31)}|`);
console.log(`+${border}+`);
console.log(`| Messages: ${messages.length}${' '.repeat(Math.max(0, 40 - String(messages.length).length))}|`);
console.log(`| Tools enabled: ${toolDefinitions.length > 0}${' '.repeat(33)}|`);
console.log(`| Tool count: ${toolDefinitions.length}${' '.repeat(Math.max(0, 38 - String(toolDefinitions.length).length))}|`);
if (toolsList) {
console.log(`| Tools: ${toolsList.slice(0, 42)}${' '.repeat(Math.max(0, 43 - toolsList.length))}|`);
console.log(`\n${red}┌${border}┐`);
console.log(`│ SENDING TO PROVIDER${' '.repeat(31)}│`);
console.log(`├${border}┤`);
console.log(`│ Messages: ${messages.length}${' '.repeat(40 - String(messages.length).length)}│`);
console.log(`│ Tools enabled: ${toolDefinitions.length > 0}${' '.repeat(33)}│`);
console.log(`│ Tool count: ${toolDefinitions.length}${' '.repeat(38 - String(toolDefinitions.length).length)}│`);
if (toolDefinitions.length > 0) {
const toolsList = toolDefinitions.map(t => t.name).join(', ');
console.log(`│ Tools: ${toolsList.slice(0, 42)}${' '.repeat(Math.max(0, 43 - toolsList.length))}│`);
// Show a sample of the schema for the first tool (very useful for debugging)
const firstTool = toolDefinitions[0];
if (firstTool?.parameters) {
const schemaPreview = JSON.stringify(firstTool.parameters, null, 2).slice(0, 300);
console.log(`│ First tool schema sample:\n${schemaPreview}...`);
}
}
console.log(`| Last message: ${preview}${' '.repeat(Math.max(0, 36 - preview.length))}|`);
console.log(`+${border}+${reset}\n`);
const preview = String(messages[messages.length-1]?.content || '').slice(0, 45);
console.log(`│ Last message: ${preview}${' '.repeat(Math.max(0, 36 - preview.length))}│`);
console.log(`└${border}┘${reset}\n`);
}
// Stream the response
+3 -10
View File
@@ -1,21 +1,14 @@
import { Command } from 'commander';
import { ensureSolidTransformPlugin } from '@opentui/solid/bun-plugin';
import packageJson from '../package.json';
import { runHeadless } from './cli';
import { configManager } from './config/manager';
import { startTUI } from './tui';
const program = new Command();
async function startInteractiveTUI() {
ensureSolidTransformPlugin();
const { startTUI } = await import('./tui');
startTUI();
}
program
.name('zero')
.description('A clean terminal AI coding agent')
.version(packageJson.version);
.version('0.1.0');
program
.option('-p, --prompt <prompt>', 'Run in headless mode with the given prompt')
@@ -24,7 +17,7 @@ program
await runHeadless(options.prompt);
} else {
// Launch the interactive TUI (Grok Build style)
await startInteractiveTUI();
startTUI();
}
});
+8 -9
View File
@@ -106,9 +106,11 @@ export class OpenAIProvider implements Provider {
try {
for await (const chunk of stream) {
// Some OpenAI-compatible servers send errors as special chunks
if ((chunk as any).error) {
const errorData = (chunk as any).error;
throw formatProviderCreateError(errorData);
const errData = (chunk as any).error;
const msg = errData.message || JSON.stringify(errData);
throw new Error(`Provider returned error: ${msg}`);
}
const delta = chunk.choices[0]?.delta;
@@ -128,7 +130,7 @@ export class OpenAIProvider implements Provider {
toolCallAccumulators.set(tc.index, acc);
}
// If we already had data at this index and now get a new id,
// If we already had data at this index and now get a new id,
// the previous tool call is complete.
if (tc.id && acc.id && acc.id !== tc.id) {
if (acc.id) {
@@ -207,6 +209,7 @@ function formatProviderCreateError(error: unknown): Error {
function getDetailedErrorMessage(error: any): string {
if (!error) return 'Unknown error';
// Try common places where real error lives (especially for custom gateways)
if (error.message && !error.message.includes('Provider returned error')) {
return error.message;
}
@@ -214,18 +217,14 @@ function getDetailedErrorMessage(error: any): string {
if (error.error) {
if (typeof error.error === 'string') return error.error;
if (error.error.message) return error.error.message;
try {
return JSON.stringify(error.error);
} catch {}
try { return JSON.stringify(error.error); } catch {}
}
if (error.response?.data) {
const data = error.response.data;
if (data.error?.message) return data.error.message;
if (typeof data.error === 'string') return data.error;
try {
return JSON.stringify(data);
} catch {}
try { return JSON.stringify(data); } catch {}
}
if (error.cause?.message) return error.cause.message;
+277
View File
@@ -0,0 +1,277 @@
import React, { useState } from 'react';
import { Box, Text, useInput } from 'ink';
import TextInput from 'ink-text-input';
import { configManager } from '../config/manager';
type AddMode = 'choose' | 'opengateway' | 'generic';
interface AddProviderProps {
onDone: (providerName?: string) => void;
onCancel: () => void;
}
export const AddProvider: React.FC<AddProviderProps> = ({ onDone, onCancel }) => {
const [mode, setMode] = useState<AddMode>('choose');
// For the choose menu
const [selectedOption, setSelectedOption] = useState(0); // 0 = OpenGateway, 1 = Generic
// For the OpenGateway form (step based)
const [ogwFormStep, setOgwFormStep] = useState(0); // 0 = API Key, 1 = Model, 2 = Done
// OpenGateway fields
const [ogwKey, setOgwKey] = useState('');
const [ogwModel, setOgwModel] = useState('mimo-v2.5-pro');
// Generic fields
const [name, setName] = useState('');
const [baseURL, setBaseURL] = useState('https://api.openai.com/v1');
const [apiKey, setApiKey] = useState('');
const [model, setModel] = useState('gpt-4o');
const [error, setError] = useState('');
const [success, setSuccess] = useState(false);
useInput((input, key) => {
if (key.escape) {
if (mode === 'choose') {
onCancel();
} else {
// Reset states when going back
setMode('choose');
setOgwFormStep(0);
setError('');
setSuccess(false);
setSelectedOption(0);
}
return;
}
// Arrow key + Enter navigation for the choose menu
if (mode === 'choose') {
if (key.upArrow) {
setSelectedOption((prev) => Math.max(0, prev - 1));
return;
}
if (key.downArrow) {
setSelectedOption((prev) => Math.min(1, prev + 1));
return;
}
if (key.return) {
if (selectedOption === 0) {
setMode('opengateway');
setOgwFormStep(0);
} else {
setMode('generic');
}
return;
}
// Still support numbers for convenience
if (input === '1') {
setMode('opengateway');
setOgwFormStep(0);
}
if (input === '2') {
setMode('generic');
}
}
});
const saveOpenGateway = () => {
if (!ogwKey.trim()) {
setError('API key is required');
return;
}
const profileName = 'opengateway';
configManager.addProvider({
name: profileName,
baseURL: 'https://opengateway.gitlawb.com/v1',
apiKey: ogwKey.trim(),
model: ogwModel.trim(),
description: 'OpenGateway',
});
setSuccess(true);
// Auto-close after a short delay so the user sees the success message
setTimeout(() => {
onDone(profileName);
}, 1200);
};
const saveGeneric = () => {
if (!name.trim() || !baseURL.trim() || !model.trim()) {
setError('Name, Base URL, and Model are required');
return;
}
configManager.addProvider({
name: name.trim(),
baseURL: baseURL.trim(),
apiKey: apiKey.trim() || undefined,
model: model.trim(),
description: 'Custom OpenAI-compatible',
});
setSuccess(true);
setTimeout(() => onDone(name.trim()), 1200);
};
if (mode === 'choose') {
return (
<Box flexDirection="column" padding={1}>
<Text bold color="cyan">Add New Provider</Text>
<Text color="gray">Esc to go back • ↑↓ to navigate • Enter to select</Text>
<Box marginY={1} flexDirection="column">
<Text color={selectedOption === 0 ? 'greenBright' : 'white'}>
{selectedOption === 0 ? '› ' : ' '}1. Add OpenGateway (recommended)
</Text>
{selectedOption === 0 && (
<Text color="gray" dimColor>
{' '}You'll be asked for your ogw_live_... API key
</Text>
)}
<Box marginTop={1}>
<Text color={selectedOption === 1 ? 'greenBright' : 'white'}>
{selectedOption === 1 ? '› ' : ' '}2. Add custom OpenAI-compatible provider
</Text>
</Box>
{selectedOption === 1 && (
<Text color="gray" dimColor>
{' '}For Groq, OpenAI, Ollama, etc.
</Text>
)}
</Box>
</Box>
);
}
if (mode === 'opengateway') {
if (success) {
return (
<Box flexDirection="column" padding={1}>
<Text color="greenBright" bold>
✓ OpenGateway provider added successfully!
</Text>
<Text color="gray" dimColor>
It is now your active provider.
</Text>
</Box>
);
}
return (
<Box flexDirection="column" padding={1}>
<Text bold color="cyan">Add OpenGateway Provider</Text>
<Text color="gray">Esc to go back</Text>
{ogwFormStep === 0 && (
<Box marginTop={1} flexDirection="column">
<Text color="yellowBright">Step 1/2 — Enter your OpenGateway API key</Text>
<Text color="gray" dimColor>
You can get one at https://opengateway.gitlawb.com
</Text>
<Box marginTop={1}>
<Text>API Key: </Text>
<TextInput
value={ogwKey}
onChange={setOgwKey}
mask="*"
placeholder="ogw_live_..."
/>
</Box>
<Box marginTop={1}>
<Text color="gray" dimColor>Press Enter to continue</Text>
</Box>
<TextInput
value=""
onChange={() => {}}
onSubmit={() => {
if (ogwKey.trim()) {
setOgwFormStep(1);
setError('');
} else {
setError('API key cannot be empty');
}
}}
/>
</Box>
)}
{ogwFormStep === 1 && (
<Box marginTop={1} flexDirection="column">
<Text color="yellowBright">Step 2/2 — Model name</Text>
<Box marginTop={1}>
<Text>Model: </Text>
<TextInput value={ogwModel} onChange={setOgwModel} />
</Box>
{error && <Text color="red">⚠ {error}</Text>}
<Box marginTop={1}>
<Text color="gray" dimColor>Press Enter to save</Text>
</Box>
<TextInput
value=""
onChange={() => {}}
onSubmit={saveOpenGateway}
/>
</Box>
)}
</Box>
);
}
if (mode === 'generic') {
if (success) {
return (
<Box flexDirection="column" padding={1}>
<Text color="greenBright" bold>
✓ Provider added successfully!
</Text>
</Box>
);
}
return (
<Box flexDirection="column" padding={1}>
<Text bold color="cyan">Add Custom Provider</Text>
<Text color="gray">Esc to go back</Text>
<Box marginTop={1}>
<Text>Name: </Text>
<TextInput value={name} onChange={setName} />
</Box>
<Box>
<Text>Base URL: </Text>
<TextInput value={baseURL} onChange={setBaseURL} />
</Box>
<Box>
<Text>API Key: </Text>
<TextInput value={apiKey} onChange={setApiKey} mask="*" />
</Box>
<Box>
<Text>Model: </Text>
<TextInput value={model} onChange={setModel} />
</Box>
{error && <Text color="red">{error}</Text>}
<Box marginTop={1}>
<Text color="gray">Press Enter to save</Text>
</Box>
<TextInput
value=""
onChange={() => {}}
onSubmit={saveGeneric}
/>
</Box>
);
}
return null;
};
+584 -1337
View File
File diff suppressed because it is too large. Load diff
+39
View File
@@ -0,0 +1,39 @@
import React from 'react';
import { Box, Text } from 'ink';
type Usage = {
promptTokens: number;
completionTokens: number;
};
interface HeaderProps {
provider: string;
model: string;
usage: Usage;
totalTokens: number;
planMode: boolean;
}
export const Header: React.FC<HeaderProps> = ({ provider, model, usage, totalTokens, planMode }) => (
<Box
borderStyle="single"
borderColor={planMode ? 'green' : 'gray'}
paddingX={1}
flexDirection="row"
justifyContent="space-between"
>
<Box flexDirection="row">
<Text color="cyanBright" bold>ZERO</Text>
<Text color="gray" dimColor> local agent</Text>
{planMode && <Text color="green"> - PLAN</Text>}
</Box>
<Box flexDirection="row">
<Text color="cyan">{provider}</Text>
<Text color="gray"> / </Text>
<Text color="magenta">{model}</Text>
<Text color="gray" dimColor>
{' '}p:{usage.promptTokens} c:{usage.completionTokens} t:{totalTokens}
</Text>
</Box>
</Box>
);
+33
View File
@@ -0,0 +1,33 @@
import React from 'react';
import { Box, Text } from 'ink';
export const Logo: React.FC = () => {
return (
<Box flexDirection="column" marginBottom={1}>
<Text color="cyanBright" bold>
{` ███████╗ ███████╗ ██████╗ ██████╗ `}
</Text>
<Text color="cyanBright" bold>
{` ╚══███╔╝ ██╔════╝ ██╔══██╗ ██╔═══██╗`}
</Text>
<Text color="cyanBright" bold>
{` ███╔╝ █████╗ ██████╔╝ ██║ ██║`}
</Text>
<Text color="cyanBright" bold>
{` ███╔╝ ██╔══╝ ██╔══██╗ ██║ ██║`}
</Text>
<Text color="cyanBright" bold>
{` ███████╗ ███████╗ ██║ ██║ ╚██████╔╝`}
</Text>
<Text color="cyanBright" bold>
{` ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ `}
</Text>
<Box marginTop={1}>
<Text color="gray" dimColor>
A clean terminal AI coding agent
</Text>
</Box>
</Box>
);
};
+230
View File
@@ -0,0 +1,230 @@
import React, { useEffect, useState } from 'react';
import { Box, Text } from 'ink';
import { highlightCode } from './highlighter';
// Formatter for model responses: paragraphs, inline code, and highlighted code blocks
interface MessageRendererProps {
content: string;
}
export const MessageRenderer: React.FC<MessageRendererProps> = ({ content }) => {
const [highlightedBlocks, setHighlightedBlocks] = useState<Map<number, string>>(new Map());
const codeBlockRegex = /```(\w+)?\n?([\s\S]*?)```/g;
const parts: React.ReactNode[] = [];
let lastIndex = 0;
let match;
let blockIndex = 0;
const matches: Array<{ index: number; lang: string; code: string; fullMatch: string }> = [];
while ((match = codeBlockRegex.exec(content)) !== null) {
matches.push({
index: match.index,
lang: match[1] || 'text',
code: (match[2] ?? '').trim(),
fullMatch: match[0],
});
}
// Highlight code blocks asynchronously
useEffect(() => {
let cancelled = false;
const highlightAll = async () => {
const newHighlights = new Map<number, string>();
for (let i = 0; i < matches.length; i++) {
const m = matches[i];
if (!m) continue;
try {
const ansi = await highlightCode(m.code, m.lang);
newHighlights.set(i, ansi);
} catch {
newHighlights.set(i, m.code);
}
}
if (!cancelled) {
setHighlightedBlocks(newHighlights);
}
};
if (matches.length > 0) {
highlightAll();
} else {
setHighlightedBlocks(new Map());
}
return () => {
cancelled = true;
};
}, [content]);
for (let i = 0; i < matches.length; i++) {
const m = matches[i];
if (!m) continue;
// Add text before this code block (with paragraph formatting)
if (m.index > lastIndex) {
const textBefore = content.slice(lastIndex, m.index);
parts.push(renderTextBlock(textBefore, `text-${parts.length}`));
}
const highlighted = highlightedBlocks.get(i);
parts.push(
<Box
key={`code-${i}`}
flexDirection="column"
borderStyle="single"
borderColor="gray"
marginTop={1}
marginBottom={1}
paddingX={1}
>
{m.lang && m.lang !== 'text' && (
<Text color="cyan" dimColor>{m.lang}</Text>
)}
{highlighted ? (
<Text>{highlighted}</Text>
) : (
<Text color="gray" dimColor>Highlighting...</Text>
)}
</Box>
);
lastIndex = m.index + m.fullMatch.length;
blockIndex++;
}
// Add remaining text after last code block (with paragraph formatting)
if (lastIndex < content.length) {
const remaining = content.slice(lastIndex);
parts.push(renderTextBlock(remaining, `text-${parts.length}`));
}
return <>{parts.length > 0 ? parts : renderTextBlock(content, 'full')}</>;
};
// Stronger formatter for model responses (paragraphs + basic lists + inline formatting)
function renderTextBlock(text: string, keyPrefix: string): React.ReactNode {
const lines = text.split('\n');
// Group into logical blocks: paragraphs vs lists
const blocks: Array<{ type: 'paragraph' | 'list'; lines: string[] }> = [];
let current: { type: 'paragraph' | 'list'; lines: string[] } | null = null;
for (const line of lines) {
const trimmed = line.trim();
const isListItem = /^\s*(\d+\.|[-*+])\s+/.test(line);
if (isListItem) {
if (!current || current.type !== 'list') {
if (current) blocks.push(current);
current = { type: 'list', lines: [] };
}
current.lines.push(line);
} else {
if (!current || current.type !== 'paragraph') {
if (current) blocks.push(current);
current = { type: 'paragraph', lines: [] };
}
current.lines.push(line);
}
}
if (current) blocks.push(current);
return (
<Box key={keyPrefix} flexDirection="column">
{blocks.map((block, bIndex) => {
const isLastBlock = bIndex === blocks.length - 1;
if (block.type === 'list') {
return (
<Box
key={`${keyPrefix}-list-${bIndex}`}
flexDirection="column"
marginBottom={isLastBlock ? 0 : 1}
>
{block.lines.map((line, lIndex) => (
<Text key={`${keyPrefix}-l-${lIndex}`}>
{formatInline(line)}
</Text>
))}
</Box>
);
}
// Paragraph block — join lines and split on double newlines for sub-paragraphs
const paragraphText = block.lines.join('\n').trim();
const subParagraphs = paragraphText
.split(/\n\s*\n/)
.map(p => p.trim())
.filter(Boolean);
return (
<Box
key={`${keyPrefix}-p-${bIndex}`}
flexDirection="column"
marginBottom={isLastBlock ? 0 : 1}
>
{subParagraphs.map((p, pIndex) => (
<Box key={`${keyPrefix}-sp-${pIndex}`} marginBottom={pIndex < subParagraphs.length - 1 ? 1 : 0}>
<Text>{formatInline(p)}</Text>
</Box>
))}
</Box>
);
})}
</Box>
);
}
// Handles inline formatting: **bold**, `inline code`
function formatInline(text: string): React.ReactNode {
const elements: React.ReactNode[] = [];
let remaining = text;
let idx = 0;
// Simple sequential parser for **bold** and `code`
while (remaining.length > 0) {
// Try bold first
const boldMatch = remaining.match(/^\*\*([^*]+)\*\*/);
if (boldMatch) {
elements.push(
<Text key={`bold-${idx++}`} bold color="white">
{boldMatch[1]}
</Text>
);
remaining = remaining.slice(boldMatch[0].length);
continue;
}
// Try inline code
const codeMatch = remaining.match(/^`([^`]+)`/);
if (codeMatch) {
elements.push(
<Text key={`code-${idx++}`} color="cyan" backgroundColor="#2d2d2d">
{codeMatch[1]}
</Text>
);
remaining = remaining.slice(codeMatch[0].length);
continue;
}
// Take plain text until next special char
const nextSpecial = remaining.search(/(\*\*|`)/);
const plain = nextSpecial === -1 ? remaining : remaining.slice(0, nextSpecial);
if (plain) {
elements.push(<Text key={`plain-${idx++}`}>{plain}</Text>);
remaining = remaining.slice(plain.length);
} else {
// Fallback single char
elements.push(<Text key={`plain-${idx++}`}>{remaining[0]}</Text>);
remaining = remaining.slice(1);
}
}
return <>{elements}</>;
}
+115
View File
@@ -0,0 +1,115 @@
import React, { useState } from 'react';
import { Box, Text, useInput } from 'ink';
import { configManager } from '../config/manager';
import type { ProviderProfile } from '../config/types';
interface ProviderPickerProps {
onSelect: (name: string) => void;
onCancel: () => void;
onAddNew: () => void;
}
export const ProviderPicker: React.FC<ProviderPickerProps> = ({ onSelect, onCancel, onAddNew }) => {
const providers = configManager.listProviders();
const activeProvider = configManager.getActiveProvider()?.name;
const totalItems = providers.length + 1; // +1 for "Add new"
const [selectedIndex, setSelectedIndex] = useState(0);
useInput((input, key) => {
if (key.escape || (key.ctrl && input === 'c')) {
onCancel();
return;
}
if (key.upArrow) {
setSelectedIndex((prev) => Math.max(0, prev - 1));
return;
}
if (key.downArrow) {
setSelectedIndex((prev) => Math.min(totalItems - 1, prev + 1));
return;
}
if (key.return) {
if (selectedIndex < providers.length) {
const selected = providers[selectedIndex];
if (selected) onSelect(selected.name);
} else {
// Last item = Add new
onAddNew();
}
return;
}
// Quick select by number
const num = parseInt(input, 10);
if (!isNaN(num) && num >= 1 && num <= totalItems) {
if (num <= providers.length) {
const selected = providers[num - 1];
if (selected) onSelect(selected.name);
} else {
onAddNew();
}
}
});
return (
<Box flexDirection="column" padding={1}>
<Text bold color="cyan">
Select Provider
</Text>
<Text color="gray" dimColor>
↑↓ to navigate • Enter to select • Esc to cancel
</Text>
<Box marginY={1} flexDirection="column">
{providers.map((provider, index) => {
const isSelected = index === selectedIndex;
const isActive = provider.name === activeProvider;
return (
<Box key={provider.name} paddingLeft={1}>
<Text color={isSelected ? 'green' : 'white'}>
{isSelected ? '› ' : ' '}
{provider.name}
{isActive && <Text color="blue"> (current)</Text>}
</Text>
</Box>
);
})}
{/* Add new option */}
<Box paddingLeft={1}>
<Text color={selectedIndex === providers.length ? 'green' : 'cyan'}>
{selectedIndex === providers.length ? '› ' : ' '}
+ Add new provider...
</Text>
</Box>
</Box>
{providers[selectedIndex] && (
<Box flexDirection="column" marginLeft={2} borderStyle="round" paddingX={1}>
<Text>
<Text bold>Model:</Text> {providers[selectedIndex].model}
</Text>
<Text>
<Text bold>Base URL:</Text> {providers[selectedIndex].baseURL}
</Text>
{providers[selectedIndex].description && (
<Text>
<Text bold>Description:</Text> {providers[selectedIndex].description}
</Text>
)}
</Box>
)}
<Box marginTop={1}>
<Text color="gray" dimColor>
Press 1-{totalItems} for quick selection
</Text>
</Box>
</Box>
);
};
+15
View File
@@ -0,0 +1,15 @@
import React from 'react';
import { Text } from 'ink';
import Spinner from 'ink-spinner';
interface ThinkingSpinnerProps {
label?: string;
}
export const ThinkingSpinner: React.FC<ThinkingSpinnerProps> = ({ label = 'thinking' }) => {
return (
<Text color="gray" dimColor>
<Spinner type="dots" /> {label}...
</Text>
);
};
+58
View File
@@ -0,0 +1,58 @@
import React from 'react';
import { Box, Text } from 'ink';
import type { PlanItem } from '../tools/plan';
export const TodoRail: React.FC<{ plan: PlanItem[] }> = ({ plan }) => (
<Box
width={34}
flexShrink={0}
flexDirection="column"
borderStyle="single"
borderColor="gray"
paddingX={1}
>
<Text color="green" bold>todo</Text>
{plan.length === 0 ? (
<Text color="gray" dimColor>No active plan yet.</Text>
) : (
plan.map((item, index) => (
<Box key={item.id || index} flexDirection="column" marginTop={1}>
<Text color={planStatusColor(item.status)}>
{index + 1}. {planStatusMark(item.status)} {item.content}
</Text>
{item.notes && (
<Text color="gray" dimColor>
{' '}{item.notes}
</Text>
)}
</Box>
))
)}
</Box>
);
export function planStatusMark(status: PlanItem['status']): string {
switch (status) {
case 'completed':
return 'x';
case 'in_progress':
return '>';
case 'failed':
return '!';
default:
return 'o';
}
}
export function planStatusColor(status: PlanItem['status']): 'green' | 'yellow' | 'red' | 'gray' {
switch (status) {
case 'completed':
return 'green';
case 'in_progress':
return 'yellow';
case 'failed':
return 'red';
default:
return 'gray';
}
}
+202
View File
@@ -0,0 +1,202 @@
import React, { useEffect, useState } from 'react';
import { Box, Text } from 'ink';
import { highlightCode } from './highlighter';
interface ToolCallRendererProps {
name: string;
args: string;
result?: string;
status?: 'running' | 'success' | 'error';
}
export const ToolCallRenderer: React.FC<ToolCallRendererProps> = ({
name,
args,
result,
status = 'success',
}) => {
const [isExpanded, setIsExpanded] = useState(false);
const [highlightedArgs, setHighlightedArgs] = useState<string | null>(null);
const [highlightedResult, setHighlightedResult] = useState<string | null>(null);
const hasResult = !!result;
const isLongResult = hasResult && (result!.length > 400 || result!.split('\n').length > 12);
const [showFullResult, setShowFullResult] = useState(false);
// Generate a nice one-line summary for collapsed state
const summary = getToolSummary(name, args);
// Highlight arguments (only when expanded)
useEffect(() => {
if (!isExpanded) return;
const highlight = async () => {
try {
let jsonArgs = args;
try {
const parsed = JSON.parse(args);
jsonArgs = JSON.stringify(parsed, null, 2);
} catch {
// not valid JSON
}
const ansi = await highlightCode(jsonArgs, 'json');
setHighlightedArgs(ansi);
} catch {
setHighlightedArgs(args);
}
};
highlight();
}, [args, isExpanded]);
// Highlight result (only when expanded)
useEffect(() => {
if (!isExpanded || !result) return;
const highlight = async () => {
try {
const looksLikeCode =
result.includes('function') ||
result.includes('const ') ||
result.includes('import ') ||
result.includes('=>') ||
result.includes('class ');
const ansi = await highlightCode(result, looksLikeCode ? 'typescript' : 'text');
setHighlightedResult(ansi);
} catch {
setHighlightedResult(result);
}
};
highlight();
}, [result, isExpanded]);
const borderColor =
status === 'running' ? 'yellow' : status === 'error' ? 'red' : 'green';
const statusIcon =
status === 'running' ? '⟳' : status === 'error' ? '✕' : '✓';
const statusColor =
status === 'running' ? 'yellow' : status === 'error' ? 'red' : 'green';
// === COLLAPSED VIEW — clean formatter for model actions ===
if (!isExpanded) {
const showToggle = args || hasResult;
return (
<Box flexDirection="row" paddingX={1} paddingY={0}>
<Text color={statusColor} bold>
{statusIcon} {name}
</Text>
<Text color="gray" dimColor> {summary}</Text>
{showToggle && (
<Text
color="cyan"
dimColor
{...({ onPress: () => setIsExpanded(true) } as any)}
>
{' '}[show]
</Text>
)}
</Box>
);
}
// === EXPANDED VIEW — formatted details ===
return (
<Box
flexDirection="column"
borderStyle="single"
borderColor={borderColor}
paddingX={0}
paddingY={0}
>
{/* Header */}
<Box paddingX={1} flexDirection="row" justifyContent="space-between">
<Text color={statusColor} bold>
{statusIcon} {name}
</Text>
<Text
color="cyan"
dimColor
{...({ onPress: () => setIsExpanded(false) } as any)}
>
[hide]
</Text>
</Box>
{/* Arguments */}
<Box paddingX={1} paddingTop={0} flexDirection="column">
<Text color="gray" dimColor bold>args</Text>
{highlightedArgs ? (
<Text>{highlightedArgs}</Text>
) : (
<Text color="gray" dimColor>…</Text>
)}
</Box>
{/* Result */}
{hasResult && (
<Box paddingX={1} paddingTop={0} flexDirection="column">
<Text color="gray" dimColor bold>result</Text>
{highlightedResult || result ? (
<Text color="green" dimColor>
{isLongResult && !showFullResult
? (highlightedResult || result!).slice(0, 200) + '...'
: (highlightedResult || result)}
</Text>
) : null}
{isLongResult && (
<Text
color="cyan"
dimColor
{...({ onPress: () => setShowFullResult(!showFullResult) } as any)}
>
{showFullResult ? ' [less]' : ' [more]'}
</Text>
)}
</Box>
)}
</Box>
);
};
// Smart one-line summary for collapsed tool calls.
// We want: "tool name + the command it actually ran" (e.g. "read src/App.tsx", "ls -la .")
function getToolSummary(name: string, args: string): string {
try {
const parsed = JSON.parse(args);
if (name === 'bash') {
const cmd = parsed.command || '';
return cmd.length > 65 ? cmd.slice(0, 62) + '...' : cmd;
}
if (name === 'read_file') {
const path = parsed.path || '';
const short = path.length > 60 ? path.slice(0, 57) + '...' : path;
return `read ${short}`;
}
if (name === 'edit_file') {
const path = parsed.path || '';
const short = path.length > 60 ? path.slice(0, 57) + '...' : path;
return `edit ${short}`;
}
// Fallback for future tools
const keys = Object.keys(parsed);
if (keys.length > 0) {
const firstKey = keys[0]!;
const val = String((parsed as any)[firstKey] ?? '');
const shortVal = val.length > 50 ? val.slice(0, 47) + '...' : val;
return `${firstKey}: ${shortVal}`;
}
return args.length > 65 ? args.slice(0, 62) + '...' : args;
} catch {
return args.length > 65 ? args.slice(0, 62) + '...' : args;
}
}
+69 -54
View File
@@ -1,71 +1,86 @@
import { createHighlighter } from 'shiki';
import { createHighlighter } from 'shiki'
export type HighlightedToken = {
content: string;
color?: string;
};
let highlighterPromise: ReturnType<typeof createHighlighter> | null = null
const highlightCache = new Map<string, Promise<string>>()
export type HighlightedLine = HighlightedToken[];
let highlighterPromise: ReturnType<typeof createHighlighter> | undefined;
const highlightCache = new Map<string, Promise<HighlightedLine[]>>();
function hashCode(input: string) {
let hash = 0;
for (let i = 0; i < input.length; i++) {
hash = ((hash << 5) - hash + input.charCodeAt(i)) | 0;
}
return hash.toString(36);
}
export async function highlightCode(code: string, lang = 'text'): Promise<HighlightedLine[]> {
const cacheKey = `${lang}:${code.length}:${hashCode(code)}`;
const cached = highlightCache.get(cacheKey);
if (cached) return cached;
const promise = tokenizeCode(code, lang);
highlightCache.set(cacheKey, promise);
return promise;
}
async function tokenizeCode(code: string, lang: string): Promise<HighlightedLine[]> {
try {
highlighterPromise ??= createHighlighter({
themes: ['github-dark'],
export async function getHighlighter() {
if (!highlighterPromise) {
highlighterPromise = createHighlighter({
themes: ['github-dark', 'github-light'],
langs: [
'bash',
'css',
'dockerfile',
'go',
'html',
'typescript',
'javascript',
'json',
'tsx',
'jsx',
'markdown',
'json',
'python',
'rust',
'go',
'bash',
'shell',
'markdown',
'css',
'html',
'sql',
'toml',
'tsx',
'typescript',
'yaml',
'toml',
'dockerfile',
],
});
})
}
return highlighterPromise
}
const highlighter = await highlighterPromise;
const result = highlighter.codeToTokens(code, {
export async function highlightCode(code: string, lang: string = 'text'): Promise<string> {
const cacheKey = `${lang}:${code.length}:${hashString(code)}`
const cached = highlightCache.get(cacheKey)
if (cached) return cached
const highlighted = highlightCodeUncached(code, lang)
highlightCache.set(cacheKey, highlighted)
return highlighted
}
async function highlightCodeUncached(code: string, lang: string): Promise<string> {
try {
const highlighter = await getHighlighter()
const lines = highlighter.codeToTokensBase(code, {
lang: (lang || 'text') as any,
theme: 'github-dark',
});
})
return result.tokens.map((line) =>
line.map((token) => ({
content: token.content,
color: token.color,
})),
);
} catch {
return code.split(/\r?\n/).map((line) => [{ content: line }]);
return lines
.map((line) =>
line
.map((token) => token.color ? `${hexToAnsi(token.color)}${token.content}\x1b[39m` : token.content)
.join(''),
)
.join('\n')
} catch (error) {
// Fallback to plain text if highlighting fails
return code
}
}
function hashString(value: string): string {
let hash = 5381
for (let i = 0; i < value.length; i++) {
hash = ((hash << 5) + hash) ^ value.charCodeAt(i)
}
return (hash >>> 0).toString(36)
}
function hexToAnsi(hex: string): string {
const normalized = hex.replace('#', '')
const value = Number.parseInt(normalized, 16)
if (Number.isNaN(value)) {
return ''
}
const red = (value >> 16) & 255
const green = (value >> 8) & 255
const blue = value & 255
return `\x1b[38;2;${red};${green};${blue}m`
}
+9 -31
View File
@@ -1,35 +1,13 @@
/** @jsxImportSource @opentui/solid */
import { createCliRenderer } from '@opentui/core';
import { render } from '@opentui/solid';
import React from 'react';
import { render } from 'ink';
import { App } from './App';
import { getHighlighter } from './highlighter';
// Preload Shiki highlighter early for fast syntax highlighting
getHighlighter().catch(() => {
// Silently fail - we'll fallback gracefully
});
export function startTUI() {
void start();
}
async function start() {
const renderer = await createCliRenderer({
externalOutputMode: 'passthrough',
targetFps: 60,
gatherStats: false,
exitOnCtrlC: false,
useMouse: true,
enableMouseMovement: true,
useKittyKeyboard: {},
openConsoleOnError: false,
});
const exit = () => {
if (!renderer.isDestroyed) {
renderer.setTerminalTitle('');
renderer.destroy();
}
};
process.once('SIGINT', exit);
renderer.once('destroy', () => {
process.off('SIGINT', exit);
});
await render(() => <App onExit={exit} />, renderer);
render(<App />);
}
+69
View File
@@ -85,4 +85,73 @@ describe('runAgent tool-call flow', () => {
expect(answer).toBe('just an answer');
expect(provider.received).toHaveLength(1);
});
it('reports usage and plan updates through callbacks', async () => {
const provider = new MockProvider([
[
{ type: 'usage', promptTokens: 10, completionTokens: 2 },
{ type: 'tool-call-start', id: 'call_1', name: 'update_plan' },
{
type: 'tool-call-delta',
id: 'call_1',
argumentsFragment: JSON.stringify({
plan: [{ id: '1', content: 'track this', status: 'in_progress' }],
}),
},
{ type: 'tool-call-end', id: 'call_1' },
],
[{ type: 'text', content: 'done' }],
]);
const usageEvents: Array<{ promptTokens: number; completionTokens: number }> = [];
const planSnapshots: string[][] = [];
await runAgent('make a plan', provider, {
onUsage: (usage) => usageEvents.push(usage),
onPlanUpdate: (plan) => planSnapshots.push(plan.map((item) => item.content)),
});
expect(usageEvents).toEqual([{ promptTokens: 10, completionTokens: 2 }]);
expect(planSnapshots).toEqual([[], ['track this']]);
});
it('does not leak plan update callbacks across separate runs', async () => {
const firstProvider = new MockProvider([
[
{ type: 'tool-call-start', id: 'call_1', name: 'update_plan' },
{
type: 'tool-call-delta',
id: 'call_1',
argumentsFragment: JSON.stringify({
plan: [{ id: '1', content: 'first plan', status: 'pending' }],
}),
},
{ type: 'tool-call-end', id: 'call_1' },
],
[{ type: 'text', content: 'first done' }],
]);
const secondProvider = new MockProvider([
[
{ type: 'tool-call-start', id: 'call_2', name: 'update_plan' },
{
type: 'tool-call-delta',
id: 'call_2',
argumentsFragment: JSON.stringify({
plan: [{ id: '2', content: 'second plan', status: 'pending' }],
}),
},
{ type: 'tool-call-end', id: 'call_2' },
],
[{ type: 'text', content: 'second done' }],
]);
const planSnapshots: string[][] = [];
await runAgent('first', firstProvider, {
onPlanUpdate: (plan) => planSnapshots.push(plan.map((item) => item.content)),
});
await runAgent('second', secondProvider, {});
expect(planSnapshots).toEqual([[], ['first plan']]);
});
});
+1 -2
View File
@@ -5,8 +5,7 @@
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "preserve",
"jsxImportSource": "@opentui/solid",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode