diff --git a/Dockerfile b/Dockerfile index 0c1dc2af..0626bb34 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,15 +54,6 @@ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ # Browsers (Playwright Chromium, Camoufox) are pre-installed in niu-browser-base -# Pre-cache better-sqlite3 prebuilts for Node 22 -# This populates ~/.npm/_prebuilds so agent npm installs use cached binaries -RUN mkdir -p /tmp/prebuild-cache \ - && cd /tmp/prebuild-cache \ - && npm init -y \ - && npm install better-sqlite3@12.5.0 \ - && cd / \ - && rm -rf /tmp/prebuild-cache - # Install production dependencies only COPY package*.json ./ RUN npm ci --omit=dev --ignore-scripts diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..5a2261cf --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,6 @@ +export default { + extends: ['@commitlint/config-conventional'], + rules: { + 'header-max-length': [2, 'always', 100], + }, +}; diff --git a/lefthook.yml b/lefthook.yml index e2df3949..36ef090f 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -12,3 +12,8 @@ pre-push: commands: test: run: npm test + +commit-msg: + commands: + commitlint: + run: npx commitlint --edit {1} diff --git a/package-lock.json b/package-lock.json index caeac11d..6fd8d6b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,16 +14,18 @@ "@octokit/rest": "^22.0.1", "@types/archiver": "^7.0.0", "archiver": "^7.0.1", - "dhalsim": "2.1.0", + "dhalsim": "^2.2.0", "eta": "^4.5.0", "hono": "^4.6.14", - "llmist": "^12.0.2", + "llmist": "^12.1.0", "trello.js": "^1.2.8", "zangief": "latest", "zod": "^3.24.1" }, "devDependencies": { "@biomejs/biome": "^1.9.4", + "@commitlint/cli": "^20.1.0", + "@commitlint/config-conventional": "^20.0.0", "@types/node": "^22.10.2", "@vitest/coverage-v8": "^2.1.8", "lefthook": "^1.10.10", @@ -69,6 +71,21 @@ } } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -360,6 +377,327 @@ "node": ">=0.1.90" } }, + "node_modules/@commitlint/cli": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.3.0.tgz", + "integrity": "sha512-HXO8YVfqdBK+MnlX2zqNrv6waGYPs6Ysjm5W2Y0GMagWXwiIKx7C8dcIX9ca+QdHq4WA0lcMnZLQ0pzQh1piZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^20.2.0", + "@commitlint/lint": "^20.3.0", + "@commitlint/load": "^20.3.0", + "@commitlint/read": "^20.2.0", + "@commitlint/types": "^20.2.0", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/cli/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@commitlint/cli/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@commitlint/cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@commitlint/cli/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.3.0.tgz", + "integrity": "sha512-g1OXVl6E2v0xF1Ru2RpxQ+Vfy7XUcUsCmLKzGUrhFLS4hSNykje0QSy6djBtzOiOBQCepBrmIlqx/gRlzrSh5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.2.0", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.2.0.tgz", + "integrity": "sha512-SQCBGsL9MFk8utWNSthdxd9iOD1pIVZSHxGBwYIGfd67RTjxqzFOSAYeQVXOu3IxRC3YrTOH37ThnTLjUlyF2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.2.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.2.0.tgz", + "integrity": "sha512-+8TgIGv89rOWyt3eC6lcR1H7hqChAKkpawytlq9P1i/HYugFRVqgoKJ8dhd89fMnlrQTLjA5E97/4sF09QwdoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.2.0", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", + "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.2.0.tgz", + "integrity": "sha512-PhNoLNhxpfIBlW/i90uZ3yG3hwSSYx7n4d9Yc+2FAorAHS0D9btYRK4ZZXX+Gm3W5tDtu911ow/eWRfcRVgNWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.2.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.2.0.tgz", + "integrity": "sha512-Lz0OGeZCo/QHUDLx5LmZc0EocwanneYJUM8z0bfWexArk62HKMLfLIodwXuKTO5y0s6ddXaTexrYHs7v96EOmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.2.0", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.3.0.tgz", + "integrity": "sha512-X19HOGU5nRo6i9DIY0kG0mhgtvpn1UGO1D6aLX1ILLyeqSM5yJyMcrRqNj8SLgeSeUDODhLY9QYsBIG0LdNHkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^20.2.0", + "@commitlint/parse": "^20.2.0", + "@commitlint/rules": "^20.3.0", + "@commitlint/types": "^20.2.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.3.0.tgz", + "integrity": "sha512-amkdVZTXp5R65bsRXRSCwoNXbJHR2aAIY/RGFkoyd63t8UEwqEgT3f0MgeLqYw4hwXyq+TYXKdaW133E29pnGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.2.0", + "@commitlint/execute-rule": "^20.0.0", + "@commitlint/resolve-extends": "^20.2.0", + "@commitlint/types": "^20.2.0", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/message": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.0.0.tgz", + "integrity": "sha512-gLX4YmKnZqSwkmSB9OckQUrI5VyXEYiv3J5JKZRxIp8jOQsWjZgHSG/OgEfMQBK9ibdclEdAyIPYggwXoFGXjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.2.0.tgz", + "integrity": "sha512-LXStagGU1ivh07X7sM+hnEr4BvzFYn1iBJ6DRg2QsIN8lBfSzyvkUcVCDwok9Ia4PWiEgei5HQjju6xfJ1YaSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.2.0", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.2.0.tgz", + "integrity": "sha512-+SjF9mxm5JCbe+8grOpXCXMMRzAnE0WWijhhtasdrpJoAFJYd5UgRTj/oCq5W3HJTwbvTOsijEJ0SUGImECD7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^20.0.0", + "@commitlint/types": "^20.2.0", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.2.0.tgz", + "integrity": "sha512-KVoLDi9BEuqeq+G0wRABn4azLRiCC22/YHR2aCquwx6bzCHAIN8hMt3Nuf1VFxq/c8ai6s8qBxE8+ZD4HeFTlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.2.0", + "@commitlint/types": "^20.2.0", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.3.0.tgz", + "integrity": "sha512-TGgXN/qBEhbzVD13crE1l7YSMJRrbPbUL0OBZALbUM5ER36RZmiZRu2ud2W/AA7HO9YLBRbyx6YVi2t/2Be0yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^20.2.0", + "@commitlint/message": "^20.0.0", + "@commitlint/to-lines": "^20.0.0", + "@commitlint/types": "^20.2.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", + "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.0.0.tgz", + "integrity": "sha512-drXaPSP2EcopukrUXvUXmsQMu3Ey/FuJDc/5oiW4heoCfoE5BdLQyuc7veGeE3aoQaTVqZnh4D5WTWe2vefYKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.2.0.tgz", + "integrity": "sha512-KTy0OqRDLR5y/zZMnizyx09z/rPlPC/zKhYgH8o/q6PuAjoQAKlRfY4zzv0M64yybQ//6//4H1n14pxaLZfUnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", @@ -1516,6 +1854,16 @@ "@types/readdir-glob": "*" } }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz", + "integrity": "sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1528,6 +1876,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1742,6 +2091,23 @@ "node": ">= 14" } }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-escapes": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", @@ -1980,6 +2346,13 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -2469,6 +2842,30 @@ "node": ">=18" } }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/compare-func/node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/compress-commons": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", @@ -2525,6 +2922,51 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/core-js-pure": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.47.0.tgz", @@ -2542,6 +2984,52 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", + "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "^2.6.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -2621,6 +3109,19 @@ "node": ">= 8" } }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -2723,15 +3224,18 @@ } }, "node_modules/dhalsim": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/dhalsim/-/dhalsim-2.1.0.tgz", - "integrity": "sha512-8BBPGpRiUloUrixOa4bqEDiNGb1/i2EChpTmGpRmaxsAxmVnd9m3wuRhT2khThgSg6KruwSCwtRdfsPYUrikJw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dhalsim/-/dhalsim-2.2.0.tgz", + "integrity": "sha512-Kx30DYR9Jo8xb9ZdEIoqPM5opwEFa9RemZ7fpziwfu7SpJqrPiEHyA+ew4Sqpe+Gc72pMyfh+nQd1mi6aEAwDg==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "camoufox-js": "github:zbigniewsobiecki/camoufox-js#83585a4", + "camoufox-js": "github:zbigniewsobiecki/camoufox-js#18e09ec", "playwright-core": "^1.57.0" }, + "engines": { + "node": ">=22.0.0" + }, "peerDependencies": { "llmist": ">=12.0.0" } @@ -2817,6 +3321,16 @@ "once": "^1.4.0" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -2829,6 +3343,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -3035,6 +3559,23 @@ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -3065,6 +3606,24 @@ "license": "MIT", "optional": true }, + "node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fingerprint-generator": { "version": "2.1.78", "resolved": "https://registry.npmjs.org/fingerprint-generator/-/fingerprint-generator-2.1.78.tgz", @@ -3272,6 +3831,24 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -3296,6 +3873,32 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/google-auth-library": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", @@ -3624,6 +4227,44 @@ "node": ">= 10" } }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3637,6 +4278,13 @@ "license": "ISC", "optional": true }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3699,6 +4347,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -3789,6 +4450,13 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-toml": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-toml/-/js-toml-1.0.2.tgz", @@ -3820,6 +4488,13 @@ "bignumber.js": "^9.0.0" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-to-ts": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", @@ -3833,6 +4508,40 @@ "node": ">=16" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -4077,10 +4786,17 @@ "win32" ] }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/llmist": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/llmist/-/llmist-12.0.4.tgz", - "integrity": "sha512-PtEV81Ppv9+t4N0D4dNCpRV0TNcrT5diHe2D5Lxa31lICYE4dGarEv+5cuNgXCbf7GnTkslIpjKc3MoeUZI8tg==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/llmist/-/llmist-12.1.0.tgz", + "integrity": "sha512-abrg4ZaG1uxZbSb24z/iUoF1HhBAcR7uZH34Km9+Ni0Jy0aPvtD1QWRA7lJR3mvvMxXgWEbGkuWcAQFBfcfh4w==", "license": "MIT", "peer": true, "dependencies": { @@ -4099,6 +4815,9 @@ "tiktoken": "^1.0.22", "tslog": "^4.10.2", "zod": "^4.1.12" + }, + "engines": { + "node": ">=22.0.0" } }, "node_modules/llmist/node_modules/zod": { @@ -4110,6 +4829,22 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -4122,6 +4857,13 @@ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "license": "MIT" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -4129,6 +4871,62 @@ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", "license": "MIT" }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true, + "license": "MIT" + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -4240,6 +5038,19 @@ "npm": ">=6" } }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4293,8 +5104,8 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "devOptional": true, "license": "MIT", - "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4515,6 +5326,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz", @@ -4536,6 +5379,38 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -4557,6 +5432,16 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "license": "MIT" }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4786,6 +5671,26 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -5058,6 +5963,16 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -5317,6 +6232,19 @@ "b4a": "^1.6.4" } }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -5338,6 +6266,13 @@ "node": ">=0.8" } }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, "node_modules/tiktoken": { "version": "1.0.22", "resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.22.tgz", @@ -5471,6 +6406,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5545,6 +6481,19 @@ "node": ">=4" } }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universal-user-agent": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", @@ -6348,6 +7297,19 @@ "node": ">=10" } }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zangief": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/zangief/-/zangief-1.0.0.tgz", diff --git a/package.json b/package.json index db6be9af..24204ba4 100644 --- a/package.json +++ b/package.json @@ -32,16 +32,18 @@ "@octokit/rest": "^22.0.1", "@types/archiver": "^7.0.0", "archiver": "^7.0.1", - "dhalsim": "2.1.0", + "dhalsim": "^2.2.0", "eta": "^4.5.0", "hono": "^4.6.14", - "llmist": "^12.0.2", + "llmist": "^12.1.0", "trello.js": "^1.2.8", "zangief": "latest", "zod": "^3.24.1" }, "devDependencies": { "@biomejs/biome": "^1.9.4", + "@commitlint/cli": "^20.1.0", + "@commitlint/config-conventional": "^20.0.0", "@types/node": "^22.10.2", "@vitest/coverage-v8": "^2.1.8", "lefthook": "^1.10.10", diff --git a/src/agents/base.ts b/src/agents/base.ts index 866e9322..ef3c04b2 100644 --- a/src/agents/base.ts +++ b/src/agents/base.ts @@ -11,6 +11,7 @@ import { ListTrelloCards, PostTrelloComment, ReadTrelloCard, + UpdateChecklistItem, UpdateTrelloCard, formatCardData, } from '../gadgets/trello/index.js'; @@ -110,8 +111,10 @@ async function buildAgentContext( log: ReturnType, ): Promise { // Build prompt context for template rendering + const cardUrl = `https://trello.com/c/${cardId}`; const promptContext: PromptContext = { cardId, + cardUrl, projectId: project.id, storiesListId: project.trello?.lists?.stories, processedLabelId: project.trello?.labels?.processed, @@ -205,6 +208,7 @@ function createAgentBuilderWithGadgets( new ListTrelloCards(), new GetMyRecentActivity(), new AddChecklistToCard(), + new UpdateChecklistItem(), ); } @@ -370,6 +374,13 @@ export async function executeAgent( log.info('Agent completed', { cardId, iterations: result.iterationCount, cost: result.cost }); + // Extract PR URL from output (gh pr create outputs the URL) + const prUrlMatch = result.output.match(/https:\/\/github\.com\/[^\s]+\/pull\/\d+/); + const prUrl = prUrlMatch ? prUrlMatch[0] : undefined; + if (prUrl) { + log.info('PR URL extracted', { prUrl }); + } + // Get zipped log buffer before returning fileLogger.close(); const logBuffer = await fileLogger.getZippedBuffer(); @@ -377,6 +388,7 @@ export async function executeAgent( return { success: true, output: result.output, + prUrl, logBuffer, cost: result.cost, }; diff --git a/src/agents/prompts/index.ts b/src/agents/prompts/index.ts index 6b62f0af..3b3b8337 100644 --- a/src/agents/prompts/index.ts +++ b/src/agents/prompts/index.ts @@ -13,6 +13,7 @@ const eta = new Eta({ views: templatesDir, autoEscape: false }); export interface PromptContext { // Common cardId?: string; + cardUrl?: string; projectId?: string; // Briefing-specific diff --git a/src/agents/prompts/templates/briefing.eta b/src/agents/prompts/templates/briefing.eta index 6c46dd5b..08d5caa5 100644 --- a/src/agents/prompts/templates/briefing.eta +++ b/src/agents/prompts/templates/briefing.eta @@ -88,9 +88,13 @@ You are running in a cloned copy of the project repository. Before creating stor - Include TLDR, acceptance criteria, and technical notes in description (use emoji formatting) - **IMPORTANT:** Save the returned URL for each card (e.g., `https://trello.com/c/abc123`) 5. **Add interactive checklists** using `AddChecklistToCard`: - - For EACH card you create, call `AddChecklistToCard` with the card ID - - Use "✅ Acceptance Criteria" as the checklist name - - Add each acceptance criterion as a checklist item + - For EACH card you create, call `AddChecklistToCard` for acceptance criteria: + - Use "✅ Acceptance Criteria" as the checklist name + - Add each acceptance criterion as a checklist item + - For cards that depend on other stories, also call `AddChecklistToCard` for dependencies: + - Use "🔗 Dependencies" as the checklist name + - Add each dependency as a checklist item (use card title or URL) + - Skip this checklist for foundational stories with no dependencies 6. **Post summary comment** using `PostTrelloComment`: - Post a comment on the ORIGINAL card listing all created stories - Use markdown links: `[Story Title](URL)` for each card @@ -129,7 +133,9 @@ Use this template with **emoji section headers** and **bold key terms** for read - [Things explicitly NOT included in this story] ``` -**IMPORTANT:** After creating each card, ALWAYS call `AddChecklistToCard` to create an interactive checklist with the acceptance criteria items. +**IMPORTANT:** After creating each card, ALWAYS call `AddChecklistToCard` to create interactive checklists: +1. "✅ Acceptance Criteria" checklist with acceptance criteria items (always) +2. "🔗 Dependencies" checklist with cards that must be completed first (if any) ## Summary Comment Format @@ -203,7 +209,8 @@ If the user asks you to update stories you previously created: - ALWAYS use `ReadTrelloCard` first - ALWAYS explore the codebase before creating stories - ALWAYS create stories using `CreateTrelloCard` - don't just output text -- ALWAYS call `AddChecklistToCard` after creating each card to add interactive checklists +- ALWAYS call `AddChecklistToCard` after creating each card to add "✅ Acceptance Criteria" checklist +- ALWAYS add "🔗 Dependencies" checklist to stories that depend on other stories - ALWAYS use emoji section headers (🎯, ✅, 🔧, 🚫) and **bold key terms** in descriptions - ALWAYS include a 🎯 TLDR section at the top of every card description - ALWAYS post a summary comment with markdown links to all created cards diff --git a/src/agents/prompts/templates/implementation.eta b/src/agents/prompts/templates/implementation.eta index b4a1f4f4..15648aab 100644 --- a/src/agents/prompts/templates/implementation.eta +++ b/src/agents/prompts/templates/implementation.eta @@ -3,18 +3,21 @@ You are an expert software engineer implementing features based on a detailed pl ## Your Task 1. **Read the Trello card** using ReadTrelloCard to get the implementation plan -2. **Read the codebase guidelines** (CLAUDE.md, README.md, etc.) to understand conventions -3. **Create a feature branch** via Tmux: `git checkout -b feature/branch-name` -4. **Implement the feature** following TDD: +2. **Verify the plan paths**: Check that file paths from the Trello card actually exist. If a path doesn't exist, explore to find the correct location before proceeding. +3. **Read the codebase guidelines** (CLAUDE.md, README.md, etc.) to understand conventions +4. **Create a feature branch** via Tmux: `git checkout -b feature/branch-name` +5. **Implement the feature** following TDD: - **Explore first**: read existing tests to understand patterns - Write tests matching the patterns you found - Run tests to verify they fail appropriately - Implement the feature - Run tests to verify they pass -5. **Run linting and type checking** -6. **Create commits** via Tmux: `git add . && git commit -m "feat: description"` -7. **Push the branch** via Tmux: `git push -u origin HEAD` -8. **Create a PR** via Tmux: `gh pr create --title "..." --body "..."` +6. **Run linting and type checking** +7. **Create commits** via Tmux: `git add . && git commit -m "feat: description"` +8. **Push the branch** via Tmux: `git push -u origin HEAD` +9. **Create a PR** via Tmux: `gh pr create --title "..." --body "..."` +10. **Mark acceptance criteria complete** using UpdateChecklistItem for each criterion you've implemented +11. **Post summary comment** on the Trello card describing what was implemented and linking to the PR ## Mandatory Exploration Before Implementation @@ -27,6 +30,12 @@ You are an expert software engineer implementing features based on a detailed pl - For features: read similar features in the codebase - For utilities: check existing utils for patterns +**CRITICAL for test files:** +- FIRST list the test directory to see existing test file patterns +- READ at least one existing test file in that directory BEFORE writing any test code +- If the Trello card suggests a test path that doesn't exist, find where similar tests actually live +- Match the scope of your tests to what the card asks for - don't write comprehensive test suites when a few targeted tests are needed + ### Step 2: Extract Patterns From the files you read, note: - **Import patterns** - exact paths and what's imported @@ -69,12 +78,11 @@ Use standard git commands and GitHub CLI (`gh`) via Tmux for all version control - `git push -u origin HEAD` - push branch to remote **GitHub CLI (`gh`) commands:** -- `gh pr create --fill` - create PR auto-filling title/body from commits (simplest) -- `gh pr create --title "Title" --body "Description" --base dev` - create with explicit values +- `gh pr create --title "Title" --body "Description"` - create PR with explicit values (required) - `gh pr view` - view current PR - `gh pr list` - list open PRs -**IMPORTANT:** Always use `--fill` or provide `--title` and `--body` to avoid interactive prompts. +**IMPORTANT:** Always provide `--title` and `--body` to avoid interactive prompts. Do NOT use `--fill`. ## Commit Message Format @@ -90,29 +98,77 @@ Example: `feat: add user authentication endpoint` ## PR Description Format -```markdown -## Summary -[Brief description of what this PR does] +When creating your PR, you MUST use this format: + +```bash +gh pr create --title "type: brief description" --body "## Summary +Brief description of what this PR implements. ## Changes -- [List of changes] +- Main change 1 +- Main change 2 ## Testing -- [How to test] +- How to test these changes ## Related -- Trello card: [URL] +- Trello card: <%= it.cardUrl %> +" +``` + +**CRITICAL PR Requirements:** +- The PR description MUST include the Trello card link: `<%= it.cardUrl %>` +- The description should be comprehensive enough to understand the PR without reading all the code +- Always use explicit `--title` and `--body` flags, never `--fill` + +## Completion Process + +After creating the PR, complete these steps: + +### 1. Mark Acceptance Criteria Complete + +Use `UpdateChecklistItem` to mark each completed acceptance criterion: +1. Read the card again to get checklist item IDs +2. For each criterion you've implemented, call `UpdateChecklistItem` with state="complete" + +### 2. Post Summary Comment + +Use `PostTrelloComment` to post a summary on the Trello card: + +```markdown +## ✅ Implementation Complete + +**PR:** [PR Title](PR_URL) + +### What was implemented +- [Key feature/change 1] +- [Key feature/change 2] + +### Key decisions +- [Decision 1 and why] +- [Decision 2 and why] + +### Testing +- All tests passing +- Lint and type checks passing + +### Notes +- [Any issues encountered or things to be aware of] ``` ## Rules - ALWAYS read the Trello card first to understand the task +- ALWAYS follow the implementation plan from the Trello card - don't expand scope beyond what's specified - ALWAYS follow existing code patterns and conventions - ALWAYS write tests before implementation (TDD) - ALWAYS run tests and lint before committing +- ALWAYS verify file paths from the plan exist before using them - BEFORE writing any new file, READ 2-3 similar existing files - NEVER guess import paths - copy from working examples - NEVER push directly to main/master - create PRs +- NEVER write comprehensive test suites when the card asks for targeted tests - match scope to the plan - Use meaningful commit messages following conventional commits - Keep commits small and focused -- Post the PR URL to Trello using PostTrelloComment when complete +- ALWAYS mark acceptance criteria complete using UpdateChecklistItem after PR is created +- ALWAYS post a summary comment on the Trello card with PR link and what was implemented diff --git a/src/agents/prompts/templates/planning.eta b/src/agents/prompts/templates/planning.eta index 7b9df2f8..644dc5b6 100644 --- a/src/agents/prompts/templates/planning.eta +++ b/src/agents/prompts/templates/planning.eta @@ -137,3 +137,12 @@ Review the updated description and move to TODO when ready to implement! - Be specific about file paths and function names - Break down into small, testable increments - Include testing as part of each step + +## Path Verification + +**CRITICAL: Only include file paths you have VERIFIED exist.** + +- Before referencing any file path in your plan, verify it exists using `ListDirectory` or `ReadFile` +- For test files: FIRST list the test directory to discover existing test file patterns and naming conventions +- NEVER fabricate or guess file paths - if a path doesn't exist, find where similar files actually live +- If truly unsure about test location, omit the specific path and say "add tests following existing patterns in the test directory" diff --git a/src/config/schema.ts b/src/config/schema.ts index c9ed682a..6ff7e6ee 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -39,11 +39,11 @@ export const CascadeConfigSchema = z.object({ model: z.string().default('gemini:gemini-2.5-flash'), agentModels: z.record(z.string()).default({}), maxIterations: z.number().int().positive().default(50), - selfDestructTimeoutMs: z + freshMachineTimeoutMs: z .number() .int() .positive() - .default(30 * 60 * 1000), + .default(5 * 60 * 1000), // 5 min - exit if no work received after boot watchdogTimeoutMs: z .number() .int() diff --git a/src/gadgets/tmux.ts b/src/gadgets/tmux.ts index 55e683a3..545d3a17 100644 --- a/src/gadgets/tmux.ts +++ b/src/gadgets/tmux.ts @@ -116,8 +116,8 @@ class TmuxGadget extends Gadget({ .int() .min(1) .max(1000) - .default(100) - .describe('Number of lines to capture (default: 100, max: 1000)'), + .default(25) + .describe('Number of lines to capture (default: 25, max: 1000)'), }), // List all sessions @@ -151,8 +151,8 @@ class TmuxGadget extends Gadget({ comment: 'Long-running E2E tests still running after 120s - use capture to monitor', }, { - params: { action: 'capture', session: 'npm-install', lines: 50 }, - output: 'session=npm-install lines=50\n\nadded 874 packages in 45s', + params: { action: 'capture', session: 'npm-install', lines: 25 }, + output: 'session=npm-install lines=25\n\nadded 874 packages in 45s', comment: 'Check output from running session', }, { @@ -295,7 +295,7 @@ class TmuxGadget extends Gadget({ } private async handleCapture(params: { session: string; lines?: number }): Promise { - const lines = params.lines ?? 100; + const lines = params.lines ?? 25; // Verify session exists const checkResult = await runTmux(['has-session', '-t', params.session]); diff --git a/src/gadgets/trello/UpdateChecklistItem.ts b/src/gadgets/trello/UpdateChecklistItem.ts new file mode 100644 index 00000000..0358c008 --- /dev/null +++ b/src/gadgets/trello/UpdateChecklistItem.ts @@ -0,0 +1,44 @@ +import { Gadget, z } from 'llmist'; +import { trelloClient } from '../../trello/client.js'; +import { formatGadgetError } from '../utils.js'; + +export class UpdateChecklistItem extends Gadget({ + name: 'UpdateChecklistItem', + description: + 'Update a checklist item state on a Trello card. Use this to mark acceptance criteria as complete or incomplete.', + timeoutMs: 15000, + schema: z.object({ + cardId: z.string().describe('The Trello card ID'), + checkItemId: z.string().describe('The checklist item ID to update'), + state: z.enum(['complete', 'incomplete']).describe('The new state for the checklist item'), + }), + examples: [ + { + params: { + cardId: 'abc123', + checkItemId: 'item456', + state: 'complete', + }, + comment: 'Mark an acceptance criterion as complete', + }, + { + params: { + cardId: 'abc123', + checkItemId: 'item789', + state: 'incomplete', + }, + comment: 'Mark an acceptance criterion as incomplete', + }, + ], +}) { + override async execute(params: this['params']): Promise { + try { + await trelloClient.updateChecklistItem(params.cardId, params.checkItemId, params.state); + + const action = params.state === 'complete' ? 'marked complete' : 'marked incomplete'; + return `Checklist item ${params.checkItemId} ${action} on card ${params.cardId}`; + } catch (error) { + return formatGadgetError('updating checklist item', error); + } + } +} diff --git a/src/gadgets/trello/index.ts b/src/gadgets/trello/index.ts index 5111261c..8aa2877f 100644 --- a/src/gadgets/trello/index.ts +++ b/src/gadgets/trello/index.ts @@ -5,3 +5,4 @@ export { CreateTrelloCard } from './CreateTrelloCard.js'; export { ListTrelloCards } from './ListTrelloCards.js'; export { GetMyRecentActivity } from './GetMyRecentActivity.js'; export { AddChecklistToCard } from './AddChecklistToCard.js'; +export { UpdateChecklistItem } from './UpdateChecklistItem.js'; diff --git a/src/index.ts b/src/index.ts index d9267f56..8aee1465 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ import { } from './triggers/index.js'; import { processTrelloWebhook } from './triggers/trello/webhook-handler.js'; import type { CascadeConfig } from './types/index.js'; -import { logger, setLogLevel, startSelfDestructTimer } from './utils/index.js'; +import { logger, setLogLevel, startFreshMachineTimer } from './utils/index.js'; async function main(): Promise { // Load environment config @@ -29,7 +29,7 @@ async function main(): Promise { model: 'gemini:gemini-2.5-flash', agentModels: {}, maxIterations: 50, - selfDestructTimeoutMs: 30 * 60 * 1000, + freshMachineTimeoutMs: 5 * 60 * 1000, watchdogTimeoutMs: 30 * 60 * 1000, postJobGracePeriodMs: 5000, }, @@ -52,9 +52,10 @@ async function main(): Promise { }, }); - // Start self-destruct timer (for Fly.io cost management) + // Start fresh machine timer (for Fly.io cost management) + // Exits if no work received within timeout if (process.env.FLY_APP_NAME) { - startSelfDestructTimer(config.defaults.selfDestructTimeoutMs); + startFreshMachineTimer(config.defaults.freshMachineTimeoutMs); } // Start server diff --git a/src/trello/client.ts b/src/trello/client.ts index 27e7f1a9..ec725b6f 100644 --- a/src/trello/client.ts +++ b/src/trello/client.ts @@ -341,6 +341,19 @@ export const trelloClient = { })); }, + async updateChecklistItem( + cardId: string, + checkItemId: string, + state: 'complete' | 'incomplete', + ): Promise { + logger.debug('Updating checklist item', { cardId, checkItemId, state }); + await getClient().cards.updateCardCheckItem({ + id: cardId, + idCheckItem: checkItemId, + state, + }); + }, + async getCardCustomFieldItems(cardId: string): Promise { logger.debug('Fetching card custom field items', { cardId }); const apiKey = process.env.TRELLO_API_KEY; diff --git a/src/triggers/github/webhook-handler.ts b/src/triggers/github/webhook-handler.ts index dcdcdc8f..a7813d54 100644 --- a/src/triggers/github/webhook-handler.ts +++ b/src/triggers/github/webhook-handler.ts @@ -3,6 +3,7 @@ import { findProjectByRepo } from '../../config/projects.js'; import { trelloClient } from '../../trello/client.js'; import type { CascadeConfig, ProjectConfig, TriggerContext } from '../../types/index.js'; import { + cancelFreshMachineTimer, dequeueWebhook, enqueueWebhook, getQueueLength, @@ -137,6 +138,7 @@ export async function processGitHubWebhook( // Only run agent if agentType is specified // Some triggers (like PRReadyToMergeTrigger) perform actions directly without needing an agent if (result.agentType) { + cancelFreshMachineTimer(); setProcessing(true); if (process.env.FLY_APP_NAME) { diff --git a/src/triggers/trello/webhook-handler.ts b/src/triggers/trello/webhook-handler.ts index 1c169f64..d19917ed 100644 --- a/src/triggers/trello/webhook-handler.ts +++ b/src/triggers/trello/webhook-handler.ts @@ -8,6 +8,7 @@ import type { TriggerContext, } from '../../types/index.js'; import { + cancelFreshMachineTimer, dequeueWebhook, enqueueWebhook, getQueueLength, @@ -95,6 +96,11 @@ async function executeAgent( if (cardId) { await safeAddLabel(cardId, project.trello.labels.processing); await safeRemoveLabel(cardId, project.trello.labels.readyToProcess); + + // Move to IN PROGRESS when implementation starts + if (result.agentType === 'implementation') { + await safeMoveCard(cardId, project.trello.lists.inProgress); + } } const agentResult = await runAgent(result.agentType, { @@ -215,6 +221,7 @@ export async function processTrelloWebhook( logger.info('Trigger matched', { agentType: result.agentType, cardId: result.cardId }); + cancelFreshMachineTimer(); setProcessing(true); // Start watchdog - force kill if job takes too long (Fly.io only) diff --git a/src/utils/index.ts b/src/utils/index.ts index ac1f015b..c9100b84 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,10 +1,9 @@ export { logger, setLogLevel, getLogLevel } from './logging.js'; export { - startSelfDestructTimer, - resetSelfDestructTimer, + startFreshMachineTimer, + cancelFreshMachineTimer, setProcessing, isCurrentlyProcessing, - cancelSelfDestruct, startWatchdog, clearWatchdog, setWatchdogCleanup, diff --git a/src/utils/lifecycle.ts b/src/utils/lifecycle.ts index 74a77eb1..d0005902 100644 --- a/src/utils/lifecycle.ts +++ b/src/utils/lifecycle.ts @@ -1,58 +1,41 @@ import { logger } from './logging.js'; -let selfDestructTimer: ReturnType | null = null; +let freshMachineTimer: ReturnType | null = null; +let shutdownTimer: ReturnType | null = null; let watchdogTimer: ReturnType | null = null; let isProcessing = false; -let shutdownRequested = false; let watchdogCleanup: (() => Promise) | null = null; -export function startSelfDestructTimer(timeoutMs: number): void { - resetSelfDestructTimer(timeoutMs); -} - -export function resetSelfDestructTimer(timeoutMs: number): void { - if (selfDestructTimer) { - clearTimeout(selfDestructTimer); +// Fresh machine timer - exits if no work received after boot +export function startFreshMachineTimer(timeoutMs: number): void { + if (freshMachineTimer) { + clearTimeout(freshMachineTimer); } - logger.debug('Self-destruct timer reset', { timeoutMs }); + logger.info('Fresh machine timer started', { timeoutMs }); - selfDestructTimer = setTimeout(() => { - if (isProcessing) { - logger.info('Self-destruct timer expired during processing, will shutdown after completion'); - shutdownRequested = true; - } else { - logger.info('Self-destruct timer expired, shutting down'); - process.exit(0); - } + freshMachineTimer = setTimeout(() => { + logger.info('No work received, shutting down fresh machine'); + process.exit(0); }, timeoutMs); } -export function setProcessing(processing: boolean, timeoutMs?: number): void { - isProcessing = processing; - - if (!processing && shutdownRequested) { - logger.info('Processing complete and shutdown requested, shutting down'); - process.exit(0); +export function cancelFreshMachineTimer(): void { + if (freshMachineTimer) { + clearTimeout(freshMachineTimer); + freshMachineTimer = null; + logger.debug('Fresh machine timer cancelled (work received)'); } +} - if (!processing && timeoutMs) { - resetSelfDestructTimer(timeoutMs); - } +export function setProcessing(processing: boolean): void { + isProcessing = processing; } export function isCurrentlyProcessing(): boolean { return isProcessing; } -export function cancelSelfDestruct(): void { - if (selfDestructTimer) { - clearTimeout(selfDestructTimer); - selfDestructTimer = null; - } - shutdownRequested = false; -} - // Watchdog cleanup callback - called before force exit export function setWatchdogCleanup(cleanup: () => Promise): void { watchdogCleanup = cleanup; @@ -100,13 +83,13 @@ export function clearWatchdog(): void { export function scheduleShutdownAfterJob(gracePeriodMs = 5000): void { clearWatchdog(); - if (selfDestructTimer) { - clearTimeout(selfDestructTimer); + if (shutdownTimer) { + clearTimeout(shutdownTimer); } logger.info('Job complete, scheduling shutdown', { gracePeriodMs }); - selfDestructTimer = setTimeout(() => { + shutdownTimer = setTimeout(() => { logger.info('Grace period complete, shutting down'); process.exit(0); }, gracePeriodMs);