diff --git a/CHANGELOG.md b/CHANGELOG.md index 59be4516..77537b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -862,7 +862,7 @@ All command names have been simplified for clarity: - `ci-fixer.md` (sonnet): Fix CI failures and PR comments, called by ci-monitor - `simple-fixer.md` (haiku): Execute pre-defined code fixes mechanically - **Workflow Enforcement Gates** - Explicit STOP gates in all agents - - Agents cannot skip review-orchestrator, delivery-validator, docs-updater + - Agents cannot skip Phase 9 review loop, delivery-validator, docs-updater - Agents cannot create PRs - only /ship creates PRs - SubagentStop hooks enforce mandatory workflow sequence - **State Schema Files** diff --git a/CLAUDE.md b/CLAUDE.md index aea1b738..317a4d2e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,7 +49,7 @@ lib/ # Shared library (canonical source) └── index.js # Main exports plugins/ # Claude Code plugins -├── next-task/ # Master workflow (14 agents) +├── next-task/ # Master workflow (12 agents) ├── ship/ # PR workflow ├── deslop/ # AI slop cleanup ├── audit-project/ # Multi-agent review @@ -91,7 +91,7 @@ Platform-aware: `.claude/` (Claude), `.opencode/` (OpenCode), `.codex/` (Codex) Cannot skip in /next-task: - `exploration-agent` → before planning - `planning-agent` → before implementation -- `review-orchestrator` → before shipping +- **Phase 9 review loop** → MUST use orchestrate-review skill, spawns parallel reviewers, iterates until clean - `delivery-validator` → before /ship ## PR Auto-Review @@ -137,6 +137,6 @@ Choose the appropriate model based on task complexity and quality multiplier eff **Examples**: - `/enhance:agent` uses opus - false positives damage agent quality across entire codebase - `simple-fixer` uses haiku - mechanically applies pre-defined fixes with no judgment -- `review-orchestrator` uses opus - review quality affects entire workflow +- Phase 9 review loop spawns sonnet reviewers - multiple focused agents reduce rubber-stamping - `worktree-manager` uses haiku - scripted git commands with no decision-making diff --git a/README.md b/README.md index df0629b0..e8eaccce 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ This means you can run `/deslop apply` and trust that it won't break things. ### 2. Review Loops With Safeguards -The review-orchestrator agent runs core review passes (code quality, security, performance, test coverage) plus conditional specialists until there are no open issues. Then it runs deslop-work on its own fixes to catch any AI artifacts it introduced. +The Phase 9 review loop spawns parallel reviewer agents (code quality, security, performance, test coverage) plus conditional specialists, iterating until no open issues remain. It runs deslop-work after each iteration to catch any AI artifacts. ### 3. Workflow Enforcement @@ -122,7 +122,7 @@ Frontier models write good code. That's solved. What's not solved: **1. One agent, one job, done extremely well** -Same principle as good code: single responsibility. The exploration-agent explores. The implementation-agent implements. The review-orchestrator coordinates reviews. No agent tries to do everything. 29 specialized agents, each with narrow scope and clear success criteria. +Same principle as good code: single responsibility. The exploration-agent explores. The implementation-agent implements. Phase 9 spawns multiple focused reviewers. No agent tries to do everything. 28 specialized agents, each with narrow scope and clear success criteria. **2. Pipeline with gates, not a monolith** @@ -239,7 +239,6 @@ With proper workflow structure, context management, and quality gates, AI agents | implementation-agent | opus | Writes the actual code | | deslop-work | sonnet | Removes AI artifacts before review | | test-coverage-checker | sonnet | Validates tests exist and are meaningful | -| review-orchestrator | opus | Coordinates parallel review agents | | delivery-validator | sonnet | Final checks before shipping | | docs-updater | sonnet | Updates documentation | | ci-monitor | haiku | Watches CI status | @@ -552,7 +551,7 @@ implementation-agent writes code ↓ deslop-work cleans AI artifacts ↓ -review-orchestrator iterates until approved +Phase 9 review loop iterates until approved ↓ delivery-validator checks requirements ↓ diff --git a/adapters/codex/install.sh b/adapters/codex/install.sh index ff57d963..cf451203 100644 --- a/adapters/codex/install.sh +++ b/adapters/codex/install.sh @@ -124,8 +124,47 @@ for mapping in "${SKILL_MAPPINGS[@]}"; do fi done +# Install native skills (already have SKILL.md format) +echo +echo "📚 Installing native skills..." + +# Native skill mappings: skill_name:plugin:skill_name_in_source +NATIVE_SKILL_MAPPINGS=( + "orchestrate-review:next-task:orchestrate-review" +) + +for mapping in "${NATIVE_SKILL_MAPPINGS[@]}"; do + IFS=':' read -r SKILL_NAME PLUGIN SOURCE_SKILL <<< "$mapping" + SOURCE_SKILL_DIR="$REPO_ROOT/plugins/$PLUGIN/skills/$SOURCE_SKILL" + TARGET_SKILL_DIR="$CODEX_SKILLS_DIR/$SKILL_NAME" + + if [ -d "$SOURCE_SKILL_DIR" ]; then + # Create skill directory + mkdir -p "$TARGET_SKILL_DIR" + + # Copy SKILL.md + if [ -f "$SOURCE_SKILL_DIR/SKILL.md" ]; then + cp "$SOURCE_SKILL_DIR/SKILL.md" "$TARGET_SKILL_DIR/SKILL.md" + echo " ✓ Installed native skill: \$${SKILL_NAME}" + else + echo " ⚠️ Skipped \$${SKILL_NAME} (SKILL.md not found)" + continue + fi + + # Copy optional subdirectories (references/, scripts/, assets/) + for subdir in references scripts assets; do + if [ -d "$SOURCE_SKILL_DIR/$subdir" ]; then + cp -r "$SOURCE_SKILL_DIR/$subdir" "$TARGET_SKILL_DIR/" + echo " ✓ Copied $subdir/ directory" + fi + done + else + echo " ⚠️ Skipped \$${SKILL_NAME} (source not found: $SOURCE_SKILL_DIR)" + fi +done + # Remove old/deprecated skills and prompts -OLD_SKILLS=("deslop" "review" "reality-check-set" "pr-merge") +OLD_SKILLS=("deslop" "review" "reality-check-set" "pr-merge" "review-orchestrator") for old_skill in "${OLD_SKILLS[@]}"; do if [ -d "$CODEX_SKILLS_DIR/$old_skill" ]; then rm -rf "$CODEX_SKILLS_DIR/$old_skill" diff --git a/adapters/opencode-plugin/index.ts b/adapters/opencode-plugin/index.ts index 50a509b7..5d015e15 100644 --- a/adapters/opencode-plugin/index.ts +++ b/adapters/opencode-plugin/index.ts @@ -34,7 +34,6 @@ const AGENT_THINKING_CONFIG: Record { - if (input.agent === "review-orchestrator") { + if (input.agent === "planning-agent") { output.options.thinking = { type: "enabled", budgetTokens: 16000 } } } @@ -662,7 +662,7 @@ export const WorkflowPlugin: Plugin = async (ctx) => { "chat.params": async (input, output) => { // Use higher reasoning for complex agents - if (["review-orchestrator", "planning-agent"].includes(input.agent)) { + if (["planning-agent", "implementation-agent"].includes(input.agent)) { output.options.thinking = { type: "enabled", budgetTokens: 16000 } } } @@ -890,10 +890,6 @@ Add to user's `opencode.jsonc`: }, // Complex agents - extended thinking - "review-orchestrator": { - "model": "anthropic/claude-sonnet-4", - "options": { "thinking": { "type": "enabled", "budgetTokens": 16000 } } - }, "planning-agent": { "model": "anthropic/claude-sonnet-4", "options": { "thinking": { "type": "enabled", "budgetTokens": 16000 } } diff --git a/agent-docs/workflow.md b/agent-docs/workflow.md index adc7ad0b..4038f9b2 100644 --- a/agent-docs/workflow.md +++ b/agent-docs/workflow.md @@ -25,7 +25,7 @@ The main orchestrator **MUST spawn these agents in order**: | 7 | `implementation-agent` | opus | Read, Write, Edit, Bash | Execute plan | | 8 | `deslop-work` | sonnet | Read, Grep, Edit, Bash(git:*) | Clean AI slop (uses pipeline.js) | | 8 | `test-coverage-checker` | sonnet | Bash(npm:*), Read, Grep | Validate test coverage | -| 9 | `review-orchestrator` | opus | Task(review) | Multi-pass review loop | +| 9 | Phase 9 review loop | sonnet reviewers | Task(general-purpose) | Multi-pass review with parallel agents | | 10 | `delivery-validator` | sonnet | Bash(npm:*), Read | Validate completion | | 11 | `docs-updater` | sonnet | Read, Edit, Task(simple-fixer) | Update documentation | | 12 | `/ship` command | - | - | PR creation and merge | @@ -34,13 +34,13 @@ The main orchestrator **MUST spawn these agents in order**: - **`exploration-agent`** - Required for understanding codebase before planning - **`planning-agent`** - Required for creating implementation plan -- **`review-orchestrator`** - Required for code review before shipping +- **Phase 9 review loop** - Required for code review before shipping (uses orchestrate-review skill) - **`delivery-validator`** - Required before calling /ship ### Review Decision Gate -If review-orchestrator reports `blocked: true` (iteration limit or stall), /next-task must decide: -- Re-run review-orchestrator with `--resume`, or +If Phase 9 review loop reports `blocked: true` (iteration limit or stall), /next-task must decide: +- Re-run Phase 9 review loop, or - Override and continue if issues are non-blocking (clear the queue file). --- diff --git a/docs/CROSS_PLATFORM.md b/docs/CROSS_PLATFORM.md index fb86ec70..7ba0cacb 100644 --- a/docs/CROSS_PLATFORM.md +++ b/docs/CROSS_PLATFORM.md @@ -81,7 +81,7 @@ claude --plugin-dir /path/to/awesome-slash/plugins/next-task | exploration-agent | opus | Deep codebase analysis | | planning-agent | opus | Design implementation plans | | implementation-agent | opus | Execute plans with quality code | -| review-orchestrator | opus | Multi-agent review iteration | +| Phase 9 review loop | sonnet reviewers | Multi-pass review with parallel agents | | deslop-work | sonnet | Clean AI slop from changes | | test-coverage-checker | sonnet | Validate test coverage | | delivery-validator | sonnet | Autonomous delivery validation | @@ -150,7 +150,7 @@ The native plugin (`~/.opencode/plugins/awesome-slash/`) provides deep integrati | Execution | 0 | worktree-manager, simple-fixer, ci-monitor | | Discovery | 8k | task-discoverer, docs-updater | | Analysis | 12k | exploration-agent, deslop-work, ci-fixer | -| Reasoning | 16k | planning-agent, implementation-agent, review-orchestrator | +| Reasoning | 16k | planning-agent, implementation-agent | | Synthesis | 20k | plan-synthesizer, enhancement-orchestrator | **Provider-Specific Thinking:** diff --git a/docs/README.md b/docs/README.md index a28ac23f..746f0ba0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,7 +42,7 @@ AI models can write code. The bottleneck is everything else—picking tasks, man | Document | Description | |----------|-------------| -| [reference/AGENTS.md](./reference/AGENTS.md) | All 32 agents: purpose, model, tools, restrictions. | +| [reference/AGENTS.md](./reference/AGENTS.md) | All 31 agents: purpose, model, tools, restrictions. | | [reference/SLOP-PATTERNS.md](./reference/SLOP-PATTERNS.md) | All detection patterns by language, severity, auto-fix. | | [reference/MCP-TOOLS.md](./reference/MCP-TOOLS.md) | MCP server tools: parameters, returns, platform config. | diff --git a/docs/reference/AGENTS.md b/docs/reference/AGENTS.md index 2ec82c44..f0bb4b11 100644 --- a/docs/reference/AGENTS.md +++ b/docs/reference/AGENTS.md @@ -2,7 +2,7 @@ Complete reference for all agents in awesome-slash. -**TL;DR:** 32 agents across 5 plugins. opus for reasoning, sonnet for patterns, haiku for execution. Each agent does one thing well. +**TL;DR:** 31 agents across 5 plugins. opus for reasoning, sonnet for patterns, haiku for execution. Each agent does one thing well. --- @@ -10,7 +10,7 @@ Complete reference for all agents in awesome-slash. | Plugin | Agents | Jump to | |--------|--------|---------| -| next-task | 13 | [task-discoverer](#task-discoverer), [worktree-manager](#worktree-manager), [exploration-agent](#exploration-agent), [planning-agent](#planning-agent), [implementation-agent](#implementation-agent), [deslop-work](#deslop-work), [test-coverage-checker](#test-coverage-checker), [review-orchestrator](#review-orchestrator), [delivery-validator](#delivery-validator), [docs-updater](#docs-updater), [simple-fixer](#simple-fixer), [ci-monitor](#ci-monitor), [ci-fixer](#ci-fixer) | +| next-task | 12 | [task-discoverer](#task-discoverer), [worktree-manager](#worktree-manager), [exploration-agent](#exploration-agent), [planning-agent](#planning-agent), [implementation-agent](#implementation-agent), [deslop-work](#deslop-work), [test-coverage-checker](#test-coverage-checker), [delivery-validator](#delivery-validator), [docs-updater](#docs-updater), [simple-fixer](#simple-fixer), [ci-monitor](#ci-monitor), [ci-fixer](#ci-fixer) | | audit-project | 10 | [code-quality-reviewer](#code-quality-reviewer), [security-expert](#security-expert), [performance-engineer](#performance-engineer), [test-quality-guardian](#test-quality-guardian), [architecture-reviewer](#architecture-reviewer), [database-specialist](#database-specialist), [api-designer](#api-designer), [frontend-specialist](#frontend-specialist), [backend-specialist](#backend-specialist), [devops-reviewer](#devops-reviewer) | | enhance | 7 | [enhancement-orchestrator](#enhancement-orchestrator), [plugin-enhancer](#plugin-enhancer), [agent-enhancer](#agent-enhancer), [claudemd-enhancer](#claudemd-enhancer), [docs-enhancer](#docs-enhancer), [prompt-enhancer](#prompt-enhancer), [enhancement-reporter](#enhancement-reporter) | | drift-detect | 1 | [plan-synthesizer](#plan-synthesizer) | @@ -26,7 +26,7 @@ Complete reference for all agents in awesome-slash. ## Overview -awesome-slash uses 32 specialized agents across 5 plugins. Each agent is optimized for a specific task and assigned a model based on complexity: +awesome-slash uses 31 specialized agents across 5 plugins. Each agent is optimized for a specific task and assigned a model based on complexity: | Model | Use Case | Cost | |-------|----------|------| @@ -35,7 +35,7 @@ awesome-slash uses 32 specialized agents across 5 plugins. Each agent is optimiz | haiku | Mechanical execution, no judgment | Low | **Agent types:** -- **File-based agents** (22) - Defined in `plugins/*/agents/*.md` with frontmatter +- **File-based agents** (21) - Defined in `plugins/*/agents/*.md` with frontmatter - **Role-based agents** (10) - Defined inline via Task tool with specialized prompts --- @@ -200,39 +200,6 @@ awesome-slash uses 32 specialized agents across 5 plugins. Each agent is optimiz --- -### review-orchestrator - -**Model:** opus -**Purpose:** Coordinate multi-agent review until clean. - -**What it does:** -1. Launches core review passes (parallel when nested subagents are supported; otherwise runs in-agent passes in series on Claude Code): - - Code quality (includes error handling) - - Security - - Performance - - Test coverage -2. Adds conditional specialists (DB, architecture, API, frontend, backend, devops) -3. Aggregates findings by severity -4. Writes a review queue file in the platform state dir -5. Fixes all non-false-positive issues -6. Runs a deslop pass after each iteration (deslop-work when nested subagents are supported; inline slop scan on Claude Code) -7. Loops until no open issues remain -8. Stops early on iteration limit or stall and returns control for decision - -**Tools available:** -- Task (only when nested subagents are supported) -- Bash (git) -- Read, Write, Edit - -**Restrictions:** -- MUST NOT create PR -- MUST NOT push -- MUST NOT invoke delivery-validator - -**Why opus:** Review coordination requires judgment. Which findings are real? Which can be auto-fixed? How to prioritize? Opus handles this well. - ---- - ### delivery-validator **Model:** sonnet @@ -666,7 +633,6 @@ Agents have restricted tool access for safety: | Agent | Restricted From | Why | |-------|-----------------|-----| | implementation-agent | PR creation, git push | Workflow enforces order | -| review-orchestrator | PR creation, git push | Must complete review first | | delivery-validator | PR creation, git push | Must pass validation first | | worktree-manager | Most tools | Only needs git | | simple-fixer | Most tools | Only needs edit | diff --git a/docs/workflows/NEXT-TASK.md b/docs/workflows/NEXT-TASK.md index d25e5d2a..6718fcef 100644 --- a/docs/workflows/NEXT-TASK.md +++ b/docs/workflows/NEXT-TASK.md @@ -182,29 +182,29 @@ Both agents run in parallel: ### Phase 9: Review Loop -**Agent:** review-orchestrator (opus) +**Execution:** Inline in main orchestrator (uses orchestrate-review skill) **Human interaction: No** -The agent: -1. Launches core review passes (parallel when nested subagents are supported; otherwise runs in-agent passes in series on Claude Code): - - Code quality (includes error handling) - - Security - - Performance - - Test coverage -2. Adds conditional specialists (DB, architecture, API, frontend, backend, devops) -3. Aggregates findings by severity (critical/high/medium/low) -4. Fixes all non-false-positive issues -5. Writes a review queue file in the platform state dir -6. **Runs a deslop pass after EACH iteration** (uses deslop-work where nested subagents are supported; inline slop scan on Claude Code) -7. Repeats until no open issues remain -8. Stops early if iteration limit or stall detected; control returns to /next-task for decision +The orchestrator: +1. Reads orchestrate-review skill for guidance +2. Detects content signals (database, API, frontend, backend, devops, architecture) +3. Spawns parallel Task agents (general-purpose, sonnet) - one per review pass: + - Core (always): code quality, security, performance, test coverage + - Conditional: database, architecture, api, frontend, backend, devops +4. Aggregates findings by severity (critical/high/medium/low) +5. Fixes all non-false-positive issues +6. Commits fixes +7. Runs deslop-work after each iteration +8. Re-reviews changed files +9. Repeats until no open issues remain +10. Stops early if iteration limit or stall detected The loop continues until clean, but stops early if iteration limits or stall detection trigger. **Restrictions enforced:** - MUST NOT create PR - MUST NOT push to remote -- MUST NOT invoke delivery-validator (handled by workflow) +- MUST NOT skip review loop --- @@ -318,7 +318,7 @@ A SubagentStop hook enforces the workflow sequence. When any agent completes, th **Enforced rules:** - Cannot skip deslop-work or test-coverage-checker -- Cannot skip review-orchestrator +- Cannot skip Phase 9 review loop - Cannot skip delivery-validator - Cannot skip docs-updater - Cannot create PR before `/ship` is invoked @@ -330,7 +330,7 @@ A SubagentStop hook enforces the workflow sequence. When any agent completes, th | Model | Agents | Why | |-------|--------|-----| -| **opus** | exploration-agent, planning-agent, implementation-agent, review-orchestrator | Complex reasoning, quality-critical phases | +| **opus** | exploration-agent, planning-agent, implementation-agent | Complex reasoning, quality-critical phases | | **sonnet** | task-discoverer, deslop-work, test-coverage-checker, delivery-validator, docs-updater, ci-fixer | Moderate reasoning, structured tasks | | **haiku** | worktree-manager, simple-fixer, ci-monitor | Mechanical execution, no judgment needed | diff --git a/docs/workflows/SHIP.md b/docs/workflows/SHIP.md index b16293d7..bc9b03cd 100644 --- a/docs/workflows/SHIP.md +++ b/docs/workflows/SHIP.md @@ -184,7 +184,7 @@ mutation($threadId: ID!) { ### Phase 7: Internal Review (Standalone Only) -**Skipped when called from `/next-task`** (review already completed by review-orchestrator). +**Skipped when called from `/next-task`** (review already completed by Phase 9 review loop). When standalone, launches core review passes in parallel: - Code quality (includes error handling) @@ -335,7 +335,7 @@ Outputs detailed logging for each phase. When called from `/next-task` (via `--state-file` argument): **Skipped phases:** -- Phase 7 (internal review) - Already done by review-orchestrator +- Phase 7 (internal review) - Already done by Phase 9 review loop - Deslop cleanup - Already done by deslop-work **Still runs:** diff --git a/package-lock.json b/package-lock.json index d5ea98a0..becfa391 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "awesome-slash", - "version": "2.8.3", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "awesome-slash", - "version": "2.8.3", + "version": "3.1.0", "license": "MIT", "dependencies": { "js-yaml": "^4.1.1" @@ -15,6 +15,7 @@ "awesome-slash": "bin/cli.js" }, "devDependencies": { + "@modelcontextprotocol/sdk": "^1.25.3", "jest": "^29.7.0" }, "engines": { @@ -518,6 +519,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -911,6 +925,46 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.25.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz", + "integrity": "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1054,6 +1108,55 @@ "dev": true, "license": "MIT" }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1249,6 +1352,31 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1325,6 +1453,47 @@ "dev": true, "license": "MIT" }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1476,6 +1645,30 @@ "dev": true, "license": "MIT" }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1483,6 +1676,44 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -1563,6 +1794,16 @@ "node": ">=0.10.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -1583,6 +1824,28 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.267", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", @@ -1610,6 +1873,16 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -1620,6 +1893,39 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1630,6 +1936,13 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", @@ -1654,6 +1967,39 @@ "node": ">=4" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1704,6 +2050,74 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1711,6 +2125,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -1734,6 +2165,28 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1748,6 +2201,26 @@ "node": ">=8" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1800,14 +2273,53 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/get-stream": { @@ -1845,6 +2357,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1862,6 +2387,19 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1875,6 +2413,17 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -1882,6 +2431,27 @@ "dev": true, "license": "MIT" }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -1892,6 +2462,23 @@ "node": ">=10.17.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==", + "dev": true, + "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/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -1941,6 +2528,16 @@ "dev": true, "license": "ISC" }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -1994,6 +2591,13 @@ "node": ">=0.12.0" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2694,6 +3298,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2733,6 +3347,20 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2835,6 +3463,39 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2856,6 +3517,33 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -2893,6 +3581,16 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -2930,6 +3628,42 @@ "node": ">=8" } }, + "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/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3030,6 +3764,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3067,6 +3811,17 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3097,6 +3852,16 @@ "node": ">= 6" } }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -3152,6 +3917,20 @@ "node": ">= 6" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -3169,6 +3948,48 @@ ], "license": "MIT" }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -3186,6 +4007,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -3240,6 +4071,30 @@ "node": ">=10" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -3250,6 +4105,60 @@ "semver": "bin/semver.js" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3273,6 +4182,82 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -3338,6 +4323,16 @@ "node": ">=10" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -3474,6 +4469,16 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3497,6 +4502,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -3504,6 +4524,16 @@ "dev": true, "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -3550,6 +4580,16 @@ "node": ">=10.12.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -3673,6 +4713,27 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } } } } diff --git a/package.json b/package.json index a094644f..5d23d458 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "js-yaml": "^4.1.1" }, "devDependencies": { + "@modelcontextprotocol/sdk": "^1.25.3", "jest": "^29.7.0" } } diff --git a/plugins/audit-project/commands/audit-project-agents.md b/plugins/audit-project/commands/audit-project-agents.md index 3ce925db..b6aba26b 100644 --- a/plugins/audit-project/commands/audit-project-agents.md +++ b/plugins/audit-project/commands/audit-project-agents.md @@ -4,6 +4,8 @@ This file contains detailed agent coordination for `/audit-project`. **Parent document**: `audit-project.md` +**Review Pass Definitions**: See `orchestrate-review` skill for canonical pass definitions (core + conditional). This command uses the same review passes but detects signals from project structure (not just changed files). + ## Agent Specialization ### File Filtering by Agent diff --git a/plugins/enhance/README.md b/plugins/enhance/README.md index 42041c9f..80636ddd 100644 --- a/plugins/enhance/README.md +++ b/plugins/enhance/README.md @@ -149,7 +149,7 @@ Each analyzer generates a markdown report: Can be invoked by: - Direct command: `/enhance:*` -- `review-orchestrator` during PR review +- Phase 9 review loop during workflow - `delivery-validator` before shipping - Individual analysis workflows diff --git a/plugins/enhance/agents/agent-enhancer.md b/plugins/enhance/agents/agent-enhancer.md index 8e4658fb..58365d65 100644 --- a/plugins/enhance/agents/agent-enhancer.md +++ b/plugins/enhance/agents/agent-enhancer.md @@ -319,7 +319,7 @@ This agent processes files... This agent can be invoked by: - `/enhance:agent` command -- `review-orchestrator` during PR review +- Phase 9 review loop during workflow - `delivery-validator` before shipping - Individual analysis workflows diff --git a/plugins/enhance/agents/docs-enhancer.md b/plugins/enhance/agents/docs-enhancer.md index 40e17bbd..f109f31a 100644 --- a/plugins/enhance/agents/docs-enhancer.md +++ b/plugins/enhance/agents/docs-enhancer.md @@ -313,7 +313,7 @@ configuration, troubleshooting, and examples] This agent can be invoked by: - `/enhance:docs` command -- `review-orchestrator` during PR review +- Phase 9 review loop during workflow - `delivery-validator` before shipping - Individual analysis workflows diff --git a/plugins/enhance/agents/plugin-enhancer.md b/plugins/enhance/agents/plugin-enhancer.md index 172f7e11..30bb7851 100644 --- a/plugins/enhance/agents/plugin-enhancer.md +++ b/plugins/enhance/agents/plugin-enhancer.md @@ -174,7 +174,7 @@ For HIGH certainty issues with available fixes: This agent can be invoked by: - `/enhance:plugin` command -- `review-orchestrator` during PR review +- Phase 9 review loop during workflow - `delivery-validator` before shipping ## Quality Multiplier diff --git a/plugins/enhance/agents/prompt-enhancer.md b/plugins/enhance/agents/prompt-enhancer.md index 962ca175..b89febbe 100644 --- a/plugins/enhance/agents/prompt-enhancer.md +++ b/plugins/enhance/agents/prompt-enhancer.md @@ -334,7 +334,7 @@ Respond with a JSON object: This agent can be invoked by: - `/enhance:prompt` command -- `review-orchestrator` during PR review +- Phase 9 review loop during workflow - Individual analysis workflows ## Quality Multiplier diff --git a/plugins/next-task/agents/delivery-validator.md b/plugins/next-task/agents/delivery-validator.md index f654e978..76d601fa 100644 --- a/plugins/next-task/agents/delivery-validator.md +++ b/plugins/next-task/agents/delivery-validator.md @@ -396,7 +396,7 @@ This agent is called: ✓ implementation-agent completed ✓ deslop-work ran on new code ✓ test-coverage-checker ran (advisory) -✓ review-orchestrator APPROVED (no open issues or override) +✓ Phase 9 review loop APPROVED (no open issues or override) ``` ### What This Agent MUST NOT Do @@ -417,7 +417,7 @@ implementation-agent ↓ Pre-review gates ↓ -review-orchestrator (MUST have approved) +Phase 9 review loop (MUST have approved) ↓ delivery-validator (YOU ARE HERE) ↓ diff --git a/plugins/next-task/agents/docs-updater.md b/plugins/next-task/agents/docs-updater.md index cdfa8ef2..c49b7e2f 100644 --- a/plugins/next-task/agents/docs-updater.md +++ b/plugins/next-task/agents/docs-updater.md @@ -359,7 +359,7 @@ This agent is called: ✓ implementation-agent completed ✓ deslop-work ran on new code ✓ test-coverage-checker ran (advisory) -✓ review-orchestrator APPROVED +✓ Phase 9 review loop APPROVED ✓ delivery-validator APPROVED ``` @@ -380,7 +380,7 @@ implementation-agent ↓ Pre-review gates ↓ -review-orchestrator (approved) +Phase 9 review loop (approved) ↓ delivery-validator (approved) ↓ diff --git a/plugins/next-task/agents/implementation-agent.md b/plugins/next-task/agents/implementation-agent.md index 97ff4029..df9da93a 100644 --- a/plugins/next-task/agents/implementation-agent.md +++ b/plugins/next-task/agents/implementation-agent.md @@ -363,7 +363,7 @@ implementation-agent (YOU ARE HERE) ↓ Pre-review gates: deslop-work + test-coverage-checker ↓ - review-orchestrator (must approve) + Phase 9 review loop (must approve) ↓ delivery-validator (must approve) ↓ @@ -409,7 +409,7 @@ ${gitLog} --- ⏸️ STOPPING HERE - SubagentStop hook will trigger pre-review gates → deslop-work + test-coverage-checker (parallel) - → review-orchestrator + → Phase 9 review loop → delivery-validator → docs-updater → /ship diff --git a/plugins/next-task/agents/review-orchestrator.md b/plugins/next-task/agents/review-orchestrator.md deleted file mode 100644 index 66f85ecd..00000000 --- a/plugins/next-task/agents/review-orchestrator.md +++ /dev/null @@ -1,811 +0,0 @@ ---- -name: review-orchestrator -description: Orchestrate deep review passes (code quality, security, performance, test coverage, plus specialists) until all non-false-positive issues are resolved. -tools: Task, Bash(git:*), Read, Write, Edit -model: opus ---- - -# Review Orchestrator Agent - -You coordinate multiple review passes, aggregate findings, and iterate -until no non-false-positive issues remain. - -If the platform does not allow nested subagents (Claude Code), you MUST -run all review passes yourself and MUST NOT call Task to spawn subagents. -On platforms that allow nested subagents (OpenCode/Codex), you MAY run -passes in parallel using Task. - -## Configuration - -```javascript -// Review loop with security limits and stall detection -const workflowState = require('${CLAUDE_PLUGIN_ROOT}'.replace(/\\/g, '/') + '/lib/state/workflow-state.js'); -const { getPlatformName } = require('${CLAUDE_PLUGIN_ROOT}'.replace(/\\/g, '/') + '/lib/platform/state-dir.js'); -const crypto = require('crypto'); - -const platform = getPlatformName(process.cwd()); -const supportsNestedSubagents = platform === 'opencode' || platform === 'codex'; -``` - - -## Phase 1: Get Changed Files - -```bash -# Get list of changed files -CHANGED_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null || git diff --name-only) -CHANGED_COUNT=$(echo "$CHANGED_FILES" | wc -l) - -echo "Files to review: $CHANGED_COUNT" -echo "$CHANGED_FILES" - -# Get diff stats -git diff --stat HEAD~1..HEAD 2>/dev/null || git diff --stat -``` - -## Phase 2: Start Review Phase - -```javascript -workflowState.setPhase('review-loop'); -``` - -## Resume Mode - -If invoked with `--resume`, reuse the existing review queue file from flow state -or the most recent queue in the platform state dir. Otherwise create a new queue. - -## Phase 3: Prepare Review Queue + Content Signals - -```javascript -const path = require('path'); -const fs = require('fs'); -const { getStateDirPath } = require('${CLAUDE_PLUGIN_ROOT}'.replace(/\\/g, '/') + '/lib/platform/state-dir.js'); - -const resumeRequested = (typeof ARGUMENTS !== 'undefined' && ARGUMENTS.includes('--resume')) - || process.env.REVIEW_RESUME === 'true'; - -let changedFiles = CHANGED_FILES.split('\n').filter(Boolean); -let changedFilesList = changedFiles.join(', '); -const stateDirPath = getStateDirPath(process.cwd()); -if (!fs.existsSync(stateDirPath)) { - fs.mkdirSync(stateDirPath, { recursive: true }); -} - -function findLatestQueue(dirPath) { - const files = fs.readdirSync(dirPath) - .filter(name => name.startsWith('review-queue-') && name.endsWith('.json')) - .map(name => ({ - name, - fullPath: path.join(dirPath, name), - mtime: fs.statSync(path.join(dirPath, name)).mtimeMs - })) - .sort((a, b) => b.mtime - a.mtime); - return files[0]?.fullPath || null; -} - -function safeReadJson(filePath) { - try { - return JSON.parse(fs.readFileSync(filePath, 'utf8')); - } catch (error) { - console.warn(`Review queue unreadable: ${filePath}. Starting fresh.`); - return null; - } -} - -let reviewQueuePath = null; -if (resumeRequested) { - const flow = workflowState.readFlow(); - const flowQueuePath = flow?.reviewQueue?.path; - if (flowQueuePath && fs.existsSync(flowQueuePath)) { - reviewQueuePath = flowQueuePath; - } else { - reviewQueuePath = findLatestQueue(stateDirPath); - } -} - -let isNewQueue = !reviewQueuePath; -if (!reviewQueuePath) { - reviewQueuePath = path.join(stateDirPath, `review-queue-${Date.now()}.json`); -} - -if (!isNewQueue && fs.existsSync(reviewQueuePath)) { - const existingQueue = safeReadJson(reviewQueuePath); - if (existingQueue) { - if (Array.isArray(existingQueue.scope?.files) && existingQueue.scope.files.length > 0) { - changedFiles = existingQueue.scope.files; - changedFilesList = changedFiles.join(', '); - } - } else { - isNewQueue = true; - } -} - -const normalizedFiles = changedFiles.map(file => file.replace(/\\/g, '/')); -const signals = { - hasDb: normalizedFiles.some(f => /(db|database|migrations?|schema|prisma|sequelize|typeorm|knex|sql)/i.test(f)), - hasApi: normalizedFiles.some(f => /(api|routes?|controllers?|handlers?|server|express|fastify|nestjs|koa|hapi)/i.test(f)), - hasFrontend: normalizedFiles.some(f => /\.(tsx|jsx|vue|svelte)$/.test(f)) || normalizedFiles.some(f => /(components?|pages|frontend|ui)/i.test(f)), - hasBackend: normalizedFiles.some(f => /(server|backend|services?|controllers?|domain|use-?cases?)/i.test(f)), - hasDevops: normalizedFiles.some(f => /(^|\/)(\.github\/workflows|\.circleci|\.gitlab-ci|Jenkinsfile|\.travis\.yml|azure-pipelines\.yml|bitbucket-pipelines\.yml|Dockerfile|docker\/|k8s|helm|terraform)/i.test(f)), - needsArchitecture: normalizedFiles.length > 20 -}; - -if (isNewQueue) { - const reviewQueue = { - status: 'open', - scope: { type: 'diff', files: changedFiles }, - passes: [], - items: [], - iteration: 0, - stallCount: 0, - updatedAt: new Date().toISOString() - }; - fs.writeFileSync(reviewQueuePath, JSON.stringify(reviewQueue, null, 2), 'utf8'); -} else { - const reviewQueue = safeReadJson(reviewQueuePath) || { - status: 'open', - scope: { type: 'diff', files: changedFiles }, - passes: [], - items: [], - iteration: 0, - stallCount: 0, - updatedAt: new Date().toISOString() - }; - reviewQueue.status = 'open'; - reviewQueue.updatedAt = new Date().toISOString(); - reviewQueue.resumedAt = new Date().toISOString(); - fs.writeFileSync(reviewQueuePath, JSON.stringify(reviewQueue, null, 2), 'utf8'); -} - -workflowState.updateFlow({ - reviewQueue: { - path: reviewQueuePath, - status: 'open', - scope: { type: 'diff', files: changedFiles }, - updatedAt: new Date().toISOString() - } -}); -``` - -## Phase 4: Launch Review Passes - -Launch core passes (code quality, security, performance, test coverage) plus conditional specialists. -If `supportsNestedSubagents` is false (Claude Code), run each pass yourself in-series. - -```javascript -const reviewPasses = [ - { - id: 'code-quality', - role: 'code quality reviewer', - focus: [ - 'Code style and consistency', - 'Best practices violations', - 'Potential bugs and logic errors', - 'Error handling and failure paths', - 'Maintainability issues', - 'Code duplication' - ] - }, - { - id: 'security', - role: 'security reviewer', - focus: [ - 'Auth/authz flaws', - 'Input validation and output encoding', - 'Injection risks (SQL/command/template)', - 'Secrets exposure and unsafe configs', - 'Insecure defaults' - ] - }, - { - id: 'performance', - role: 'performance reviewer', - focus: [ - 'N+1 queries and inefficient loops', - 'Blocking operations in async paths', - 'Hot path inefficiencies', - 'Memory leaks or unnecessary allocations' - ] - }, - { - id: 'test-coverage', - role: 'test coverage reviewer', - focus: [ - 'New code without corresponding tests', - 'Missing edge case coverage', - 'Test quality (meaningful assertions)', - 'Integration test needs', - 'Mock/stub appropriateness' - ] - } -]; - -if (signals.hasDb) { - reviewPasses.push({ - id: 'database', - role: 'database specialist', - focus: ['Query performance', 'Indexes and transactions', 'Migration safety', 'Data integrity'] - }); -} - -if (signals.needsArchitecture) { - reviewPasses.push({ - id: 'architecture', - role: 'architecture reviewer', - focus: ['Module boundaries', 'Dependency direction', 'Cross-layer coupling', 'Consistency of patterns'] - }); -} - -if (signals.hasApi) { - reviewPasses.push({ - id: 'api', - role: 'api designer', - focus: ['REST conventions', 'Error/status consistency', 'Pagination/filters', 'Versioning concerns'] - }); -} - -if (signals.hasFrontend) { - reviewPasses.push({ - id: 'frontend', - role: 'frontend specialist', - focus: ['Component boundaries', 'State management patterns', 'Accessibility', 'Render performance'] - }); -} - -if (signals.hasBackend) { - reviewPasses.push({ - id: 'backend', - role: 'backend specialist', - focus: ['Service boundaries', 'Domain logic correctness', 'Concurrency and idempotency', 'Background job safety'] - }); -} - -if (signals.hasDevops) { - reviewPasses.push({ - id: 'devops', - role: 'devops reviewer', - focus: ['CI/CD safety', 'Secrets handling', 'Build/test pipelines', 'Deploy config correctness'] - }); -} - -async function runReviewPasses(filesList) { - const list = Array.isArray(filesList) ? filesList.join(', ') : String(filesList || '').trim(); - - if (supportsNestedSubagents) { - const reviewPromises = reviewPasses.map(pass => Task({ - subagent_type: "review", - prompt: `Role: ${pass.role}. - -Review the following files: -${list} - -Focus on: -${pass.focus.map(item => `- ${item}`).join('\n')} - -Write findings to ${reviewQueuePath} (append JSONL if possible). If you cannot write files, return JSON only. - -Return JSON ONLY in this format: -{ - "pass": "${pass.id}", - "findings": [ - { - "file": "path/to/file.ts", - "line": 42, - "severity": "critical|high|medium|low", - "category": "${pass.id}", - "description": "Issue description", - "suggestion": "How to fix", - "confidence": "high|medium|low", - "falsePositive": false - } - ] -}` - })); - - return Promise.all(reviewPromises); - } - - // Claude Code: nested subagents not allowed. Perform each pass yourself. - return reviewPasses.map(pass => selfReviewPass(pass, list)); -} - -function selfReviewPass(pass, filesList) { - // Read diffs/files and perform a focused review yourself. - // You MUST return JSON in the same format as subagent results. - return { - pass: pass.id, - findings: [ - // Populate with real findings you identify. - // If none, return an empty array. - ] - }; -} - -let results = await runReviewPasses(changedFilesList); -``` - -## Phase 5: Aggregate Results + Update Queue - -```javascript -function aggregateFindings(results) { - const items = []; - const validSeverities = new Set(['critical', 'high', 'medium', 'low']); - - for (const result of results) { - const pass = result.pass || 'unknown'; - const findings = Array.isArray(result.findings) ? result.findings : []; - for (const finding of findings) { - const severity = validSeverities.has(finding.severity) ? finding.severity : 'low'; - items.push({ - id: `${pass}:${finding.file}:${finding.line}:${finding.description}`, - pass, - ...finding, - severity, - status: finding.falsePositive ? 'false-positive' : 'open' - }); - } - } - - const seen = new Set(); - const deduped = items.filter(item => { - const key = `${item.pass}:${item.file}:${item.line}:${item.description}`; - if (seen.has(key)) return false; - seen.add(key); - return true; - }); - - const bySeverity = { critical: [], high: [], medium: [], low: [] }; - for (const item of deduped) { - if (!item.falsePositive) { - const bucket = bySeverity[item.severity] ? item.severity : 'low'; - bySeverity[bucket].push(item); - } - } - - const totals = { - critical: bySeverity.critical.length, - high: bySeverity.high.length, - medium: bySeverity.medium.length, - low: bySeverity.low.length - }; - - return { - items: deduped, - bySeverity, - totals, - openCount: totals.critical + totals.high + totals.medium + totals.low - }; -} - -let findings = aggregateFindings(results); - -const reviewQueueState = safeReadJson(reviewQueuePath) || { - status: 'open', - scope: { type: 'diff', files: changedFiles }, - passes: [], - items: [], - iteration: 0, - stallCount: 0, - updatedAt: new Date().toISOString() -}; -reviewQueueState.passes = reviewPasses.map(pass => pass.id); -reviewQueueState.items = findings.items; -reviewQueueState.updatedAt = new Date().toISOString(); -fs.writeFileSync(reviewQueuePath, JSON.stringify(reviewQueueState, null, 2), 'utf8'); - -if (findings.openCount === 0) { - const resolvedQueue = safeReadJson(reviewQueuePath) || reviewQueueState; - resolvedQueue.status = 'resolved'; - resolvedQueue.updatedAt = new Date().toISOString(); - fs.writeFileSync(reviewQueuePath, JSON.stringify(resolvedQueue, null, 2), 'utf8'); - if (fs.existsSync(reviewQueuePath)) { - try { - fs.unlinkSync(reviewQueuePath); - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; - } - } - } - workflowState.updateFlow({ - reviewQueue: { - path: reviewQueuePath, - status: 'resolved', - updatedAt: new Date().toISOString() - } - }); -} -``` - -## Phase 6: Log Results - -```javascript -console.log(`Found: ${findings.openCount} open issues (${findings.totals.critical} critical, ${findings.totals.high} high, ${findings.totals.medium} medium, ${findings.totals.low} low)`); -``` - -## Phase 7: Report Findings - -```javascript -let iteration = 1; -``` - -```markdown -## Review Results - Iteration ${iteration} - -### Summary -| Pass | Open Findings | -|------|---------------| -${reviewPasses.map(pass => `| ${pass.id} | ${findings.items.filter(i => i.pass === pass.id && !i.falsePositive).length} |`).join('\n')} -| **Total** | **${findings.openCount}** | - -### Critical Issues -${findings.bySeverity.critical.map(i => `- **${i.file}:${i.line}** - ${i.description}`).join('\n')} - -### High Issues -${findings.bySeverity.high.map(i => `- **${i.file}:${i.line}** - ${i.description}`).join('\n')} - -### Medium Issues -${findings.bySeverity.medium.map(i => `- **${i.file}:${i.line}** - ${i.description}`).join('\n')} - -### Low Issues -${findings.bySeverity.low.map(i => `- **${i.file}:${i.line}** - ${i.description}`).join('\n')} -``` - -## Phase 8: Iteration Loop (Until Approved) - -```javascript -const MAX_ITERATIONS = Number(process.env.REVIEW_MAX_ITERATIONS || 5); -const MAX_STALLS = Number(process.env.REVIEW_MAX_STALLS || 2); -let lastHash = null; -let stallCount = 0; - -function hashOpenItems(items) { - const openItems = items - .filter(item => !item.falsePositive) - .map(item => ({ - pass: item.pass, - file: item.file, - line: item.line, - severity: item.severity, - description: item.description - })) - .sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))); - return crypto.createHash('sha256').update(JSON.stringify(openItems)).digest('hex'); -} - -// Loop until no non-false-positive issues remain (bounded by security limits) -while (findings.openCount > 0) { - console.log(`\n## Review Iteration ${iteration}`); - console.log(`Fixing ${findings.openCount} issues across all severities...`); - - const orderedIssues = [ - ...findings.bySeverity.critical, - ...findings.bySeverity.high, - ...findings.bySeverity.medium, - ...findings.bySeverity.low - ]; - - for (const issue of orderedIssues) { - if (issue.falsePositive) { - continue; - } - console.log(`Fixing ${issue.severity}: ${issue.file}:${issue.line} - ${issue.description}`); - await fixIssue(issue); - } - - // Commit fixes - await exec(`git add . && git commit -m "fix: address review feedback (iteration ${iteration})"`); - - // ========================================================= - // POST-ITERATION DESLOP: Clean any slop introduced by fixes - // ========================================================= - const fixedFiles = await exec('git diff --name-only HEAD~1'); - - console.log(`\n### Post-Iteration Deslop`); - console.log(`Cleaning slop from ${fixedFiles.split('\n').length} fixed files...`); - - if (supportsNestedSubagents) { - await Task({ - subagent_type: "next-task:deslop-work", - model: "sonnet", - prompt: `Clean AI slop introduced by review fixes. - -Files to analyze: ${fixedFiles} - -This is a post-iteration cleanup. Report any new slop patterns -(console.log, debug statements, placeholder text, etc.) that -were accidentally introduced while fixing review issues. - -Do NOT auto-fix - just report for the next iteration.` - }); - } else { - // Claude Code: run an inline slop scan and fix obvious artifacts. - // Focus on: console.log/debugger, TODO/FIXME placeholders, lorem/placeholder text, - // unused imports from trial-and-error, commented-out debug blocks. - // Apply safe fixes directly and leave notes for ambiguous cases. - } - // ========================================================= - - // Log iteration progress - console.log(`Iteration ${iteration} complete. Re-checking review passes...`); - - // Re-run review passes on changed files - const changedInIteration = await exec('git diff --name-only HEAD~1'); - results = await runReviewPasses(changedInIteration || changedFilesList); - findings = aggregateFindings(results); - - const currentHash = hashOpenItems(findings.items); - stallCount = currentHash === lastHash ? stallCount + 1 : 0; - lastHash = currentHash; - - // Refresh review queue file - const refreshedQueue = safeReadJson(reviewQueuePath) || { - status: 'open', - scope: { type: 'diff', files: changedFiles }, - passes: reviewPasses.map(pass => pass.id), - items: [], - iteration: 0, - stallCount: 0, - updatedAt: new Date().toISOString() - }; - refreshedQueue.items = findings.items; - refreshedQueue.passes = reviewPasses.map(pass => pass.id); - refreshedQueue.iteration = iteration; - refreshedQueue.stallCount = stallCount; - refreshedQueue.updatedAt = new Date().toISOString(); - - if (findings.openCount === 0) { - if (fs.existsSync(reviewQueuePath)) { - try { - fs.unlinkSync(reviewQueuePath); - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; - } - } - } - workflowState.updateFlow({ - reviewQueue: { - path: reviewQueuePath, - status: 'resolved', - updatedAt: new Date().toISOString() - } - }); - break; - } - - const limitReached = iteration >= MAX_ITERATIONS; - const stalled = stallCount >= MAX_STALLS; - if (limitReached || stalled) { - const reason = limitReached ? 'iteration-limit' : 'stall-detected'; - refreshedQueue.status = 'blocked'; - refreshedQueue.blockedReason = reason; - refreshedQueue.blockedAt = new Date().toISOString(); - fs.writeFileSync(reviewQueuePath, JSON.stringify(refreshedQueue, null, 2), 'utf8'); - - workflowState.updateFlow({ - reviewResult: { - approved: false, - blocked: true, - reason, - iteration, - remaining: { - critical: findings.bySeverity.critical.length, - high: findings.bySeverity.high.length, - medium: findings.bySeverity.medium.length, - low: findings.bySeverity.low.length - }, - reviewQueuePath - }, - reviewQueue: { - path: reviewQueuePath, - status: 'blocked', - updatedAt: new Date().toISOString() - } - }); - - console.log(`Review blocked (${reason}). Hand back for decision.`); - break; - } - - fs.writeFileSync(reviewQueuePath, JSON.stringify(refreshedQueue, null, 2), 'utf8'); - - iteration++; -} -``` - -## Phase 9: Final Status - -```javascript -if (findings.openCount > 0) { - console.log("\n## ⚠ Review Blocked"); - console.log(`Queue: ${reviewQueuePath}`); - console.log("Hand back to next-task orchestrator for decision."); - return; -} - -// When we exit the loop with zero open issues -console.log("\n## ✓ Review Approved"); -console.log("All review issues resolved."); -console.log(`Completed after ${iteration - 1} iteration(s).`); - -// Update flow with review result -workflowState.updateFlow({ - reviewResult: { - approved: true, - iterations: iteration - 1, - remainingIssues: 0, - reviewQueuePath - } -}); -``` - -## Fix Issue Helper - -```javascript -async function fixIssue(issue) { - const fs = require('fs'); - // Read the file - const content = fs.readFileSync(issue.file, 'utf8'); - const lines = content.split('\n'); - - // Apply fix based on category - switch (issue.category) { - case 'code-quality': - // Fix logic, style, or error handling issues - break; - case 'security': - // Apply security fix - break; - case 'performance': - // Apply performance fix - break; - case 'test-coverage': - // Add or improve tests - break; - case 'database': - case 'api': - case 'frontend': - case 'backend': - case 'devops': - case 'architecture': - // Apply domain-specific fix - break; - } -} - -// Use runReviewPasses(...) for initial and re-review passes. -``` - -## Output Format (JSON) - -```json -{ - "status": "approved", - "blocked": false, - "iterations": 2, - "passes": { - "code-quality": { - "status": "completed", - "findings": { "critical": 0, "high": 1, "medium": 0, "low": 0 } - }, - "security": { - "status": "completed", - "findings": { "critical": 0, "high": 0, "medium": 1, "low": 0 } - }, - "performance": { - "status": "completed", - "findings": { "critical": 0, "high": 0, "medium": 0, "low": 1 } - }, - "test-coverage": { - "status": "completed", - "findings": { "critical": 0, "high": 0, "medium": 0, "low": 1 } - }, - "database": { - "status": "skipped", - "findings": { "critical": 0, "high": 0, "medium": 0, "low": 0 } - } - }, - "summary": { - "totalOpen": 0, - "fixedIssues": 6, - "falsePositives": 1 - }, - "reviewQueue": { - "path": "{state-dir}/review-queue-20260125.json", - "status": "resolved", - "cleaned": true - }, - "fixedIssues": [ - { - "file": "src/api/client.ts", - "line": 42, - "severity": "critical", - "category": "security", - "description": "Hardcoded API key in source", - "fixApplied": "Moved to environment variable" - }, - { - "file": "src/utils/parser.ts", - "line": 87, - "severity": "high", - "category": "code-quality", - "description": "Unhandled null case in parse function", - "fixApplied": "Added null check with early return" - } - ], - "notesForPR": [] -} -``` - -## ⛔ WORKFLOW GATES - READ CAREFULLY - -### Prerequisites (MUST be true before this agent runs) - -``` -✓ implementation-agent completed -✓ deslop-work ran on new code -✓ test-coverage-checker ran (advisory) -``` - -### What This Agent MUST NOT Do - -``` -╔══════════════════════════════════════════════════════════════════╗ -║ ⛔ DO NOT CREATE A PULL REQUEST ║ -║ ⛔ DO NOT PUSH TO REMOTE ║ -║ ⛔ DO NOT SKIP TO SHIPPING ║ -║ ⛔ DO NOT INVOKE delivery-validator YOURSELF ║ -╚══════════════════════════════════════════════════════════════════╝ -``` - -### Required Workflow Position - -``` -implementation-agent - ↓ - Pre-review gates (deslop-work + test-coverage-checker) - ↓ -review-orchestrator (YOU ARE HERE) - ↓ - [STOP WHEN APPROVED] - ↓ - SubagentStop hook triggers automatically - ↓ - delivery-validator (must approve) - ↓ - docs-updater - ↓ - /ship command (creates PR) -``` - -### Required Handoff - -When review is APPROVED (no non-false-positive issues remain), you MUST: -1. Update workflow state with `reviewApproved: true` -2. Output the approval summary -3. **STOP** - the SubagentStop hook will trigger delivery-validator - -If review is BLOCKED (iteration limit or stall), you MUST: -1. Update workflow state with `reviewResult.blocked: true` and the queue path -2. Report remaining issues and why they are blocked -3. **STOP** - /next-task will decide whether to resume or override - -## Success Criteria - -- Core review passes run in parallel: code quality, security, performance, test coverage -- Conditional specialists run when signals indicate relevance -- Results aggregated with severity counts and written to the review queue file -- All non-false-positive issues are fixed -- **deslop-work runs after each iteration** to clean slop from fixes -- Iteration continues until the queue is empty or limits trigger a block -- Queue file removed when review completes -- State updated with agent results -- **STOP after approval** - SubagentStop hook advances to delivery-validator - -## Model Choice: Opus - -This agent uses **opus** because: -- Coordinates multiple specialized review agents -- Must aggregate and prioritize findings intelligently -- Fixing issues requires understanding code context -- Iteration decisions need judgment about when to stop diff --git a/plugins/next-task/agents/test-coverage-checker.md b/plugins/next-task/agents/test-coverage-checker.md index 731f51b3..de1160c3 100644 --- a/plugins/next-task/agents/test-coverage-checker.md +++ b/plugins/next-task/agents/test-coverage-checker.md @@ -416,7 +416,7 @@ ${summary.recommendation} ## Behavior - **Advisory only** - Does NOT block workflow -- Reports coverage gaps to review-orchestrator +- Reports coverage gaps to Phase 9 review loop - Suggestions included in PR description - Implementation-agent may optionally add tests based on findings @@ -424,7 +424,7 @@ ${summary.recommendation} This agent is called: 1. **Before first review round** - In parallel with deslop-work -2. Results passed to review-orchestrator for context +2. Results passed to Phase 9 review loop for context ## Success Criteria diff --git a/plugins/next-task/commands/next-task.md b/plugins/next-task/commands/next-task.md index d4bf3a1e..79c8645d 100644 --- a/plugins/next-task/commands/next-task.md +++ b/plugins/next-task/commands/next-task.md @@ -85,7 +85,7 @@ Implementation → Pre-Review Gates → Review Loop → Delivery Validation ║ ↓ MUST trigger ║ ║ 2. deslop-work + test-coverage-checker (parallel) ║ ║ ↓ MUST trigger ║ -║ 3. review-orchestrator (MUST approve - no open issues or override) ║ +║ 3. Phase 9 review loop (MUST approve - no open issues or override) ║ ║ ↓ MUST trigger (only if approved) ║ ║ 4. delivery-validator (MUST approve - tests pass, build passes) ║ ║ ↓ MUST trigger (only if approved) ║ @@ -97,7 +97,7 @@ Implementation → Pre-Review Gates → Review Loop → Delivery Validation ║ ║ ║ ⛔ NO AGENT may create a PR - only /ship creates PRs ║ ║ ⛔ NO AGENT may push to remote - only /ship pushes ║ -║ ⛔ NO AGENT may skip the review-orchestrator ║ +║ ⛔ NO AGENT may skip the Phase 9 review loop ║ ║ ⛔ NO AGENT may skip the delivery-validator ║ ║ ⛔ NO AGENT may skip the docs-updater ║ ║ ⛔ NO AGENT may skip workflow-status.json updates after each phase ║ @@ -107,14 +107,14 @@ Implementation → Pre-Review Gates → Review Loop → Delivery Validation ## Review Decision Gate (Blocked/Resume) -If `review-orchestrator` exits with `reviewResult.blocked: true`, the /next-task orchestrator MUST decide the next action (no user prompt). +If Phase 9 review loop exits with `reviewResult.blocked: true`, the orchestrator MUST decide the next action (no user prompt). Use the review queue file (`flow.reviewQueue.path`) to inspect open items and their `pass` values. Decision rules: -1. **If any open issue is critical/high OR any open issue is from security/performance/devops/database/api/backend/architecture** → re-run review-orchestrator with `--resume`. +1. **If any open issue is critical/high OR any open issue is from security/performance/devops/database/api/backend/architecture** → re-run Phase 9 review loop. 2. **If all open issues are medium/low and only code-quality/test-coverage** → you may override and continue if the issues are non-blocking. -3. **If unclear** → re-run with `--resume`. +3. **If unclear** → re-run Phase 9 review loop. When overriding, update flow: ```javascript @@ -646,20 +646,23 @@ await Promise.all([ ## Phase 9: Review Loop -→ **Agent**: `next-task:review-orchestrator` (opus) +MANDATORY: Follow the orchestrate-review skill exactly. DO NOT skip. DO NOT improvise. + +The skill contains all implementation details: +- Review pass definitions +- Signal detection patterns +- Task spawning logic +- Finding aggregation +- Iteration loop algorithm +- Stall detection ```javascript workflowState.startPhase('review-loop'); -await Task({ - subagent_type: "next-task:review-orchestrator", - model: "opus", - prompt: `Orchestrate deep review. Fix all non-false-positive issues. Max ${policy.maxReviewIterations || 5} iterations.` -}); +// FOLLOW THE ORCHESTRATE-REVIEW SKILL EXACTLY +// All implementation details are in: plugins/next-task/skills/orchestrate-review/SKILL.md -// Runs a deslop pass after each iteration to clean fixes. -// On Claude Code (no nested subagents), review-orchestrator performs inline passes. -// → SubagentStop hook triggers delivery validation when approved (or returns control if blocked) +// SubagentStop hook triggers delivery validation when approved (or returns control if blocked) ``` ## Phase 10: Delivery Validation diff --git a/plugins/next-task/hooks/hooks.json b/plugins/next-task/hooks/hooks.json index ea493b55..8e2bbe39 100644 --- a/plugins/next-task/hooks/hooks.json +++ b/plugins/next-task/hooks/hooks.json @@ -5,7 +5,7 @@ "hooks": [ { "type": "prompt", - "prompt": "## WORKFLOW ENFORCEMENT - SubagentStop Hook\n\nA subagent has completed. You MUST determine and execute the next workflow phase.\n\n### Check Workflow State\n\n```javascript\nconst state = workflowState.readState();\nconst completedAgent = state.phases.current;\nconst agentResult = // from the subagent output\n```\n\n### Decision Tree (MUST FOLLOW)\n\n1. **If implementation-agent completed**:\n - MUST run deslop-work AND test-coverage-checker (parallel)\n - ```javascript\n await Promise.all([\n Task({ subagent_type: 'next-task:deslop-work' }),\n Task({ subagent_type: 'next-task:test-coverage-checker' })\n ]);\n ```\n - Then MUST run review-orchestrator\n\n2. **If pre-review gates completed**:\n - MUST run review-orchestrator\n - ```javascript\n await Task({ subagent_type: 'next-task:review-orchestrator', model: 'opus' });\n ```\n\n3. **If review-orchestrator completed**:\n - Check if approved: `agentResult.approved === true`\n - If APPROVED: MUST run delivery-validator\n ```javascript\n await Task({ subagent_type: 'next-task:delivery-validator' });\n ```\n - If NOT APPROVED: MUST run deslop-work on fixes, then retry review\n\n4. **If delivery-validator completed**:\n - Check if approved: `agentResult.approved === true`\n - If APPROVED: MUST run docs-updater\n ```javascript\n await Task({ subagent_type: 'next-task:docs-updater' });\n ```\n - If NOT APPROVED: Return to implementation with fix instructions\n\n5. **If docs-updater completed**:\n - MUST invoke /ship command\n - ```javascript\n await Skill({ skill: 'ship:ship' });\n ```\n\n### CRITICAL ENFORCEMENT\n\n```\n╔══════════════════════════════════════════════════════════════════╗\n║ ⛔ NEVER skip deslop-work or test-coverage-checker ║\n║ ⛔ NEVER skip review-orchestrator ║\n║ ⛔ NEVER skip delivery-validator ║\n║ ⛔ NEVER skip docs-updater ║\n║ ⛔ NEVER create PR before /ship is invoked ║\n║ ⛔ NEVER push to remote before /ship is invoked ║\n╚══════════════════════════════════════════════════════════════════╝\n```\n\n### Workflow Sequence (MANDATORY)\n\n```\nimplementation-agent\n ↓ (SubagentStop)\npre-review gates (deslop-work + test-coverage-checker)\n ↓ (SubagentStop)\nreview-orchestrator\n ↓ (SubagentStop, if approved)\ndelivery-validator\n ↓ (SubagentStop, if approved)\ndocs-updater\n ↓ (SubagentStop)\n/ship command\n```\n\nReturn: {\"ok\": true, \"nextPhase\": \"\"}" + "prompt": "## WORKFLOW ENFORCEMENT - SubagentStop Hook\n\nA subagent has completed. You MUST determine and execute the next workflow phase.\n\n### Check Workflow State\n\n```javascript\nconst state = workflowState.readState();\nconst completedAgent = state.phases.current;\nconst agentResult = // from the subagent output\n```\n\n### Decision Tree (MUST FOLLOW)\n\n1. **If implementation-agent completed**:\n - MUST run deslop-work AND test-coverage-checker (parallel)\n - ```javascript\n await Promise.all([\n Task({ subagent_type: 'next-task:deslop-work' }),\n Task({ subagent_type: 'next-task:test-coverage-checker' })\n ]);\n ```\n - Then Phase 9 (review loop) runs inline\n\n2. **If pre-review gates completed**:\n - Phase 9 (review loop) runs INLINE in main orchestrator\n - Review spawns parallel Task agents for each review pass\n - After review completes, MUST run delivery-validator\n - ```javascript\n await Task({ subagent_type: 'next-task:delivery-validator' });\n ```\n\n4. **If delivery-validator completed**:\n - Check if approved: `agentResult.approved === true`\n - If APPROVED: MUST run docs-updater\n ```javascript\n await Task({ subagent_type: 'next-task:docs-updater' });\n ```\n - If NOT APPROVED: Return to implementation with fix instructions\n\n5. **If docs-updater completed**:\n - MUST invoke /ship command\n - ```javascript\n await Skill({ skill: 'ship:ship' });\n ```\n\n### CRITICAL ENFORCEMENT\n\nNEVER skip deslop-work or test-coverage-checker.\nNEVER skip Phase 9 review loop.\nNEVER skip delivery-validator.\nNEVER skip docs-updater.\nNEVER create PR before /ship is invoked.\nNEVER push to remote before /ship is invoked.\n\n### Workflow Sequence (MANDATORY)\n\n```\nimplementation-agent\n ↓ (SubagentStop)\npre-review gates (deslop-work + test-coverage-checker)\n ↓ (SubagentStop)\nPhase 9: review loop (inline in main orchestrator)\n ↓ (SubagentStop)\ndelivery-validator\n ↓ (SubagentStop, if approved)\ndocs-updater\n ↓ (SubagentStop)\n/ship command\n```\n\nReturn: {\"ok\": true, \"nextPhase\": \"\"}" } ] } diff --git a/plugins/next-task/skills/orchestrate-review/SKILL.md b/plugins/next-task/skills/orchestrate-review/SKILL.md new file mode 100644 index 00000000..bbfb705a --- /dev/null +++ b/plugins/next-task/skills/orchestrate-review/SKILL.md @@ -0,0 +1,228 @@ +--- +name: orchestrate-review +description: "Use when user asks to \"deep review the code\", \"thorough code review\", \"multi-pass review\", or when orchestrating Phase 9 review loop. Provides review pass definitions (code quality, security, performance, test coverage, specialists), signal detection patterns, and iteration algorithms." +user-invocable: false +metadata: + short-description: "Multi-pass code review orchestration" +--- + +# Orchestrate Review + +Multi-pass code review with parallel Task agents, finding aggregation, and iteration until clean. + +## Scope-Based Specialist Selection + +Select conditional specialists based on the review scope: +- **User request**: Detect signals from content user refers to (files, directory, module) +- **Workflow (Phase 9)**: Detect signals from changed files only +- **Project audit**: Detect signals from project structure as a whole + +## Review Passes + +Spawn parallel `general-purpose` Task agents (model: `sonnet`), one per pass: + +### Core (Always) +```javascript +const corePasses = [ + { id: 'code-quality', role: 'code quality reviewer', + focus: ['Style and consistency', 'Best practices', 'Bugs and logic errors', 'Error handling', 'Maintainability', 'Duplication'] }, + { id: 'security', role: 'security reviewer', + focus: ['Auth/authz flaws', 'Input validation', 'Injection risks', 'Secrets exposure', 'Insecure defaults'] }, + { id: 'performance', role: 'performance reviewer', + focus: ['N+1 queries', 'Blocking operations', 'Hot path inefficiencies', 'Memory leaks'] }, + { id: 'test-coverage', role: 'test coverage reviewer', + focus: ['Missing tests', 'Edge case coverage', 'Test quality', 'Integration needs', 'Mock appropriateness'] } +]; +``` + +### Conditional (Signal-Based) +```javascript +if (signals.hasDb) passes.push({ id: 'database', role: 'database specialist', + focus: ['Query performance', 'Indexes/transactions', 'Migration safety', 'Data integrity'] }); +if (signals.needsArchitecture) passes.push({ id: 'architecture', role: 'architecture reviewer', + focus: ['Module boundaries', 'Dependency direction', 'Cross-layer coupling', 'Pattern consistency'] }); +if (signals.hasApi) passes.push({ id: 'api', role: 'api designer', + focus: ['REST conventions', 'Error/status consistency', 'Pagination/filters', 'Versioning'] }); +if (signals.hasFrontend) passes.push({ id: 'frontend', role: 'frontend specialist', + focus: ['Component boundaries', 'State management', 'Accessibility', 'Render performance'] }); +if (signals.hasBackend) passes.push({ id: 'backend', role: 'backend specialist', + focus: ['Service boundaries', 'Domain logic', 'Concurrency/idempotency', 'Background job safety'] }); +if (signals.hasDevops) passes.push({ id: 'devops', role: 'devops reviewer', + focus: ['CI/CD safety', 'Secrets handling', 'Build/test pipelines', 'Deploy config'] }); +``` + +## Signal Detection + +```javascript +const signals = { + hasDb: files.some(f => /(db|migrations?|schema|prisma|typeorm|sql)/i.test(f)), + hasApi: files.some(f => /(api|routes?|controllers?|handlers?)/i.test(f)), + hasFrontend: files.some(f => /\.(tsx|jsx|vue|svelte)$/.test(f)), + hasBackend: files.some(f => /(server|backend|services?|domain)/i.test(f)), + hasDevops: files.some(f => /(\.github\/workflows|Dockerfile|k8s|terraform)/i.test(f)), + needsArchitecture: files.length > 20 // 20+ files typically indicates cross-module changes +}; +``` + +## Task Prompt Template + +``` +You are a ${pass.role}. Review these changed files: +${files.join('\n')} + +Focus: ${pass.focus.map(f => `- ${f}`).join('\n')} + +Return JSON: +{ + "pass": "${pass.id}", + "findings": [{ + "file": "path.ts", + "line": 42, + "severity": "critical|high|medium|low", + "description": "Issue", + "suggestion": "Fix", + "confidence": "high|medium|low", + "falsePositive": false + }] +} + +Example findings (diverse passes and severities): + +// Security - high severity +{ "file": "src/auth/login.ts", "line": 89, "severity": "high", + "description": "Password comparison uses timing-vulnerable string equality", + "suggestion": "Use crypto.timingSafeEqual() instead of ===", + "confidence": "high", "falsePositive": false } + +// Code quality - medium severity +{ "file": "src/utils/helpers.ts", "line": 45, "severity": "medium", + "description": "Duplicated validation logic exists in src/api/validators.ts:23", + "suggestion": "Extract to shared lib/validation.ts", + "confidence": "high", "falsePositive": false } + +// Performance - low severity +{ "file": "src/config.ts", "line": 12, "severity": "low", + "description": "Magic number 3600 should be named constant", + "suggestion": "const CACHE_TTL_SECONDS = 3600;", + "confidence": "medium", "falsePositive": false } + +// False positive example +{ "file": "src/crypto/hash.ts", "line": 78, "severity": "high", + "description": "Non-constant time comparison", + "suggestion": "N/A - intentional for non-secret data", + "confidence": "low", "falsePositive": true } + +Report all issues with confidence >= medium. Empty findings array if clean. +``` + +## Aggregation + +```javascript +function aggregateFindings(results) { + const items = []; + for (const {pass, findings = []} of results) { + for (const f of findings) { + items.push({ + id: `${pass}:${f.file}:${f.line}:${f.description}`, + pass, ...f, + status: f.falsePositive ? 'false-positive' : 'open' + }); + } + } + + // Deduplicate by id + const deduped = [...new Map(items.map(i => [i.id, i])).values()]; + + // Group by severity + const bySeverity = {critical: [], high: [], medium: [], low: []}; + deduped.forEach(i => !i.falsePositive && bySeverity[i.severity || 'low'].push(i)); + + const totals = Object.fromEntries(Object.entries(bySeverity).map(([k, v]) => [k, v.length])); + + return { + items: deduped, + bySeverity, + totals, + openCount: Object.values(totals).reduce((a, b) => a + b, 0) + }; +} +``` + +## Iteration Loop + +**Security Note**: Fixes are applied by the orchestrator using standard Edit tool permissions. Critical/high severity findings should be reviewed before applying - do not blindly apply LLM-suggested fixes to security-sensitive code. The orchestrator validates each fix against the original issue. + +```javascript +// 5 iterations balances thoroughness vs cost; 2 stalls indicates fixes aren't progressing +const MAX_ITERATIONS = 5, MAX_STALLS = 2; +let iteration = 1, stallCount = 0, lastHash = null; + +while (iteration <= MAX_ITERATIONS) { + // 1. Spawn parallel Task agents + const results = await Promise.all(passes.map(pass => Task({ + subagent_type: 'general-purpose', + model: 'sonnet', + prompt: /* see template above */ + }))); + + // 2. Aggregate findings + const findings = aggregateFindings(results); + + // 3. Check if done + if (findings.openCount === 0) { + workflowState.updateFlow({ reviewResult: { approved: true, iterations: iteration } }); + break; + } + + // 4. Fix issues (severity order: critical → high → medium → low) + // Orchestrator reviews each suggestion before applying via Edit tool + for (const issue of [...findings.bySeverity.critical, ...findings.bySeverity.high, + ...findings.bySeverity.medium, ...findings.bySeverity.low]) { + if (!issue.falsePositive) { + // Read file, locate issue.line, validate suggestion, apply via Edit tool + // For complex fixes, use simple-fixer agent pattern + } + } + + // 5. Commit + exec(`git add . && git commit -m "fix: review feedback (iteration ${iteration})"`); + + // 6. Post-iteration deslop + Task({ subagent_type: 'next-task:deslop-work', model: 'sonnet' }); + + // 7. Stall detection + const hash = crypto.createHash('sha256') + .update(JSON.stringify(findings.items.filter(i => !i.falsePositive))) + .digest('hex'); + stallCount = hash === lastHash ? stallCount + 1 : 0; + lastHash = hash; + + // 8. Check limits + if (stallCount >= MAX_STALLS || iteration >= MAX_ITERATIONS) { + workflowState.updateFlow({ + reviewResult: { approved: false, blocked: true, + reason: stallCount >= MAX_STALLS ? 'stall-detected' : 'iteration-limit', + remaining: findings.totals } + }); + break; + } + + iteration++; +} +``` + +## Review Queue + +Store state at `{stateDir}/review-queue-{timestamp}.json`: +```javascript +{ + status: 'open|resolved|blocked', + scope: { type: 'diff', files: [...] }, + passes: ['code-quality', 'security', ...], + items: [/* findings */], + iteration: N, + stallCount: N +} +``` + +Delete when approved. Keep when blocked for orchestrator inspection. diff --git a/plugins/ship/commands/ship.md b/plugins/ship/commands/ship.md index 9fea24ae..75db6b5d 100644 --- a/plugins/ship/commands/ship.md +++ b/plugins/ship/commands/ship.md @@ -25,7 +25,7 @@ Auto-adapts to your project's CI platform, deployment platform, and branch strat ## Integration with /next-task When called from `/next-task` workflow (via `--state-file`): -- **SKIPS Phase 5** internal review agents (already done by review-orchestrator) +- **SKIPS Phase 5** internal review agents (already done by Phase 9 review loop) - **SKIPS deslop/docs** (already done by deslop-work, docs-updater) - **Trusts** that all quality gates passed