diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..0d5341c --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +*.tsbuildinfo +.DS_Store +Thumbs.db +test-playground/ diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 0000000..cb70681 --- /dev/null +++ b/cli/README.md @@ -0,0 +1,103 @@ +# @leadsolutions/lead-protocol + +CLI tooling for the [Lead Protocol](https://github.com/mmilanez/lead-protocol) — a multi-agent coordination framework. + +## Quick Start + +```bash +npx @leadsolutions/lead-protocol init +``` + +This copies `.agents/` into your project and generates `CLAUDE.md` and `AGENTS.md` with the boot procedure. Then edit `.agents/PROJECT_RULES.md` to set your project identity — same as the [manual setup](https://github.com/mmilanez/lead-protocol#quick-start), minus the copy-paste. + +## Installation + +No installation required — use `npx`: + +```bash +npx @leadsolutions/lead-protocol +``` + +Or install globally: + +```bash +npm install -g @leadsolutions/lead-protocol +lead-protocol +``` + +## Commands + +### `init` + +Initialize Lead Protocol in the current directory. + +```bash +lead-protocol init # Asks for confirmation +lead-protocol init --yes # Skip confirmation +``` + +What it does: +- Copies `.agents/` with all protocol files (rules, schemas, scripts, modules) +- Creates `CLAUDE.md` and `AGENTS.md` with `` tagged boot procedures +- Creates `.gitignore` with the protocol entries if none exists, or appends any missing ones if it already exists + +If Lead Protocol is already installed, you'll be asked before overwriting. Existing content in `CLAUDE.md` / `AGENTS.md` outside the `` tags is always preserved. + +### `handoff` + +Show the current handoff state for an (actor, agent) pair. + +```bash +lead-protocol handoff # Auto-detect or select pair +lead-protocol handoff --pair user@pc/claude # Specific pair +lead-protocol handoff --raw # Raw markdown +lead-protocol handoff --json # JSON output +``` + +### `validate` + +Validate protocol state files against their JSON schemas. + +```bash +lead-protocol validate # Auto-discover all +lead-protocol validate .agents/decisions.jsonl # Specific file (decisions.jsonl) +lead-protocol validate path/to/handoff.md # Specific file (handoff.md) +``` + +Recognized files are matched by name: `decisions.jsonl` and `handoff.md`. Auto-discover checks `.agents/decisions.jsonl` plus every pair's `handoff.md`. + +Exit codes: `0` = passed, `1` = validation errors, `2` = config errors. + +### `status` + +One-screen summary of the current protocol state. + +```bash +lead-protocol status # Formatted output +lead-protocol status --json # JSON output +``` + +## How `` Tags Work + +The CLI manages `CLAUDE.md` and `AGENTS.md` using XML-style tags: + +```markdown + +# CLAUDE.md — Pointer for Claude Code +...boot procedure... + +``` + +- **New file** → creates with the tagged block +- **Existing file, no tags** → appends the tagged block (your content is preserved) +- **Existing file, has tags** → replaces content between tags (idempotent) + +## Requirements + +- Node.js >= 18.0.0 +- No dependency on git, Python, or any server + +## License + +Apache-2.0 + diff --git a/cli/package-lock.json b/cli/package-lock.json new file mode 100644 index 0000000..4d3468c --- /dev/null +++ b/cli/package-lock.json @@ -0,0 +1,2078 @@ +{ + "name": "@leadsolutions/lead-protocol", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@leadsolutions/lead-protocol", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@inquirer/prompts": "^7.0.0", + "ajv": "^8.17.0", + "ajv-formats": "^3.0.0", + "chalk": "^5.4.0", + "commander": "^13.0.0" + }, + "bin": { + "lead-protocol": "dist/index.js" + }, + "devDependencies": { + "tsup": "^8.0.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "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/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "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/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "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/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000..9ef038b --- /dev/null +++ b/cli/package.json @@ -0,0 +1,48 @@ +{ + "name": "@leadsolutions/lead-protocol", + "version": "2.0.3", + "description": "CLI tooling for the Lead Protocol — multi-agent coordination framework", + "type": "module", + "bin": { + "lead-protocol": "./dist/index.js" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "typecheck": "tsc --noEmit", + "test:pack": "node scripts/test-pack.mjs", + "prepublishOnly": "npm run build" + }, + "keywords": [ + "lead-protocol", + "ai-agents", + "multi-agent", + "coordination", + "handoff", + "cli" + ], + "author": "Lead Protocol", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/mmilanez/lead-protocol.git", + "directory": "cli" + }, + "engines": { + "node": ">=18.0.0" + }, + "dependencies": { + "@inquirer/prompts": "^7.0.0", + "ajv": "^8.17.0", + "ajv-formats": "^3.0.0", + "chalk": "^5.4.0", + "commander": "^13.0.0" + }, + "devDependencies": { + "tsup": "^8.0.0", + "typescript": "^5.7.0" + } +} diff --git a/cli/scripts/sync-templates.mjs b/cli/scripts/sync-templates.mjs new file mode 100644 index 0000000..b7a0e04 --- /dev/null +++ b/cli/scripts/sync-templates.mjs @@ -0,0 +1,51 @@ +// Bundles the Lead Protocol templates into dist/templates/ at build time. +// +// The template source of truth lives at the repo root (the parent of this +// package): .agents/, AGENTS.md and CLAUDE.md. npm cannot publish files that +// sit outside the package directory, so this script mirrors them into +// dist/templates/ (the only folder we ship). Runtime code reads from there +// via getTemplatesDir() in src/lib/project.ts. +// +// Runs from tsup's onSuccess hook, after tsup has cleaned and rebuilt dist/. + +import { cpSync, rmSync, mkdirSync, existsSync, copyFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const pkgRoot = path.resolve(scriptDir, ".."); +const repoRoot = path.resolve(scriptDir, "..", ".."); +const dest = path.resolve(pkgRoot, "dist", "templates"); + +const agentsSrc = path.join(repoRoot, ".agents"); +const guidelineFiles = ["AGENTS.md", "CLAUDE.md"]; + +function fail(message) { + console.error(`[sync-templates] ${message}`); + process.exit(1); +} + +if (!existsSync(agentsSrc)) { + fail(`source not found: ${agentsSrc} (expected the template .agents/ at the repo root)`); +} +for (const file of guidelineFiles) { + if (!existsSync(path.join(repoRoot, file))) { + fail(`source not found: ${path.join(repoRoot, file)} (expected the template guideline at the repo root)`); + } +} + +// Start from a clean mirror so stale files never linger between builds. +rmSync(dest, { recursive: true, force: true }); +mkdirSync(dest, { recursive: true }); + +// Copy .agents/, skipping any per-user state (local/) that must never ship in a template. +cpSync(agentsSrc, path.join(dest, ".agents"), { + recursive: true, + filter: (src) => path.relative(agentsSrc, src).split(path.sep)[0] !== "local", +}); + +for (const file of guidelineFiles) { + copyFileSync(path.join(repoRoot, file), path.join(dest, file)); +} + +console.log(`[sync-templates] bundled templates into ${path.relative(pkgRoot, dest)}`); diff --git a/cli/scripts/test-pack.mjs b/cli/scripts/test-pack.mjs new file mode 100644 index 0000000..ff6aa6a --- /dev/null +++ b/cli/scripts/test-pack.mjs @@ -0,0 +1,105 @@ +// Faithful "production install" smoke test for @leadsolutions/lead-protocol. +// +// `npm link` reflects your on-disk folder: it ignores the `files` allowlist and +// the dependency split, so it can pass while a real install fails. This script +// exercises the real publish path instead: it builds, packs the exact tarball +// npm would publish, installs that tarball into a throwaway consumer project +// (so `files` and the real `dependencies` are exercised), then runs +// init / validate / status against it. Everything is removed at the end. +// +// One command: `npm run test:pack`. +// +// Note: installing the tarball downloads `dependencies` from the registry, so +// this needs network access (just like a real `npm install` / `npx`). + +import { execSync } from "node:child_process"; +import { + mkdtempSync, + mkdirSync, + rmSync, + writeFileSync, + readFileSync, + existsSync, +} from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const pkgRoot = path.resolve(scriptDir, ".."); +const q = (p) => `"${p}"`; + +function run(label, cmd, opts = {}) { + console.log(`\n[test-pack] ${label}\n[test-pack] $ ${cmd}`); + execSync(cmd, { stdio: "inherit", ...opts }); +} + +const tmp = mkdtempSync(path.join(os.tmpdir(), "lp-testpack-")); + +try { + // 1. Fresh build (tsup + template sync via onSuccess). + run("Building", "npm run build", { cwd: pkgRoot }); + + // 2. Pack the exact publish artifact straight into the temp dir. + console.log("\n[test-pack] Packing tarball"); + const tgzName = execSync(`npm pack --pack-destination ${q(tmp)}`, { + cwd: pkgRoot, + encoding: "utf-8", + }) + .split("\n") + .map((l) => l.trim()) + .filter(Boolean) + .pop(); + const tgz = path.join(tmp, tgzName); + console.log(`[test-pack] tarball: ${tgz}`); + + // 3. Install the tarball into a throwaway consumer (real files allowlist + deps). + writeFileSync( + path.join(tmp, "package.json"), + JSON.stringify({ name: "lp-testpack-consumer", private: true }, null, 2), + ); + run( + "Installing tarball (downloads dependencies from the registry)", + `npm install ${q(tgz)}`, + { cwd: tmp }, + ); + + const installed = path.join(tmp, "node_modules", "@leadsolutions", "lead-protocol"); + const bin = path.join(installed, "dist", "index.js"); + if (!existsSync(bin)) throw new Error(`installed bin not found: ${bin}`); + + // Templates must have shipped inside the installed package (the thing the + // `files` allowlist controls and `npm link` cannot prove). + const shipped = path.join(installed, "dist", "templates", ".agents", "CORE_RULES.md"); + if (!existsSync(shipped)) { + throw new Error(`templates missing from installed package: ${shipped}`); + } + console.log("[test-pack] OK: dist/templates shipped inside the installed package"); + + // 4. Run init / validate / status in a clean target dir. + const target = path.join(tmp, "project"); + mkdirSync(target); + run("init --yes", `node ${q(bin)} init --yes`, { cwd: target }); + + for (const file of ["CLAUDE.md", "AGENTS.md"]) { + const text = readFileSync(path.join(target, file), "utf-8"); + if (!text.includes("")) { + throw new Error(`${file} is missing the block`); + } + } + if (!existsSync(path.join(target, ".agents", "CORE_RULES.md"))) { + throw new Error(".agents/ was not created by init"); + } + console.log("[test-pack] OK: init created .agents/ and tagged CLAUDE.md / AGENTS.md"); + + run("validate", `node ${q(bin)} validate`, { cwd: target }); + run("status", `node ${q(bin)} status`, { cwd: target }); + + console.log("\n[test-pack] PASS: the published artifact installs and runs like production."); +} catch (err) { + process.exitCode = 1; + console.error(`\n[test-pack] FAIL: ${err.message}`); +} finally { + rmSync(tmp, { recursive: true, force: true }); + console.log(`[test-pack] cleaned up ${tmp}`); +} diff --git a/cli/src/commands/handoff.ts b/cli/src/commands/handoff.ts new file mode 100644 index 0000000..2b13ecf --- /dev/null +++ b/cli/src/commands/handoff.ts @@ -0,0 +1,145 @@ +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { Command } from "commander"; +import { select } from "@inquirer/prompts"; +import { findAgentsDir, discoverPairs, type AgentPair } from "../lib/project.js"; +import { parseHandoffMd, isPristineHandoff } from "../lib/handoff-parser.js"; +import * as ui from "../lib/ui.js"; + +interface HandoffOptions { + pair?: string; + raw?: boolean; + json?: boolean; +} + +function displayHandoff(pair: AgentPair): void { + const text = readFileSync(pair.handoffPath, "utf-8"); + + if (isPristineHandoff(text)) { + ui.info( + `${pair.actor}/${pair.agent} — pristine template (no session recorded yet)`, + ); + return; + } + + try { + const data = parseHandoffMd(text); + + console.log(ui.heading(`Handoff — ${pair.actor}/${pair.agent}`)); + console.log(); + console.log(ui.label("Status", ui.statusColor(data.status))); + console.log(ui.label("Last Agent", data.last_agent)); + console.log(ui.label("Timestamp", data.timestamp)); + console.log(ui.label("Updated", data.updated)); + console.log(ui.label("Version", data.version)); + console.log(); + console.log(ui.label("Last Action", data.last_action)); + console.log(ui.label("Pending Step", data.pending_step)); + console.log(ui.label("Blockers", data.blockers_context)); + console.log(ui.label("Open Threads", data.open_threads)); + console.log(); + + const checklist = data.session_close_checklist; + const total = Object.keys(checklist).length; + const checked = Object.values(checklist).filter( + (item) => item.checked || item.na, + ).length; + console.log( + ui.label("Checklist", `${checked}/${total} items resolved`), + ); + } catch { + ui.warn( + `${pair.actor}/${pair.agent} — handoff does not match the expected schema, showing raw content:`, + ); + console.log(); + console.log(text); + } +} + +export function registerHandoffCommand(program: Command): void { + program + .command("handoff") + .description("Show the current handoff state for an (actor, agent) pair") + .option("--pair ", "specify which pair to show") + .option("--raw", "print raw markdown instead of formatted output") + .option("--json", "output parsed JSON") + .action(async (opts: HandoffOptions) => { + console.log(); + + const agentsDir = findAgentsDir(); + if (!agentsDir) { + ui.error( + "Could not locate .agents/ directory. Run `lead-protocol init` first.", + ); + process.exit(1); + } + + const pairs = discoverPairs(agentsDir); + + if (pairs.length === 0) { + ui.info( + "No sessions recorded yet. Handoff files are created when an agent completes its first session.", + ); + console.log(); + return; + } + + let selectedPair: AgentPair; + + if (opts.pair) { + const [actor, agent] = opts.pair.split("/"); + const found = pairs.find( + (p) => p.actor === actor && p.agent === agent, + ); + if (!found) { + ui.error(`Pair not found: ${opts.pair}`); + console.log( + ui.dim( + ` Available: ${pairs.map((p) => `${p.actor}/${p.agent}`).join(", ")}`, + ), + ); + console.log(); + process.exit(1); + } + selectedPair = found; + } else if (pairs.length === 1) { + selectedPair = pairs[0]; + } else { + const choice = await select({ + message: "Select a pair to view:", + choices: pairs.map((p) => ({ + name: `${p.actor}/${p.agent}`, + value: `${p.actor}/${p.agent}`, + })), + }); + const [actor, agent] = choice.split("/"); + selectedPair = pairs.find( + (p) => p.actor === actor && p.agent === agent, + )!; + } + + if (opts.raw) { + const text = readFileSync(selectedPair.handoffPath, "utf-8"); + console.log(text); + return; + } + + if (opts.json) { + const text = readFileSync(selectedPair.handoffPath, "utf-8"); + if (isPristineHandoff(text)) { + console.log(JSON.stringify({ pristine: true }, null, 2)); + } else { + try { + const data = parseHandoffMd(text); + console.log(JSON.stringify(data, null, 2)); + } catch { + console.log(JSON.stringify({ parseError: true, raw: text }, null, 2)); + } + } + return; + } + + displayHandoff(selectedPair); + console.log(); + }); +} diff --git a/cli/src/commands/init.ts b/cli/src/commands/init.ts new file mode 100644 index 0000000..35d8175 --- /dev/null +++ b/cli/src/commands/init.ts @@ -0,0 +1,143 @@ +import { existsSync, readFileSync, writeFileSync, cpSync } from "node:fs"; +import path from "node:path"; +import { Command } from "commander"; +import { confirm } from "@inquirer/prompts"; +import { getTemplatesDir } from "../lib/project.js"; +import { writeGuidelines } from "../lib/guideline-writer.js"; +import * as ui from "../lib/ui.js"; + +const GITIGNORE_PROTOCOL_ENTRIES = [ + ".agents/local/", + "__pycache__/", + ".pytest_cache/", + "*.pyc", +]; + +function isLeadProtocolInstalled(targetDir: string): boolean { + return existsSync(path.join(targetDir, ".agents", "CORE_RULES.md")); +} + +function copyAgentsDir(templatesDir: string, targetDir: string): void { + const src = path.join(templatesDir, ".agents"); + const dest = path.join(targetDir, ".agents"); + cpSync(src, dest, { recursive: true }); +} + +function ensureGitignoreEntries(targetDir: string): void { + const gitignorePath = path.join(targetDir, ".gitignore"); + + if (!existsSync(gitignorePath)) { + const content = `# Lead Protocol\n${GITIGNORE_PROTOCOL_ENTRIES.join("\n")}\n`; + writeFileSync(gitignorePath, content, "utf-8"); + return; + } + + const existing = readFileSync(gitignorePath, "utf-8"); + const existingLines = new Set( + existing.split("\n").map((l) => l.trim()).filter(Boolean), + ); + + const missing = GITIGNORE_PROTOCOL_ENTRIES.filter( + (entry) => !existingLines.has(entry), + ); + + if (missing.length === 0) return; + + const suffix = existing.endsWith("\n") ? "" : "\n"; + const block = `\n# Lead Protocol\n${missing.join("\n")}\n`; + writeFileSync(gitignorePath, existing + suffix + block, "utf-8"); +} + +function generateGuidelines(templatesDir: string, targetDir: string): void { + const claudeContent = readFileSync( + path.join(templatesDir, "CLAUDE.md"), + "utf-8", + ); + const agentsContent = readFileSync( + path.join(templatesDir, "AGENTS.md"), + "utf-8", + ); + + const claudeResult = writeGuidelines( + path.join(targetDir, "CLAUDE.md"), + claudeContent, + ); + const agentsResult = writeGuidelines( + path.join(targetDir, "AGENTS.md"), + agentsContent, + ); + + if (claudeResult === "replaced") { + ui.info("CLAUDE.md updated (existing block replaced)"); + } else { + ui.success("CLAUDE.md created"); + } + + if (agentsResult === "replaced") { + ui.info("AGENTS.md updated (existing block replaced)"); + } else { + ui.success("AGENTS.md created"); + } +} + +export function registerInitCommand(program: Command): void { + program + .command("init") + .description("Initialize Lead Protocol in the current directory") + .option("-y, --yes", "skip confirmation prompt") + .action(async (opts: { yes?: boolean }) => { + const targetDir = process.cwd(); + const templatesDir = getTemplatesDir(); + + console.log(); + console.log(ui.heading("Lead Protocol — Init")); + console.log(); + + if (isLeadProtocolInstalled(targetDir)) { + if (!opts.yes) { + const proceed = await confirm({ + message: + "Lead Protocol is already installed in this directory. Overwrite protocol files?", + default: false, + }); + if (!proceed) { + ui.info("Aborted."); + console.log(); + return; + } + } + } else if (!opts.yes) { + const proceed = await confirm({ + message: "Initialize Lead Protocol in this directory?", + default: true, + }); + if (!proceed) { + ui.info("Aborted."); + console.log(); + return; + } + } + + copyAgentsDir(templatesDir, targetDir); + ui.success(".agents/ created"); + + generateGuidelines(templatesDir, targetDir); + + ensureGitignoreEntries(targetDir); + + console.log(); + ui.success("Lead Protocol initialized"); + console.log(); + console.log(ui.dim(" Next steps:")); + console.log( + ui.dim(" 1. Edit .agents/PROJECT_RULES.md — set your project identity"), + ); + console.log( + ui.dim(" 2. Edit .agents/AGENTS_MAP.md — map your agent signatures"), + ); + console.log( + ui.dim(" 3. Run `lead-protocol validate` to verify the setup"), + ); + console.log(); + }); +} diff --git a/cli/src/commands/status.ts b/cli/src/commands/status.ts new file mode 100644 index 0000000..8a947b1 --- /dev/null +++ b/cli/src/commands/status.ts @@ -0,0 +1,183 @@ +import { existsSync, readFileSync } from "node:fs"; +import path from "node:path"; +import { Command } from "commander"; +import { + findAgentsDir, + discoverPairs, + readProjectName, +} from "../lib/project.js"; +import { parseHandoffMd, isPristineHandoff } from "../lib/handoff-parser.js"; +import * as ui from "../lib/ui.js"; + +interface StatusOptions { + json?: boolean; +} + +interface DecisionEntry { + timestamp: string; + agent: string; + decision: string; + status: string; +} + +function readLastDecisions( + agentsDir: string, + count: number, +): DecisionEntry[] { + const filePath = path.join(agentsDir, "decisions.jsonl"); + if (!existsSync(filePath)) return []; + + const text = readFileSync(filePath, "utf-8"); + const lines = text + .split("\n") + .map((l) => l.trim()) + .filter(Boolean); + + const entries: DecisionEntry[] = []; + for (const line of lines.slice(-count)) { + try { + entries.push(JSON.parse(line)); + } catch { + // skip malformed lines + } + } + + return entries; +} + +function countActiveSessions(agentsDir: string): number { + const filePath = path.join(agentsDir, "sessions", "active_sessions.md"); + if (!existsSync(filePath)) return 0; + + const text = readFileSync(filePath, "utf-8"); + // Count table rows (lines starting with |, excluding header and separator) + const rows = text + .split("\n") + .filter((l) => l.startsWith("|")) + .filter((l) => !l.includes("---")) + .filter((l) => !l.toLowerCase().includes("agent")); + + return rows.length; +} + +function readProtocolVersion(agentsDir: string): string | null { + const corePath = path.join(agentsDir, "CORE_RULES.md"); + if (!existsSync(corePath)) return null; + + const text = readFileSync(corePath, "utf-8"); + const match = text.match(/Version:\s*(\S+)/); + return match?.[1] ?? null; +} + +export function registerStatusCommand(program: Command): void { + program + .command("status") + .description("Show a one-screen summary of the current Lead Protocol state") + .option("--json", "output as JSON") + .action(async (opts: StatusOptions) => { + const agentsDir = findAgentsDir(); + if (!agentsDir) { + ui.error( + "Could not locate .agents/ directory. Run `lead-protocol init` first.", + ); + process.exit(1); + } + + const projectName = readProjectName(agentsDir) ?? "Unknown Project"; + const pairs = discoverPairs(agentsDir); + const decisions = readLastDecisions(agentsDir, 3); + const activeSessions = countActiveSessions(agentsDir); + const protocolVersion = readProtocolVersion(agentsDir) ?? "unknown"; + + if (opts.json) { + const data = { + project: projectName, + protocolVersion, + activeSessions, + pairs: pairs.map((p) => { + const text = readFileSync(p.handoffPath, "utf-8"); + if (isPristineHandoff(text)) { + return { actor: p.actor, agent: p.agent, pristine: true }; + } + try { + const handoff = parseHandoffMd(text); + return { actor: p.actor, agent: p.agent, ...handoff }; + } catch { + return { actor: p.actor, agent: p.agent, parseError: true }; + } + }), + recentDecisions: decisions, + }; + console.log(JSON.stringify(data, null, 2)); + return; + } + + console.log(); + console.log( + ui.heading(`Lead Protocol Status — ${projectName}`), + ); + console.log(); + + // Handoffs + if (pairs.length === 0) { + console.log( + ui.dim( + " No sessions recorded yet. Handoff files appear after the first agent session.", + ), + ); + } else { + for (const pair of pairs) { + const text = readFileSync(pair.handoffPath, "utf-8"); + if (isPristineHandoff(text)) { + console.log( + ` ${ui.symbols.bullet} ${pair.actor}/${pair.agent} — ${ui.dim("pristine (no session yet)")}`, + ); + continue; + } + + try { + const data = parseHandoffMd(text); + console.log( + ui.heading(` Handoff (${pair.actor}/${pair.agent})`), + ); + console.log( + ui.label(" Status", ui.statusColor(data.status)), + ); + console.log(ui.label(" Last Action", data.last_action)); + console.log( + ui.label(" Pending Step", data.pending_step), + ); + console.log(ui.label(" Updated", data.timestamp)); + } catch { + console.log( + ` ${ui.symbols.warning} ${pair.actor}/${pair.agent} — could not parse handoff`, + ); + } + } + } + + console.log(); + + // Recent decisions + if (decisions.length > 0) { + console.log(ui.heading(" Recent Decisions")); + for (const d of decisions) { + const date = d.timestamp.split("T")[0]; + console.log( + ` ${ui.dim(date)} ${d.decision}`, + ); + } + } else { + console.log( + ui.dim(" No decisions recorded yet."), + ); + } + + console.log(); + console.log(ui.label(" Active Sessions", String(activeSessions))); + console.log( + ui.label(" Protocol Version", protocolVersion), + ); + console.log(); + }); +} diff --git a/cli/src/commands/validate.ts b/cli/src/commands/validate.ts new file mode 100644 index 0000000..7e1dfba --- /dev/null +++ b/cli/src/commands/validate.ts @@ -0,0 +1,122 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; +import { Command } from "commander"; +import { findAgentsDir, discoverPairs } from "../lib/project.js"; +import { + findSchemasDir, + validateDecisionsJsonl, + validateHandoff, + type ValidationResult, +} from "../lib/validator.js"; +import * as ui from "../lib/ui.js"; + +interface ValidateOptions { + schemasDir?: string; +} + +function printResult(result: ValidationResult): void { + const relativePath = path.relative(process.cwd(), result.file); + + if (result.skipped) { + console.log(` ${ui.symbols.warning} ${relativePath} — ${result.skipReason} (skipped)`); + return; + } + + if (result.errors.length === 0) { + console.log(` ${ui.symbols.success} ${relativePath}`); + return; + } + + console.log(` ${ui.symbols.error} ${relativePath}`); + for (const err of result.errors) { + console.log(` ${ui.dim(err)}`); + } +} + +export function registerValidateCommand(program: Command): void { + program + .command("validate") + .description("Validate Lead Protocol state files against their schemas") + .argument("[files...]", "specific files to validate (default: auto-discover)") + .option("--schemas-dir ", "override schemas directory") + .action(async (files: string[], opts: ValidateOptions) => { + console.log(); + console.log(ui.heading("Lead Protocol — Validate")); + console.log(); + + const agentsDir = findAgentsDir(); + if (!agentsDir) { + ui.error("Could not locate .agents/ directory. Run `lead-protocol init` first."); + process.exit(2); + } + + let schemasDir: string; + try { + schemasDir = opts.schemasDir ?? findSchemasDir(agentsDir); + } catch (e) { + ui.error((e as Error).message); + process.exit(2); + } + + const results: ValidationResult[] = []; + + if (files.length > 0) { + for (const file of files) { + const absPath = path.resolve(file); + const name = path.basename(absPath); + if (name === "decisions.jsonl") { + results.push(validateDecisionsJsonl(absPath, schemasDir)); + } else if (name === "handoff.md") { + results.push(validateHandoff(absPath, schemasDir)); + } else { + results.push({ + file: absPath, + type: "decisions", + errors: [`unrecognized file (expected 'handoff.md' or 'decisions.jsonl')`], + }); + } + } + } else { + // Auto-discover + const decisionsPath = path.join(agentsDir, "decisions.jsonl"); + if (existsSync(decisionsPath)) { + results.push(validateDecisionsJsonl(decisionsPath, schemasDir)); + } + + const pairs = discoverPairs(agentsDir); + for (const pair of pairs) { + results.push(validateHandoff(pair.handoffPath, schemasDir)); + } + + if (results.length === 0) { + ui.info("No state files found to validate (this is normal for a fresh setup)."); + console.log(); + return; + } + } + + // Print results + const passed = results.filter((r) => r.errors.length === 0 && !r.skipped); + const failed = results.filter((r) => r.errors.length > 0); + const skipped = results.filter((r) => r.skipped); + + for (const result of results) { + printResult(result); + } + + console.log(); + + if (failed.length === 0) { + const parts = [`${passed.length} passed`]; + if (skipped.length > 0) parts.push(`${skipped.length} skipped`); + ui.success(`OK — validated ${results.length} file(s) (${parts.join(", ")})`); + } else { + ui.error( + `Validation failed: ${failed.length} error(s) in ${results.length} file(s)`, + ); + } + console.log(); + + process.exit(failed.length > 0 ? 1 : 0); + }); +} diff --git a/cli/src/index.ts b/cli/src/index.ts new file mode 100644 index 0000000..c6c0f39 --- /dev/null +++ b/cli/src/index.ts @@ -0,0 +1,23 @@ +import { createRequire } from "node:module"; +import { Command } from "commander"; +import { registerInitCommand } from "./commands/init.js"; +import { registerHandoffCommand } from "./commands/handoff.js"; +import { registerValidateCommand } from "./commands/validate.js"; +import { registerStatusCommand } from "./commands/status.js"; + +const require = createRequire(import.meta.url); +const pkg = require("../package.json"); + +const program = new Command(); + +program + .name("lead-protocol") + .description("CLI tooling for the Lead Protocol — multi-agent coordination framework") + .version(pkg.version, "-v, --version"); + +registerInitCommand(program); +registerHandoffCommand(program); +registerValidateCommand(program); +registerStatusCommand(program); + +program.parse(); diff --git a/cli/src/lib/guideline-writer.ts b/cli/src/lib/guideline-writer.ts new file mode 100644 index 0000000..aa3c3c7 --- /dev/null +++ b/cli/src/lib/guideline-writer.ts @@ -0,0 +1,46 @@ +import { existsSync, readFileSync, writeFileSync } from "node:fs"; + +const TAG_OPEN = ""; +const TAG_CLOSE = ""; +const TAG_PATTERN = /[\s\S]*?<\/lead-protocol>/; + +export type WriteResult = "new" | "replaced" | "noop"; + +export function writeGuidelines( + filePath: string, + guidelines: string, +): WriteResult { + if (!guidelines.trim()) { + return "noop"; + } + + const block = `${TAG_OPEN}\n${guidelines}\n\n${TAG_CLOSE}`; + + let content = ""; + let replaced = false; + + if (existsSync(filePath)) { + content = readFileSync(filePath, "utf-8"); + + if (TAG_PATTERN.test(content)) { + content = content.replace(TAG_PATTERN, block); + replaced = true; + } else { + const existing = content.trimEnd(); + const separator = existing.length > 0 ? "\n\n===\n\n" : ""; + content = existing + separator + block; + } + } else { + content = block; + } + + content = content.replace(/\n{3,}/g, "\n\n"); + + if (!content.endsWith("\n")) { + content += "\n"; + } + + writeFileSync(filePath, content, "utf-8"); + + return replaced ? "replaced" : "new"; +} diff --git a/cli/src/lib/handoff-parser.ts b/cli/src/lib/handoff-parser.ts new file mode 100644 index 0000000..2a06b40 --- /dev/null +++ b/cli/src/lib/handoff-parser.ts @@ -0,0 +1,116 @@ +export interface HandoffData { + version: string; + updated: string; + last_agent: string; + timestamp: string; + status: string; + last_action: string; + pending_step: string; + blockers_context: string; + open_threads: string; + session_close_checklist: Record; +} + +export interface ChecklistItem { + checked: boolean; + na?: boolean; + note?: string; +} + +const PRISTINE_MARKERS = ["YYYY-MM-DD", "[Your Agent Signature]"]; + +const CHECKLIST_KEYS = [ + "activity_log_updated", + "decisions_appended", + "personal_lessons_appended", + "project_lessons_appended", + "journal_appended", + "commit_convention_followed", + "version_bumps_applied", + "active_sessions_row_removed", +] as const; + +export function isPristineHandoff(text: string): boolean { + return PRISTINE_MARKERS.some((marker) => text.includes(marker)); +} + +export function parseHandoffMd(text: string): HandoffData { + const result: Partial = {}; + + // Version + Updated line + const versionMatch = text.match( + /^>\s*Version:\s*(\S+)\s*\|\s*Updated:\s*(\S+)\s*$/m, + ); + if (!versionMatch) { + throw new Error("missing or malformed Version/Updated line"); + } + result.version = versionMatch[1]; + result.updated = versionMatch[2]; + + // Seven canonical fields as **Field:** value + const fieldMap: Record = { + "Last Agent": "last_agent", + Timestamp: "timestamp", + Status: "status", + "Last Action": "last_action", + "Pending Step": "pending_step", + "Blockers/Context": "blockers_context", + "Open Threads": "open_threads", + }; + + for (const [label, key] of Object.entries(fieldMap)) { + const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const pattern = new RegExp( + `\\*\\*${escaped}:\\*\\*\\s*(.+?)(?=\\n\\*\\*|\\n\\n|$)`, + "s", + ); + const match = text.match(pattern); + if (!match) { + throw new Error(`missing field: ${label}`); + } + (result as Record)[key] = match[1].trim(); + } + + // Session close checklist — eight checkboxes in fixed order + const checkboxPattern = /^- \[([ xX])\]\s*(.+?)(?:\s*—\s*(.+?))?$/gm; + const matches: Array<[string, string, string]> = []; + let m: RegExpExecArray | null; + + while ((m = checkboxPattern.exec(text)) !== null) { + matches.push([m[1], m[2], m[3] ?? ""]); + } + + if (matches.length < CHECKLIST_KEYS.length) { + throw new Error( + `expected ${CHECKLIST_KEYS.length} checklist items, found ${matches.length}`, + ); + } + + const checklist: Record = {}; + + for (let i = 0; i < CHECKLIST_KEYS.length; i++) { + const [mark, body, note] = matches[i]; + const checked = mark.toLowerCase() === "x"; + const item: ChecklistItem = { checked }; + + const bodyLower = body.toLowerCase(); + const noteLower = note.toLowerCase(); + + if ( + (note && (noteLower.includes("n/a") || noteLower.includes("not applicable"))) || + bodyLower.includes("n/a") + ) { + item.na = true; + } + + if (note.trim()) { + item.note = note.trim(); + } + + checklist[CHECKLIST_KEYS[i]] = item; + } + + result.session_close_checklist = checklist; + + return result as HandoffData; +} diff --git a/cli/src/lib/project.ts b/cli/src/lib/project.ts new file mode 100644 index 0000000..1541c0d --- /dev/null +++ b/cli/src/lib/project.ts @@ -0,0 +1,76 @@ +import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export function findAgentsDir(startDir?: string): string | null { + let dir = startDir ?? process.cwd(); + + while (true) { + const candidate = path.join(dir, ".agents"); + if (existsSync(candidate) && statSync(candidate).isDirectory()) { + return candidate; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + + return null; +} + +export function getTemplatesDir(): string { + // Templates are bundled into dist/templates/ at build time + // (see scripts/sync-templates.mjs). __dirname is dist/ in the bundle. + return path.resolve(__dirname, "templates"); +} + +export interface AgentPair { + actor: string; + agent: string; + handoffPath: string; +} + +export function discoverPairs(agentsDir: string): AgentPair[] { + const localDir = path.join(agentsDir, "local"); + if (!existsSync(localDir) || !statSync(localDir).isDirectory()) { + return []; + } + + const pairs: AgentPair[] = []; + + for (const actorName of readdirSync(localDir).sort()) { + const actorPath = path.join(localDir, actorName); + if (!statSync(actorPath).isDirectory()) continue; + + for (const agentName of readdirSync(actorPath).sort()) { + const agentPath = path.join(actorPath, agentName); + if (!statSync(agentPath).isDirectory()) continue; + + const handoffPath = path.join(agentPath, "handoff.md"); + if (existsSync(handoffPath)) { + pairs.push({ + actor: actorName, + agent: agentName, + handoffPath, + }); + } + } + } + + return pairs; +} + +export function readProjectName(agentsDir: string): string | null { + const projectRulesPath = path.join(agentsDir, "PROJECT_RULES.md"); + if (!existsSync(projectRulesPath)) return null; + + const content = readFileSync(projectRulesPath, "utf-8"); + const match = content.match(/\*\*Name:\*\*\s*(.+)/); + if (match && !match[1].includes("[")) { + return match[1].trim(); + } + return null; +} diff --git a/cli/src/lib/ui.ts b/cli/src/lib/ui.ts new file mode 100644 index 0000000..171a149 --- /dev/null +++ b/cli/src/lib/ui.ts @@ -0,0 +1,59 @@ +import chalk from "chalk"; + +export const symbols = { + success: chalk.green("✔"), + error: chalk.red("✖"), + warning: chalk.yellow("⚠"), + info: chalk.blue("ℹ"), + arrow: chalk.cyan("→"), + bullet: chalk.dim("•"), +}; + +export function heading(text: string): string { + return chalk.bold(text); +} + +export function label(key: string, value: string): string { + return ` ${chalk.dim(key + ":")} ${value}`; +} + +export function statusColor(status: string): string { + switch (status.toUpperCase()) { + case "STABLE": + return chalk.green(status); + case "IN_PROGRESS": + return chalk.yellow(status); + case "BLOCKED": + return chalk.red(status); + default: + return status; + } +} + +export function success(message: string): void { + console.log(`${symbols.success} ${message}`); +} + +export function error(message: string): void { + console.error(`${symbols.error} ${chalk.red(message)}`); +} + +export function warn(message: string): void { + console.log(`${symbols.warning} ${chalk.yellow(message)}`); +} + +export function info(message: string): void { + console.log(`${symbols.info} ${message}`); +} + +export function dim(text: string): string { + return chalk.dim(text); +} + +export function bold(text: string): string { + return chalk.bold(text); +} + +export function divider(): void { + console.log(chalk.dim("─".repeat(50))); +} diff --git a/cli/src/lib/validator.ts b/cli/src/lib/validator.ts new file mode 100644 index 0000000..156c58b --- /dev/null +++ b/cli/src/lib/validator.ts @@ -0,0 +1,127 @@ +import { existsSync, readFileSync } from "node:fs"; +import path from "node:path"; +import Ajv from "ajv"; +import addFormats from "ajv-formats"; +import { parseHandoffMd, isPristineHandoff } from "./handoff-parser.js"; + +export interface ValidationResult { + file: string; + type: "handoff" | "decisions"; + errors: string[]; + skipped?: boolean; + skipReason?: string; +} + +function createValidator() { + const ajv = new Ajv({ allErrors: true, strict: false, validateSchema: false }); + addFormats(ajv); + return ajv; +} + +function loadSchema(schemasDir: string, name: string): object { + const schemaPath = path.join(schemasDir, name); + if (!existsSync(schemaPath)) { + throw new Error(`schema not found: ${schemaPath}`); + } + return JSON.parse(readFileSync(schemaPath, "utf-8")); +} + +export function findSchemasDir(agentsDir: string): string { + const schemasDir = path.join(agentsDir, "schemas"); + if (!existsSync(schemasDir)) { + throw new Error( + `could not locate schemas directory at ${schemasDir}`, + ); + } + return schemasDir; +} + +export function validateDecisionsJsonl( + filePath: string, + schemasDir: string, +): ValidationResult { + const result: ValidationResult = { + file: filePath, + type: "decisions", + errors: [], + }; + + if (!existsSync(filePath)) { + result.errors.push("file not found"); + return result; + } + + const schema = loadSchema(schemasDir, "decisions.entry.schema.json"); + const ajv = createValidator(); + const validate = ajv.compile(schema); + + const text = readFileSync(filePath, "utf-8"); + const lines = text.split("\n"); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + if (!line) continue; + + let entry: unknown; + try { + entry = JSON.parse(line); + } catch (e) { + result.errors.push(`line ${i + 1}: invalid JSON — ${(e as Error).message}`); + continue; + } + + if (!validate(entry)) { + for (const err of validate.errors ?? []) { + const loc = err.instancePath || ""; + result.errors.push(`line ${i + 1}: ${loc} — ${err.message}`); + } + } + } + + return result; +} + +export function validateHandoff( + filePath: string, + schemasDir: string, +): ValidationResult { + const result: ValidationResult = { + file: filePath, + type: "handoff", + errors: [], + }; + + if (!existsSync(filePath)) { + result.errors.push("file not found"); + return result; + } + + const text = readFileSync(filePath, "utf-8"); + + if (isPristineHandoff(text)) { + result.skipped = true; + result.skipReason = "pristine template"; + return result; + } + + let data: unknown; + try { + data = parseHandoffMd(text); + } catch (e) { + result.errors.push(`parse error — ${(e as Error).message}`); + return result; + } + + const schema = loadSchema(schemasDir, "handoff.schema.json"); + const ajv = createValidator(); + const validate = ajv.compile(schema); + + if (!validate(data)) { + for (const err of validate.errors ?? []) { + const loc = err.instancePath || ""; + result.errors.push(`${loc} — ${err.message}`); + } + } + + return result; +} diff --git a/cli/tsconfig.json b/cli/tsconfig.json new file mode 100644 index 0000000..bf195d5 --- /dev/null +++ b/cli/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": false, + "isolatedModules": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/cli/tsup.config.ts b/cli/tsup.config.ts new file mode 100644 index 0000000..e19d2be --- /dev/null +++ b/cli/tsup.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + target: "node18", + outDir: "dist", + clean: true, + splitting: false, + banner: { + js: "#!/usr/bin/env node", + }, + // Mirror the repo-root templates into dist/templates/ after each build + // (runs after clean, both for `build` and `dev --watch`). + onSuccess: "node scripts/sync-templates.mjs", +});