From 6a9f075cd97c139a5f7e84e1e3f2c9ab095adf64 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Sun, 26 Jul 2026 03:20:15 -0700 Subject: [PATCH] fix: use scalar Claude agent tools (#2583) Normalize scalar Claude agent tool metadata across validators, adapters, dashboards, and generated surfaces with regression coverage. --- .claude-plugin/PLUGIN_SCHEMA_NOTES.md | 15 ++ CONTRIBUTING.md | 2 +- README.md | 2 +- agents/a11y-architect.md | 2 +- agents/agent-evaluator.md | 2 +- agents/architect.md | 2 +- agents/build-error-resolver.md | 2 +- agents/chief-of-staff.md | 2 +- agents/code-architect.md | 2 +- agents/code-explorer.md | 2 +- agents/code-reviewer.md | 2 +- agents/code-simplifier.md | 2 +- agents/comment-analyzer.md | 2 +- agents/conversation-analyzer.md | 2 +- agents/cpp-build-resolver.md | 2 +- agents/cpp-reviewer.md | 2 +- agents/csharp-reviewer.md | 2 +- agents/dart-build-resolver.md | 2 +- agents/database-reviewer.md | 2 +- agents/django-build-resolver.md | 2 +- agents/django-reviewer.md | 2 +- agents/doc-updater.md | 2 +- agents/docs-lookup.md | 2 +- agents/e2e-runner.md | 2 +- agents/fastapi-reviewer.md | 2 +- agents/flutter-reviewer.md | 2 +- agents/fsharp-reviewer.md | 2 +- agents/gan-evaluator.md | 2 +- agents/gan-generator.md | 2 +- agents/gan-planner.md | 2 +- agents/go-build-resolver.md | 2 +- agents/go-reviewer.md | 2 +- agents/harmonyos-app-resolver.md | 2 +- agents/harness-optimizer.md | 2 +- agents/healthcare-reviewer.md | 2 +- agents/homelab-architect.md | 2 +- agents/java-build-resolver.md | 2 +- agents/java-reviewer.md | 2 +- agents/kotlin-build-resolver.md | 2 +- agents/kotlin-reviewer.md | 2 +- agents/loop-operator.md | 2 +- agents/marketing-agent.md | 2 +- agents/mle-reviewer.md | 2 +- agents/network-architect.md | 2 +- agents/network-config-reviewer.md | 2 +- agents/network-troubleshooter.md | 2 +- agents/opensource-forker.md | 2 +- agents/opensource-packager.md | 2 +- agents/opensource-sanitizer.md | 2 +- agents/performance-optimizer.md | 2 +- agents/php-reviewer.md | 2 +- agents/planner.md | 2 +- agents/pr-test-analyzer.md | 2 +- agents/python-reviewer.md | 2 +- agents/pytorch-build-resolver.md | 2 +- agents/react-build-resolver.md | 2 +- agents/react-reviewer.md | 2 +- agents/refactor-cleaner.md | 2 +- agents/rust-build-resolver.md | 2 +- agents/rust-reviewer.md | 2 +- agents/security-reviewer.md | 2 +- agents/seo-specialist.md | 2 +- agents/silent-failure-hunter.md | 2 +- agents/spec-miner.md | 2 +- agents/swift-build-resolver.md | 2 +- agents/swift-reviewer.md | 2 +- agents/tdd-guide.md | 2 +- agents/type-design-analyzer.md | 2 +- agents/typescript-reviewer.md | 2 +- agents/vue-reviewer.md | 2 +- scripts/ci/validate-agents.js | 25 ++++ scripts/dashboard-web.js | 7 +- scripts/gemini-adapt-agents.js | 17 +-- scripts/lib/agent-compress.js | 5 + scripts/lib/agent-tools.js | 97 ++++++++++++ tests/ci/validate-agents-tools.test.js | 175 ++++++++++++++++++++++ tests/lib/agent-compress.test.js | 29 +++- tests/scripts/dashboard-web.test.js | 64 +++++++- tests/scripts/gemini-adapt-agents.test.js | 30 ++++ 79 files changed, 515 insertions(+), 87 deletions(-) create mode 100644 scripts/lib/agent-tools.js create mode 100644 tests/ci/validate-agents-tools.test.js diff --git a/.claude-plugin/PLUGIN_SCHEMA_NOTES.md b/.claude-plugin/PLUGIN_SCHEMA_NOTES.md index e427225fb..61859c5dd 100644 --- a/.claude-plugin/PLUGIN_SCHEMA_NOTES.md +++ b/.claude-plugin/PLUGIN_SCHEMA_NOTES.md @@ -55,6 +55,21 @@ This applies consistently across all component path fields. --- +## Agent `tools` Frontmatter: USE A SCALAR + +The array rule above applies to `plugin.json`, not agent Markdown frontmatter. +Claude Code agent files use a comma-separated scalar for their tool allowlist: + +```yaml +tools: Read, Glob, Grep +``` + +Do not use a YAML sequence such as `tools: [Read, Glob, Grep]`. Omitting the +`tools` field grants the agent access to all tools, but ECC agents declare +explicit allowlists and the repository validator requires the field. + +--- + ## The `agents` Field: DO NOT ADD > WARNING: **CRITICAL:** Do NOT add an `"agents"` field to `plugin.json`. The Claude Code plugin validator rejects it entirely. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8157a38c8..06d1431b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -199,7 +199,7 @@ agents/your-agent-name.md --- name: your-agent-name description: What this agent does and when Claude should invoke it. Be specific! -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/README.md b/README.md index 6776bd5f9..dcfb2a2e5 100644 --- a/README.md +++ b/README.md @@ -1152,7 +1152,7 @@ Subagents handle delegated tasks with limited scope. Example: --- name: code-reviewer description: Reviews code for quality, security, and maintainability -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: opus --- diff --git a/agents/a11y-architect.md b/agents/a11y-architect.md index 0cc328863..63f6c594c 100644 --- a/agents/a11y-architect.md +++ b/agents/a11y-architect.md @@ -2,7 +2,7 @@ name: a11y-architect description: Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI components, establishing design systems, or auditing code for inclusive user experiences. model: sonnet -tools: ["Read", "Write", "Edit", "Grep", "Glob"] +tools: Read, Write, Edit, Grep, Glob --- ## Prompt Defense Baseline diff --git a/agents/agent-evaluator.md b/agents/agent-evaluator.md index c44242ba2..a9ae22d96 100644 --- a/agents/agent-evaluator.md +++ b/agents/agent-evaluator.md @@ -1,7 +1,7 @@ --- name: agent-evaluator description: Evaluates agent output against 5-axis quality rubric (accuracy, completeness, clarity, actionability, conciseness). Use after any non-trivial task when the user wants a quality assessment, or when the agent-self-evaluation skill is active. Produces structured scorecard with evidence and improvement suggestions. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/architect.md b/agents/architect.md index b57cd26e2..d65bea41b 100644 --- a/agents/architect.md +++ b/agents/architect.md @@ -1,7 +1,7 @@ --- name: architect description: Software architecture specialist for system design, scalability, and technical decision-making. Use PROACTIVELY when planning new features, refactoring large systems, or making architectural decisions. -tools: ["Read", "Grep", "Glob"] +tools: Read, Grep, Glob model: opus --- diff --git a/agents/build-error-resolver.md b/agents/build-error-resolver.md index 2ab19ac35..23be5e7c9 100644 --- a/agents/build-error-resolver.md +++ b/agents/build-error-resolver.md @@ -1,7 +1,7 @@ --- name: build-error-resolver description: Build and TypeScript error resolution specialist. Use PROACTIVELY when build fails or type errors occur. Fixes build/type errors only with minimal diffs, no architectural edits. Focuses on getting the build green quickly. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/chief-of-staff.md b/agents/chief-of-staff.md index 49b844982..0ceb151f3 100644 --- a/agents/chief-of-staff.md +++ b/agents/chief-of-staff.md @@ -1,7 +1,7 @@ --- name: chief-of-staff description: Personal communication chief of staff that triages email, Slack, LINE, and Messenger. Classifies messages into 4 tiers (skip/info_only/meeting_info/action_required), generates draft replies, and enforces post-send follow-through via hooks. Use when managing multi-channel communication workflows. -tools: ["Read", "Grep", "Glob", "Bash", "Edit", "Write"] +tools: Read, Grep, Glob, Bash, Edit, Write model: sonnet --- diff --git a/agents/code-architect.md b/agents/code-architect.md index e99b3c718..4877556d2 100644 --- a/agents/code-architect.md +++ b/agents/code-architect.md @@ -2,7 +2,7 @@ name: code-architect description: Designs feature architectures by analyzing existing codebase patterns and conventions, then providing implementation blueprints with concrete files, interfaces, data flow, and build order. model: sonnet -tools: [Read, Grep, Glob, Bash] +tools: Read, Grep, Glob, Bash --- ## Prompt Defense Baseline diff --git a/agents/code-explorer.md b/agents/code-explorer.md index a39167994..a97d0c3ce 100644 --- a/agents/code-explorer.md +++ b/agents/code-explorer.md @@ -2,7 +2,7 @@ name: code-explorer description: Deeply analyzes existing codebase features by tracing execution paths, mapping architecture layers, and documenting dependencies to inform new development. model: sonnet -tools: [Read, Grep, Glob] +tools: Read, Grep, Glob --- ## Prompt Defense Baseline diff --git a/agents/code-reviewer.md b/agents/code-reviewer.md index af791188a..884d94ec2 100644 --- a/agents/code-reviewer.md +++ b/agents/code-reviewer.md @@ -1,7 +1,7 @@ --- name: code-reviewer description: Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code. MUST BE USED for all code changes. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/code-simplifier.md b/agents/code-simplifier.md index 4438e8726..b14a4926c 100644 --- a/agents/code-simplifier.md +++ b/agents/code-simplifier.md @@ -2,7 +2,7 @@ name: code-simplifier description: Simplifies and refines code for clarity, consistency, and maintainability while preserving behavior. Focus on recently modified code unless instructed otherwise. model: sonnet -tools: [Read, Write, Edit, Bash, Grep, Glob] +tools: Read, Write, Edit, Bash, Grep, Glob --- ## Prompt Defense Baseline diff --git a/agents/comment-analyzer.md b/agents/comment-analyzer.md index 4c43138ec..a8e0f48e6 100644 --- a/agents/comment-analyzer.md +++ b/agents/comment-analyzer.md @@ -2,7 +2,7 @@ name: comment-analyzer description: Analyze code comments for accuracy, completeness, maintainability, and comment rot risk. model: haiku -tools: [Read, Grep, Glob] +tools: Read, Grep, Glob --- ## Prompt Defense Baseline diff --git a/agents/conversation-analyzer.md b/agents/conversation-analyzer.md index 47a8db7c8..1e557c2dc 100644 --- a/agents/conversation-analyzer.md +++ b/agents/conversation-analyzer.md @@ -2,7 +2,7 @@ name: conversation-analyzer description: Use this agent when analyzing conversation transcripts to find behaviors worth preventing with hooks. Triggered by /hookify without arguments. model: haiku -tools: [Read, Grep] +tools: Read, Grep --- ## Prompt Defense Baseline diff --git a/agents/cpp-build-resolver.md b/agents/cpp-build-resolver.md index 7c2c41557..9eb29d969 100644 --- a/agents/cpp-build-resolver.md +++ b/agents/cpp-build-resolver.md @@ -1,7 +1,7 @@ --- name: cpp-build-resolver description: C++ build, CMake, and compilation error resolution specialist. Fixes build errors, linker issues, and template errors with minimal changes. Use when C++ builds fail. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/cpp-reviewer.md b/agents/cpp-reviewer.md index 4c2f0e6a3..d29e7ae19 100644 --- a/agents/cpp-reviewer.md +++ b/agents/cpp-reviewer.md @@ -1,7 +1,7 @@ --- name: cpp-reviewer description: Expert C++ code reviewer specializing in memory safety, modern C++ idioms, concurrency, and performance. Use for all C++ code changes. MUST BE USED for C++ projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/csharp-reviewer.md b/agents/csharp-reviewer.md index 447e1622c..57bbaf6d6 100644 --- a/agents/csharp-reviewer.md +++ b/agents/csharp-reviewer.md @@ -1,7 +1,7 @@ --- name: csharp-reviewer description: Expert C# code reviewer specializing in .NET conventions, async patterns, security, nullable reference types, and performance. Use for all C# code changes. MUST BE USED for C# projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/dart-build-resolver.md b/agents/dart-build-resolver.md index 7f5be822e..872b99e4e 100644 --- a/agents/dart-build-resolver.md +++ b/agents/dart-build-resolver.md @@ -1,7 +1,7 @@ --- name: dart-build-resolver description: Dart/Flutter build, analysis, and dependency error resolution specialist. Fixes `dart analyze` errors, Flutter compilation failures, pub dependency conflicts, and build_runner issues with minimal, surgical changes. Use when Dart/Flutter builds fail. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/database-reviewer.md b/agents/database-reviewer.md index cd7e6b004..8537765b7 100644 --- a/agents/database-reviewer.md +++ b/agents/database-reviewer.md @@ -1,7 +1,7 @@ --- name: database-reviewer description: PostgreSQL database specialist for query optimization, schema design, security, and performance. Use PROACTIVELY when writing SQL, creating migrations, designing schemas, or troubleshooting database performance. Incorporates Supabase best practices. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/django-build-resolver.md b/agents/django-build-resolver.md index 0267cad36..0a7f93f51 100644 --- a/agents/django-build-resolver.md +++ b/agents/django-build-resolver.md @@ -1,7 +1,7 @@ --- name: django-build-resolver description: Django/Python build, migration, and dependency error resolution specialist. Fixes pip/Poetry errors, migration conflicts, import errors, Django configuration issues, and collectstatic failures with minimal changes. Use when Django setup or startup fails. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/django-reviewer.md b/agents/django-reviewer.md index 746311983..73725e4b4 100644 --- a/agents/django-reviewer.md +++ b/agents/django-reviewer.md @@ -1,7 +1,7 @@ --- name: django-reviewer description: Expert Django code reviewer specializing in ORM correctness, DRF patterns, migration safety, security misconfigurations, and production-grade Django practices. Use for all Django code changes. MUST BE USED for Django projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/doc-updater.md b/agents/doc-updater.md index 0da663329..4fd5bd46e 100644 --- a/agents/doc-updater.md +++ b/agents/doc-updater.md @@ -1,7 +1,7 @@ --- name: doc-updater description: Documentation and codemap specialist. Use PROACTIVELY for updating codemaps and documentation. Runs /update-codemaps and /update-docs, generates docs/CODEMAPS/*, updates READMEs and guides. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: haiku --- diff --git a/agents/docs-lookup.md b/agents/docs-lookup.md index 79533e36f..f018ce4eb 100644 --- a/agents/docs-lookup.md +++ b/agents/docs-lookup.md @@ -1,7 +1,7 @@ --- name: docs-lookup description: When the user asks how to use a library, framework, or API or needs up-to-date code examples, use Context7 MCP to fetch current documentation and return answers with examples. Invoke for docs/API/setup questions. -tools: ["Read", "Grep", "mcp__context7__resolve-library-id", "mcp__context7__query-docs"] +tools: Read, Grep, mcp__context7__resolve-library-id, mcp__context7__query-docs model: haiku --- diff --git a/agents/e2e-runner.md b/agents/e2e-runner.md index 5b879dcf0..46a7867d8 100644 --- a/agents/e2e-runner.md +++ b/agents/e2e-runner.md @@ -1,7 +1,7 @@ --- name: e2e-runner description: End-to-end testing specialist using Vercel Agent Browser (preferred) with Playwright fallback. Use PROACTIVELY for generating, maintaining, and running E2E tests. Manages test journeys, quarantines flaky tests, uploads artifacts (screenshots, videos, traces), and ensures critical user flows work. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/fastapi-reviewer.md b/agents/fastapi-reviewer.md index cb1b5b1bf..f4c79b95c 100644 --- a/agents/fastapi-reviewer.md +++ b/agents/fastapi-reviewer.md @@ -1,7 +1,7 @@ --- name: fastapi-reviewer description: Reviews FastAPI applications for async correctness, dependency injection, Pydantic schemas, security, OpenAPI quality, testing, and production readiness. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/flutter-reviewer.md b/agents/flutter-reviewer.md index cb7e25619..2d8abef30 100644 --- a/agents/flutter-reviewer.md +++ b/agents/flutter-reviewer.md @@ -1,7 +1,7 @@ --- name: flutter-reviewer description: Flutter and Dart code reviewer. Reviews Flutter code for widget best practices, state management patterns, Dart idioms, performance pitfalls, accessibility, and clean architecture violations. Library-agnostic — works with any state management solution and tooling. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/fsharp-reviewer.md b/agents/fsharp-reviewer.md index 094603135..9628c328e 100644 --- a/agents/fsharp-reviewer.md +++ b/agents/fsharp-reviewer.md @@ -1,7 +1,7 @@ --- name: fsharp-reviewer description: Expert F# code reviewer specializing in functional idioms, type safety, pattern matching, computation expressions, and performance. Use for all F# code changes. MUST BE USED for F# projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/gan-evaluator.md b/agents/gan-evaluator.md index 0ecced4f9..95060e711 100644 --- a/agents/gan-evaluator.md +++ b/agents/gan-evaluator.md @@ -1,7 +1,7 @@ --- name: gan-evaluator description: "GAN Harness — Evaluator agent. Tests the live running application via Playwright, scores against rubric, and provides actionable feedback to the Generator." -tools: ["Read", "Write", "Bash", "Grep", "Glob"] +tools: Read, Write, Bash, Grep, Glob model: sonnet color: red --- diff --git a/agents/gan-generator.md b/agents/gan-generator.md index 51dc35f7c..af0c577ff 100644 --- a/agents/gan-generator.md +++ b/agents/gan-generator.md @@ -1,7 +1,7 @@ --- name: gan-generator description: "GAN Harness — Generator agent. Implements features according to the spec, reads evaluator feedback, and iterates until quality threshold is met." -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet color: green --- diff --git a/agents/gan-planner.md b/agents/gan-planner.md index be5da2173..57a018249 100644 --- a/agents/gan-planner.md +++ b/agents/gan-planner.md @@ -1,7 +1,7 @@ --- name: gan-planner description: "GAN Harness — Planner agent. Expands a one-line prompt into a full product specification with features, sprints, evaluation criteria, and design direction." -tools: ["Read", "Write", "Grep", "Glob"] +tools: Read, Write, Grep, Glob model: sonnet color: purple --- diff --git a/agents/go-build-resolver.md b/agents/go-build-resolver.md index c41825d2d..b3dbe383b 100644 --- a/agents/go-build-resolver.md +++ b/agents/go-build-resolver.md @@ -1,7 +1,7 @@ --- name: go-build-resolver description: Go build, vet, and compilation error resolution specialist. Fixes build errors, go vet issues, and linter warnings with minimal changes. Use when Go builds fail. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/go-reviewer.md b/agents/go-reviewer.md index e30ab8d76..72dc7654a 100644 --- a/agents/go-reviewer.md +++ b/agents/go-reviewer.md @@ -1,7 +1,7 @@ --- name: go-reviewer description: Expert Go code reviewer specializing in idiomatic Go, concurrency patterns, error handling, and performance. Use for all Go code changes. MUST BE USED for Go projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/harmonyos-app-resolver.md b/agents/harmonyos-app-resolver.md index c319014d1..ef52fd09d 100644 --- a/agents/harmonyos-app-resolver.md +++ b/agents/harmonyos-app-resolver.md @@ -1,7 +1,7 @@ --- name: harmonyos-app-resolver description: HarmonyOS application development expert specializing in ArkTS and ArkUI. Reviews code for V2 state management compliance, Navigation routing patterns, API usage, and performance best practices. Use for HarmonyOS/OpenHarmony projects. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/harness-optimizer.md b/agents/harness-optimizer.md index d4cec77bb..bf33243df 100644 --- a/agents/harness-optimizer.md +++ b/agents/harness-optimizer.md @@ -1,7 +1,7 @@ --- name: harness-optimizer description: Analyze and improve the local agent harness configuration for reliability, cost, and throughput. -tools: ["Read", "Grep", "Glob", "Bash", "Edit"] +tools: Read, Grep, Glob, Bash, Edit model: sonnet color: teal --- diff --git a/agents/healthcare-reviewer.md b/agents/healthcare-reviewer.md index 98b5953e2..187079ca7 100644 --- a/agents/healthcare-reviewer.md +++ b/agents/healthcare-reviewer.md @@ -1,7 +1,7 @@ --- name: healthcare-reviewer description: Reviews healthcare application code for clinical safety, CDSS accuracy, PHI compliance, and medical data integrity. Specialized for EMR/EHR, clinical decision support, and health information systems. -tools: ["Read", "Grep", "Glob"] +tools: Read, Grep, Glob model: opus --- diff --git a/agents/homelab-architect.md b/agents/homelab-architect.md index 0d30f1cb7..608d59eb3 100644 --- a/agents/homelab-architect.md +++ b/agents/homelab-architect.md @@ -1,7 +1,7 @@ --- name: homelab-architect description: Designs home and small-lab network plans from hardware inventory, goals, and operator experience level, with safe staged changes and rollback guidance. -tools: ["Read", "Grep"] +tools: Read, Grep model: sonnet --- diff --git a/agents/java-build-resolver.md b/agents/java-build-resolver.md index 5d3946122..ba638dd07 100644 --- a/agents/java-build-resolver.md +++ b/agents/java-build-resolver.md @@ -1,7 +1,7 @@ --- name: java-build-resolver description: Java/Maven/Gradle build, compilation, and dependency error resolution specialist. Automatically detects Spring Boot or Quarkus and applies framework-specific fixes. Fixes build errors, Java compiler errors, and Maven/Gradle issues with minimal changes. Use when Java builds fail. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/java-reviewer.md b/agents/java-reviewer.md index 96edf495a..fbe19bf40 100644 --- a/agents/java-reviewer.md +++ b/agents/java-reviewer.md @@ -1,7 +1,7 @@ --- name: java-reviewer description: Expert Java code reviewer for Spring Boot and Quarkus projects. Automatically detects the framework and applies the appropriate review rules. Covers layered architecture, JPA/Panache, MongoDB, security, and concurrency. MUST BE USED for all Java code changes. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/kotlin-build-resolver.md b/agents/kotlin-build-resolver.md index ec43f445f..45315270f 100644 --- a/agents/kotlin-build-resolver.md +++ b/agents/kotlin-build-resolver.md @@ -1,7 +1,7 @@ --- name: kotlin-build-resolver description: Kotlin/Gradle build, compilation, and dependency error resolution specialist. Fixes build errors, Kotlin compiler errors, and Gradle issues with minimal changes. Use when Kotlin builds fail. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/kotlin-reviewer.md b/agents/kotlin-reviewer.md index bf2ff36b0..95ed8a2c3 100644 --- a/agents/kotlin-reviewer.md +++ b/agents/kotlin-reviewer.md @@ -1,7 +1,7 @@ --- name: kotlin-reviewer description: Kotlin and Android/KMP code reviewer. Reviews Kotlin code for idiomatic patterns, coroutine safety, Compose best practices, clean architecture violations, and common Android pitfalls. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/loop-operator.md b/agents/loop-operator.md index a2fa6ce73..4da0665eb 100644 --- a/agents/loop-operator.md +++ b/agents/loop-operator.md @@ -1,7 +1,7 @@ --- name: loop-operator description: Operate autonomous agent loops, monitor progress, and intervene safely when loops stall. -tools: ["Read", "Grep", "Glob", "Bash", "Edit"] +tools: Read, Grep, Glob, Bash, Edit model: sonnet color: orange --- diff --git a/agents/marketing-agent.md b/agents/marketing-agent.md index 2dae88c11..adf46403e 100644 --- a/agents/marketing-agent.md +++ b/agents/marketing-agent.md @@ -1,7 +1,7 @@ --- name: marketing-agent description: Marketing strategist and copywriter for campaign planning, audience research, positioning, copy creation, and content review. Covers landing pages, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use when the user wants to plan or execute a product launch or marketing campaign. -tools: ["Read", "Grep", "Glob", "WebSearch", "WebFetch"] +tools: Read, Grep, Glob, WebSearch, WebFetch model: sonnet --- diff --git a/agents/mle-reviewer.md b/agents/mle-reviewer.md index d5cd375e8..9b5c8d55a 100644 --- a/agents/mle-reviewer.md +++ b/agents/mle-reviewer.md @@ -1,7 +1,7 @@ --- name: mle-reviewer description: Production machine-learning engineering reviewer for data contracts, feature pipelines, training reproducibility, offline/online evaluation, model serving, monitoring, and rollback. Use when ML, MLOps, model training, inference, feature store, or evaluation code changes. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/network-architect.md b/agents/network-architect.md index 5b8e73245..181fc473a 100644 --- a/agents/network-architect.md +++ b/agents/network-architect.md @@ -1,7 +1,7 @@ --- name: network-architect description: Designs enterprise or multi-site network architecture from requirements, using existing network skills for focused routing, validation, automation, and troubleshooting detail. -tools: ["Read", "Grep"] +tools: Read, Grep model: sonnet --- diff --git a/agents/network-config-reviewer.md b/agents/network-config-reviewer.md index 3e40e8282..1362f4701 100644 --- a/agents/network-config-reviewer.md +++ b/agents/network-config-reviewer.md @@ -1,7 +1,7 @@ --- name: network-config-reviewer description: Reviews router and switch configurations for security, correctness, stale references, risky change-window commands, and missing operational guardrails. -tools: ["Read", "Grep"] +tools: Read, Grep model: sonnet --- diff --git a/agents/network-troubleshooter.md b/agents/network-troubleshooter.md index 3f26bfb5c..4bd666026 100644 --- a/agents/network-troubleshooter.md +++ b/agents/network-troubleshooter.md @@ -1,7 +1,7 @@ --- name: network-troubleshooter description: Diagnoses network connectivity, routing, DNS, interface, and policy symptoms with a read-only OSI-layer workflow and evidence-backed root cause summary. -tools: ["Read", "Bash", "Grep"] +tools: Read, Bash, Grep model: sonnet --- diff --git a/agents/opensource-forker.md b/agents/opensource-forker.md index 6dac93d9c..4c5d8cbb8 100644 --- a/agents/opensource-forker.md +++ b/agents/opensource-forker.md @@ -1,7 +1,7 @@ --- name: opensource-forker description: Fork any project for open-sourcing. Copies files, strips secrets and credentials (20+ patterns), replaces internal references with placeholders, generates .env.example, and cleans git history. First stage of the opensource-pipeline skill. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: haiku --- diff --git a/agents/opensource-packager.md b/agents/opensource-packager.md index 106d547f8..e6e24199d 100644 --- a/agents/opensource-packager.md +++ b/agents/opensource-packager.md @@ -1,7 +1,7 @@ --- name: opensource-packager description: Generate complete open-source packaging for a sanitized project. Produces CLAUDE.md, setup.sh, README.md, LICENSE, CONTRIBUTING.md, and GitHub issue templates. Makes any repo immediately usable with Claude Code. Third stage of the opensource-pipeline skill. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: haiku --- diff --git a/agents/opensource-sanitizer.md b/agents/opensource-sanitizer.md index b59dc98b5..a0d538508 100644 --- a/agents/opensource-sanitizer.md +++ b/agents/opensource-sanitizer.md @@ -1,7 +1,7 @@ --- name: opensource-sanitizer description: Verify an open-source fork is fully sanitized before release. Scans for leaked secrets, PII, internal references, and dangerous files using 20+ regex patterns. Generates a PASS/FAIL/PASS-WITH-WARNINGS report. Second stage of the opensource-pipeline skill. Use PROACTIVELY before any public release. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/performance-optimizer.md b/agents/performance-optimizer.md index 84d4e3024..4d5de6f96 100644 --- a/agents/performance-optimizer.md +++ b/agents/performance-optimizer.md @@ -1,7 +1,7 @@ --- name: performance-optimizer description: Performance analysis and optimization specialist. Use PROACTIVELY for identifying bottlenecks, optimizing slow code, reducing bundle sizes, and improving runtime performance. Profiling, memory leaks, render optimization, and algorithmic improvements. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/php-reviewer.md b/agents/php-reviewer.md index 0c4d31d96..af90974fc 100644 --- a/agents/php-reviewer.md +++ b/agents/php-reviewer.md @@ -1,7 +1,7 @@ --- name: php-reviewer description: Expert PHP code reviewer specializing in PSR-12 compliance, PHP type system, Eloquent ORM patterns, security, and performance. Use for all PHP code changes. MUST BE USED for PHP projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/planner.md b/agents/planner.md index c311f492b..e9e282f54 100644 --- a/agents/planner.md +++ b/agents/planner.md @@ -1,7 +1,7 @@ --- name: planner description: Expert planning specialist for complex features and refactoring. Use PROACTIVELY when users request feature implementation, architectural changes, or complex refactoring. Automatically activated for planning tasks. -tools: ["Read", "Grep", "Glob"] +tools: Read, Grep, Glob model: opus --- diff --git a/agents/pr-test-analyzer.md b/agents/pr-test-analyzer.md index c8268371c..07bf41ebd 100644 --- a/agents/pr-test-analyzer.md +++ b/agents/pr-test-analyzer.md @@ -2,7 +2,7 @@ name: pr-test-analyzer description: Review pull request test coverage quality and completeness, with emphasis on behavioral coverage and real bug prevention. model: sonnet -tools: [Read, Grep, Glob, Bash] +tools: Read, Grep, Glob, Bash --- ## Prompt Defense Baseline diff --git a/agents/python-reviewer.md b/agents/python-reviewer.md index 9bd948555..b1b3ee6f5 100644 --- a/agents/python-reviewer.md +++ b/agents/python-reviewer.md @@ -1,7 +1,7 @@ --- name: python-reviewer description: Expert Python code reviewer specializing in PEP 8 compliance, Pythonic idioms, type hints, security, and performance. Use for all Python code changes. MUST BE USED for Python projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/pytorch-build-resolver.md b/agents/pytorch-build-resolver.md index 19511a50c..88f567968 100644 --- a/agents/pytorch-build-resolver.md +++ b/agents/pytorch-build-resolver.md @@ -1,7 +1,7 @@ --- name: pytorch-build-resolver description: PyTorch runtime, CUDA, and training error resolution specialist. Fixes tensor shape mismatches, device errors, gradient issues, DataLoader problems, and mixed precision failures with minimal changes. Use when PyTorch training or inference crashes. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/react-build-resolver.md b/agents/react-build-resolver.md index 32ff3ef75..ecfa77e0f 100644 --- a/agents/react-build-resolver.md +++ b/agents/react-build-resolver.md @@ -1,7 +1,7 @@ --- name: react-build-resolver description: Diagnose and fix React build failures across Vite, webpack, Next.js, CRA, Parcel, esbuild, and Bun. Handles JSX/TSX compile errors, hydration mismatches, server/client component boundary failures, missing types, and bundler-specific configuration issues with minimal, surgical changes. MUST BE USED when a React build fails. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/react-reviewer.md b/agents/react-reviewer.md index 34006d344..b25b79e71 100644 --- a/agents/react-reviewer.md +++ b/agents/react-reviewer.md @@ -1,7 +1,7 @@ --- name: react-reviewer description: Expert React/JSX code reviewer specializing in hook correctness, render performance, server/client component boundaries, accessibility, and React-specific security. Use for any change touching .tsx/.jsx files or React component logic. MUST BE USED for React projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/refactor-cleaner.md b/agents/refactor-cleaner.md index a09a5d9c2..093c8f647 100644 --- a/agents/refactor-cleaner.md +++ b/agents/refactor-cleaner.md @@ -1,7 +1,7 @@ --- name: refactor-cleaner description: Dead code cleanup and consolidation specialist. Use PROACTIVELY for removing unused code, duplicates, and refactoring. Runs analysis tools (knip, depcheck, ts-prune) to identify dead code and safely removes it. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/rust-build-resolver.md b/agents/rust-build-resolver.md index 144dc1ae0..552d267cd 100644 --- a/agents/rust-build-resolver.md +++ b/agents/rust-build-resolver.md @@ -1,7 +1,7 @@ --- name: rust-build-resolver description: Rust build, compilation, and dependency error resolution specialist. Fixes cargo build errors, borrow checker issues, and Cargo.toml problems with minimal changes. Use when Rust builds fail. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/rust-reviewer.md b/agents/rust-reviewer.md index 83373d26e..380fb0d9f 100644 --- a/agents/rust-reviewer.md +++ b/agents/rust-reviewer.md @@ -1,7 +1,7 @@ --- name: rust-reviewer description: Expert Rust code reviewer specializing in ownership, lifetimes, error handling, unsafe usage, and idiomatic patterns. Use for all Rust code changes. MUST BE USED for Rust projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/security-reviewer.md b/agents/security-reviewer.md index d1d476cfd..b5c5e38d2 100644 --- a/agents/security-reviewer.md +++ b/agents/security-reviewer.md @@ -1,7 +1,7 @@ --- name: security-reviewer description: Security vulnerability detection and remediation specialist. Use PROACTIVELY after writing code that handles user input, authentication, API endpoints, or sensitive data. Flags secrets, SSRF, injection, unsafe crypto, and OWASP Top 10 vulnerabilities. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/seo-specialist.md b/agents/seo-specialist.md index ec6758f13..fd127ec08 100644 --- a/agents/seo-specialist.md +++ b/agents/seo-specialist.md @@ -1,7 +1,7 @@ --- name: seo-specialist description: SEO specialist for technical SEO audits, on-page optimization, structured data, Core Web Vitals, and content/keyword mapping. Use for site audits, meta tag reviews, schema markup, sitemap and robots issues, and SEO remediation plans. -tools: ["Read", "Grep", "Glob", "WebSearch", "WebFetch"] +tools: Read, Grep, Glob, WebSearch, WebFetch model: sonnet --- diff --git a/agents/silent-failure-hunter.md b/agents/silent-failure-hunter.md index b0a1ee69d..e38053453 100644 --- a/agents/silent-failure-hunter.md +++ b/agents/silent-failure-hunter.md @@ -2,7 +2,7 @@ name: silent-failure-hunter description: Review code for silent failures, swallowed errors, bad fallbacks, and missing error propagation. model: sonnet -tools: [Read, Grep, Glob, Bash] +tools: Read, Grep, Glob, Bash --- ## Prompt Defense Baseline diff --git a/agents/spec-miner.md b/agents/spec-miner.md index 8bca3556f..f5e2e76be 100644 --- a/agents/spec-miner.md +++ b/agents/spec-miner.md @@ -2,7 +2,7 @@ name: spec-miner description: Extracts behavioral specs from existing codebases for OpenSpec. Produces flat Requirement and Invariant blocks with structured metadata (entities, enforced, id, test anchors). Outputs openspec/specs//spec.md. Fully self-bootstrapping — no dependency on codebase-onboarding. Use when onboarding a brownfield project to spec-driven development. model: opus -tools: ["Read", "Grep", "Glob", "Bash", "Write"] +tools: Read, Grep, Glob, Bash, Write --- ## Tool guardrails diff --git a/agents/swift-build-resolver.md b/agents/swift-build-resolver.md index 3063b742f..5896e74e7 100644 --- a/agents/swift-build-resolver.md +++ b/agents/swift-build-resolver.md @@ -1,7 +1,7 @@ --- name: swift-build-resolver description: Swift/Xcode build, compilation, and dependency error resolution specialist. Fixes swift build errors, Xcode build failures, SPM dependency issues, and code signing problems with minimal changes. Use when Swift builds fail. -tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] +tools: Read, Write, Edit, Bash, Grep, Glob model: sonnet --- diff --git a/agents/swift-reviewer.md b/agents/swift-reviewer.md index 39f4b0bca..c77c2b0db 100644 --- a/agents/swift-reviewer.md +++ b/agents/swift-reviewer.md @@ -1,7 +1,7 @@ --- name: swift-reviewer description: Expert Swift code reviewer specializing in protocol-oriented design, value semantics, ARC memory management, Swift Concurrency, and idiomatic patterns. Use for all Swift code changes. MUST BE USED for Swift projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/tdd-guide.md b/agents/tdd-guide.md index 1d0849840..d4f6443b8 100644 --- a/agents/tdd-guide.md +++ b/agents/tdd-guide.md @@ -1,7 +1,7 @@ --- name: tdd-guide description: Test-Driven Development specialist enforcing write-tests-first methodology. Use PROACTIVELY when writing new features, fixing bugs, or refactoring code. Ensures 80%+ test coverage. -tools: ["Read", "Write", "Edit", "Bash", "Grep"] +tools: Read, Write, Edit, Bash, Grep model: sonnet --- diff --git a/agents/type-design-analyzer.md b/agents/type-design-analyzer.md index 414a82a07..394f2626b 100644 --- a/agents/type-design-analyzer.md +++ b/agents/type-design-analyzer.md @@ -2,7 +2,7 @@ name: type-design-analyzer description: Analyze type design for encapsulation, invariant expression, usefulness, and enforcement. model: sonnet -tools: [Read, Grep, Glob] +tools: Read, Grep, Glob --- ## Prompt Defense Baseline diff --git a/agents/typescript-reviewer.md b/agents/typescript-reviewer.md index 8d408d532..23af98e65 100644 --- a/agents/typescript-reviewer.md +++ b/agents/typescript-reviewer.md @@ -1,7 +1,7 @@ --- name: typescript-reviewer description: Expert TypeScript/JavaScript code reviewer specializing in type safety, async correctness, Node/web security, and idiomatic patterns. Use for all TypeScript and JavaScript code changes. MUST BE USED for TypeScript/JavaScript projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/agents/vue-reviewer.md b/agents/vue-reviewer.md index a697654c1..137b520ee 100644 --- a/agents/vue-reviewer.md +++ b/agents/vue-reviewer.md @@ -1,7 +1,7 @@ --- name: vue-reviewer description: Expert Vue.js code reviewer specializing in Composition API correctness, reactivity pitfalls, component architecture, template security, and Vue-specific performance. Use for any change touching .vue, .ts/.js files with Vue imports, or Vue ecosystem code (Pinia, Vue Router, Nuxt). MUST BE USED for Vue projects. -tools: ["Read", "Grep", "Glob", "Bash"] +tools: Read, Grep, Glob, Bash model: sonnet --- diff --git a/scripts/ci/validate-agents.js b/scripts/ci/validate-agents.js index e4220dfa8..c6390d873 100644 --- a/scripts/ci/validate-agents.js +++ b/scripts/ci/validate-agents.js @@ -19,24 +19,44 @@ function extractFrontmatter(content) { const frontmatter = {}; const duplicates = []; + const sequenceFields = []; + let currentTopLevelKey = null; const lines = match[1].split(/\r?\n/); for (const line of lines) { + if (/^\s*-\s+/.test(line)) { + if (currentTopLevelKey) { + sequenceFields.push(currentTopLevelKey); + } + continue; + } + // Only top-level keys are unique. Indented YAML belongs to nested values. if (/^\s/.test(line)) continue; + if (!line.trim() || line.trim().startsWith('#')) continue; + + currentTopLevelKey = null; const colonIdx = line.indexOf(':'); if (colonIdx > 0) { const key = line.slice(0, colonIdx).trim(); const value = line.slice(colonIdx + 1).trim(); + currentTopLevelKey = key; if (Object.prototype.hasOwnProperty.call(frontmatter, key)) { duplicates.push(key); } frontmatter[key] = value; + if (value && '[!&*{|>'.includes(value[0])) { + sequenceFields.push(key); + } } } Object.defineProperty(frontmatter, '__duplicates__', { value: duplicates, enumerable: false, }); + Object.defineProperty(frontmatter, '__sequenceFields__', { + value: sequenceFields, + enumerable: false, + }); return frontmatter; } @@ -79,6 +99,11 @@ function validateAgents() { } } + if (frontmatter.__sequenceFields__.includes('tools')) { + console.error(`ERROR: ${file} - Agent tools must be a comma-separated scalar, not a YAML sequence`); + hasErrors = true; + } + // Validate model is a known value if (frontmatter.model && !VALID_MODELS.includes(frontmatter.model)) { console.error(`ERROR: ${file} - Invalid model '${frontmatter.model}'. Must be one of: ${VALID_MODELS.join(', ')}`); diff --git a/scripts/dashboard-web.js b/scripts/dashboard-web.js index 32167f2f9..271cc6961 100644 --- a/scripts/dashboard-web.js +++ b/scripts/dashboard-web.js @@ -12,6 +12,7 @@ const fs = require('fs'); const path = require('path'); const http = require('http'); +const { normalizeAgentTools } = require('./lib/agent-tools'); function parsePort(v) { const n = parseInt(String(v), 10); @@ -31,7 +32,11 @@ function readFrontmatter(p) { const s = l.indexOf(':'); if (s <= 0) continue; let k = l.slice(0, s).trim(), v = l.slice(s + 1).trim(); if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1); - if (v.startsWith('[') && v.endsWith(']')) { try { v = JSON.parse(v); } catch { v = v.slice(1, -1).split(',').map(x => x.trim().replace(/["']/g, '')); } } + if (k === 'tools') { + v = normalizeAgentTools(v); + } else if (v.startsWith('[') && v.endsWith(']')) { + try { v = JSON.parse(v); } catch { v = v.slice(1, -1).split(',').map(x => x.trim().replace(/["']/g, '')); } + } fm[k] = v; } fm._body = c.replace(/^---[\s\S]*?---\n*/, '').trim(); diff --git a/scripts/gemini-adapt-agents.js b/scripts/gemini-adapt-agents.js index 45faabe3b..bea91cb10 100644 --- a/scripts/gemini-adapt-agents.js +++ b/scripts/gemini-adapt-agents.js @@ -3,6 +3,7 @@ const fs = require('fs'); const path = require('path'); +const { normalizeAgentTools } = require('./lib/agent-tools'); const TOOL_NAME_MAP = new Map([ ['Read', 'read_file'], @@ -53,25 +54,13 @@ function ensureDirectory(dirPath) { } } -function stripQuotes(value) { - return value.trim().replace(/^['"]|['"]$/g, ''); -} - function parseToolList(line) { - const match = line.match(/^(\s*tools\s*:\s*)\[(.*)\]\s*$/); + const match = line.match(/^\s*tools\s*:\s*(.*)$/); if (!match) { return null; } - const rawItems = match[2].trim(); - if (!rawItems) { - return []; - } - - return rawItems - .split(',') - .map(part => stripQuotes(part)) - .filter(Boolean); + return normalizeAgentTools(match[1]); } function adaptToolName(toolName) { diff --git a/scripts/lib/agent-compress.js b/scripts/lib/agent-compress.js index d2abebee6..4772643fb 100644 --- a/scripts/lib/agent-compress.js +++ b/scripts/lib/agent-compress.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); +const { normalizeAgentTools } = require('./agent-tools'); /** * Parse YAML frontmatter from a markdown string. @@ -35,6 +36,10 @@ function parseFrontmatter(content) { value = value.slice(1, -1); } + if (key === 'tools') { + value = normalizeAgentTools(value); + } + frontmatter[key] = value; } diff --git a/scripts/lib/agent-tools.js b/scripts/lib/agent-tools.js new file mode 100644 index 000000000..8b810c885 --- /dev/null +++ b/scripts/lib/agent-tools.js @@ -0,0 +1,97 @@ +'use strict'; + +function stripSurroundingQuotes(value) { + const trimmed = value.trim(); + const quote = trimmed[0]; + if ((quote === '"' || quote === "'") && trimmed.endsWith(quote)) { + return trimmed.slice(1, -1).trim(); + } + return trimmed; +} + +function splitTopLevelToolList(value) { + const items = []; + const delimiters = []; + let quote = null; + let escaped = false; + let itemStart = 0; + + for (let index = 0; index < value.length; index += 1) { + const character = value[index]; + + if (quote) { + if (escaped) { + escaped = false; + } else if (character === '\\') { + escaped = true; + } else if (character === quote) { + quote = null; + } + continue; + } + + if (character === '"' || character === "'") { + quote = character; + continue; + } + + if (character === '(' || character === '[' || character === '{') { + delimiters.push(character); + continue; + } + + const expectedOpener = { + ')': '(', + ']': '[', + '}': '{', + }[character]; + if (expectedOpener && delimiters.at(-1) === expectedOpener) { + delimiters.pop(); + continue; + } + + if (character === ',' && delimiters.length === 0) { + items.push(value.slice(itemStart, index)); + itemStart = index + 1; + } + } + + items.push(value.slice(itemStart)); + return items; +} + +/** + * Normalize Claude agent frontmatter tools to the array shape used internally. + * + * Claude Code expects tools to be a comma-separated scalar. Flow sequences are + * still accepted here so ECC can read legacy or harness-adapted agent files. + */ +function normalizeAgentTools(value) { + if (Array.isArray(value)) { + return value + .filter(item => typeof item === 'string') + .map(stripSurroundingQuotes) + .filter(Boolean); + } + + if (typeof value !== 'string') { + return []; + } + + const trimmed = value.trim(); + const listValue = trimmed.startsWith('[') && trimmed.endsWith(']') + ? trimmed.slice(1, -1) + : stripSurroundingQuotes(trimmed); + + if (!listValue.trim()) { + return []; + } + + return splitTopLevelToolList(listValue) + .map(stripSurroundingQuotes) + .filter(Boolean); +} + +module.exports = { + normalizeAgentTools, +}; diff --git a/tests/ci/validate-agents-tools.test.js b/tests/ci/validate-agents-tools.test.js new file mode 100644 index 000000000..13742459b --- /dev/null +++ b/tests/ci/validate-agents-tools.test.js @@ -0,0 +1,175 @@ +/** + * Focused tests for validate-agents.js tools frontmatter rules. + * + * Run with: node tests/ci/validate-agents-tools.test.js + */ + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const { execFileSync } = require('child_process'); + +const validatorsDir = path.join(__dirname, '..', '..', 'scripts', 'ci'); +const repoRoot = path.join(__dirname, '..', '..'); +const canonicalAgentsDir = path.join(repoRoot, 'agents'); + +function test(name, fn) { + try { + fn(); + console.log(` \u2713 ${name}`); + return true; + } catch (err) { + console.log(` \u2717 ${name}`); + console.log(` Error: ${err.message}`); + return false; + } +} + +function createTestDir() { + return fs.mkdtempSync(path.join(os.tmpdir(), 'validate-agents-tools-test-')); +} + +function cleanupTestDir(testDir) { + fs.rmSync(testDir, { recursive: true, force: true }); +} + +function stripShebang(source) { + let s = source; + if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1); + if (s.startsWith('#!')) { + const nl = s.indexOf('\n'); + s = nl === -1 ? '' : s.slice(nl + 1); + } + return s; +} + +function runSourceViaTempFile(source) { + const tmpFile = path.join(repoRoot, `.tmp-validator-${Date.now()}-${Math.random().toString(36).slice(2)}.js`); + try { + fs.writeFileSync(tmpFile, source, 'utf8'); + const stdout = execFileSync('node', [tmpFile], { + encoding: 'utf8', + stdio: ['pipe', 'pipe', 'pipe'], + timeout: 10000, + cwd: repoRoot, + }); + return { code: 0, stdout, stderr: '' }; + } catch (err) { + return { + code: err.status || 1, + stdout: err.stdout || '', + stderr: err.stderr || '', + }; + } finally { + fs.rmSync(tmpFile, { force: true }); + } +} + +function runValidatorWithDir(validatorName, dirConstant, overridePath) { + const validatorPath = path.join(validatorsDir, `${validatorName}.js`); + let source = fs.readFileSync(validatorPath, 'utf8'); + source = stripShebang(source); + const dirRegex = new RegExp(`const ${dirConstant} = .*?;`); + source = source.replace(dirRegex, `const ${dirConstant} = ${JSON.stringify(overridePath)};`); + return runSourceViaTempFile(source); +} + +function readCanonicalAgent(file) { + const resolvedPath = path.resolve(canonicalAgentsDir, file); + const agentsRoot = path.resolve(canonicalAgentsDir); + assert.ok( + resolvedPath.startsWith(`${agentsRoot}${path.sep}`), + `${file} should resolve inside the canonical agents directory` + ); + return fs.readFileSync(resolvedPath, 'utf8'); +} + +function runTests() { + console.log('\n=== Testing validate-agents tools frontmatter ===\n'); + + let passed = 0; + let failed = 0; + + if (test('canonical agents declare tools as comma-separated scalars', () => { + const agentFiles = fs.readdirSync(canonicalAgentsDir).filter(file => file.endsWith('.md')); + + for (const file of agentFiles) { + const content = readCanonicalAgent(file); + const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); + assert.ok(frontmatter, `${file} should have frontmatter`); + + const toolsLine = frontmatter[1].match(/^tools:\s*(.+)$/m); + assert.ok(toolsLine, `${file} should declare a non-empty tools scalar`); + assert.ok( + !toolsLine[1].trim().startsWith('['), + `${file} should use comma-separated scalar tools, not a YAML sequence` + ); + } + })) passed++; else failed++; + + if (test('accepts comma-separated scalar agent tools', () => { + const testDir = createTestDir(); + try { + fs.writeFileSync(path.join(testDir, 'scalar-tools.md'), '---\nmodel: sonnet\ntools: Read, Glob, Grep\n---\n# Agent'); + + const result = runValidatorWithDir('validate-agents', 'AGENTS_DIR', testDir); + assert.strictEqual(result.code, 0, `Should accept scalar tools, got stderr: ${result.stderr}`); + } finally { + cleanupTestDir(testDir); + } + })) passed++; else failed++; + + if (test('rejects YAML sequence-form agent tools', () => { + const testDir = createTestDir(); + try { + fs.writeFileSync(path.join(testDir, 'sequence-tools.md'), '---\nmodel: sonnet\ntools: [Read, Glob, Grep]\n---\n# Agent'); + + const result = runValidatorWithDir('validate-agents', 'AGENTS_DIR', testDir); + assert.strictEqual(result.code, 1, 'Should reject sequence-form tools'); + assert.ok( + result.stderr.includes('comma-separated scalar'), + `Should explain the supported tools format, got stderr: ${result.stderr}` + ); + } finally { + cleanupTestDir(testDir); + } + })) passed++; else failed++; + + if (test('rejects block sequence-form agent tools', () => { + const testDir = createTestDir(); + try { + fs.writeFileSync(path.join(testDir, 'block-sequence-tools.md'), '---\nmodel: sonnet\ntools:\n - Read\n - Glob\n - Grep\n---\n# Agent'); + + const result = runValidatorWithDir('validate-agents', 'AGENTS_DIR', testDir); + assert.strictEqual(result.code, 1, 'Should reject block sequence-form tools'); + assert.ok( + result.stderr.includes('comma-separated scalar'), + `Should explain the supported tools format, got stderr: ${result.stderr}` + ); + } finally { + cleanupTestDir(testDir); + } + })) passed++; else failed++; + + if (test('rejects explicitly tagged YAML sequence-form agent tools', () => { + const testDir = createTestDir(); + try { + fs.writeFileSync(path.join(testDir, 'tagged-sequence-tools.md'), '---\nmodel: sonnet\ntools: !!seq [Read, Glob, Grep]\n---\n# Agent'); + + const result = runValidatorWithDir('validate-agents', 'AGENTS_DIR', testDir); + assert.strictEqual(result.code, 1, 'Should reject tagged sequence-form tools'); + assert.ok( + result.stderr.includes('comma-separated scalar'), + `Should explain the supported tools format, got stderr: ${result.stderr}` + ); + } finally { + cleanupTestDir(testDir); + } + })) passed++; else failed++; + + console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); + process.exit(failed > 0 ? 1 : 0); +} + +runTests(); diff --git a/tests/lib/agent-compress.test.js b/tests/lib/agent-compress.test.js index 34634cf87..306e1be50 100644 --- a/tests/lib/agent-compress.test.js +++ b/tests/lib/agent-compress.test.js @@ -45,6 +45,29 @@ function runTests() { passed++; else failed++; + if ( + test('parseFrontmatter normalizes comma-separated scalar tools to an array', () => { + const content = '---\nname: scalar-tools\ndescription: Scalar tools\ntools: Read, Glob, Grep\nmodel: sonnet\n---\n\nBody.'; + const { frontmatter } = parseFrontmatter(content); + assert.deepStrictEqual(frontmatter.tools, ['Read', 'Glob', 'Grep']); + }) + ) + passed++; + else failed++; + + if ( + test('parseFrontmatter preserves commas inside scoped tool arguments', () => { + const content = '---\nname: scoped-tools\ndescription: Scoped tools\ntools: Agent(worker, researcher), Read, Bash\nmodel: sonnet\n---\n\nBody.'; + const { frontmatter } = parseFrontmatter(content); + assert.deepStrictEqual( + frontmatter.tools, + ['Agent(worker, researcher)', 'Read', 'Bash'] + ); + }) + ) + passed++; + else failed++; + if ( test('parseFrontmatter handles content without frontmatter', () => { const content = 'Just a regular markdown file.'; @@ -155,7 +178,7 @@ function runTests() { // Create a temp directory with test agent files const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-compress-test-')); - const agentContent = '---\nname: test-agent\ndescription: A test agent\ntools: ["Read"]\nmodel: haiku\n---\n\nTest agent body paragraph.\n\n## Details\nMore info.'; + const agentContent = '---\nname: test-agent\ndescription: A test agent\ntools: Read\nmodel: haiku\n---\n\nTest agent body paragraph.\n\n## Details\nMore info.'; fs.writeFileSync(path.join(tmpDir, 'test-agent.md'), agentContent); fs.writeFileSync(path.join(tmpDir, 'not-an-agent.txt'), 'ignored'); @@ -332,6 +355,10 @@ function runTests() { if (!fs.existsSync(realAgentsDir)) return; // skip if not present const result = buildAgentCatalog(realAgentsDir, { mode: 'catalog' }); assert.ok(result.agents.length > 0, 'Should find at least one agent'); + assert.ok( + result.agents.every(agent => Array.isArray(agent.tools) && agent.tools.length > 0), + 'Every catalog agent should retain its tools as a non-empty array' + ); assert.ok(result.stats.compressedBytes < result.stats.originalBytes, 'Catalog should be smaller than original'); // Verify significant compression ratio const ratio = result.stats.compressedBytes / result.stats.originalBytes; diff --git a/tests/scripts/dashboard-web.test.js b/tests/scripts/dashboard-web.test.js index ed837caa5..88861b6ca 100644 --- a/tests/scripts/dashboard-web.test.js +++ b/tests/scripts/dashboard-web.test.js @@ -36,6 +36,29 @@ function cleanup(dirPath) { fs.rmSync(dirPath, { recursive: true, force: true }); } +function withTempDir(prefix, fn) { + const dirPath = createTempDir(prefix); + try { + return fn(dirPath); + } finally { + cleanup(dirPath); + } +} + +test('withTempDir removes temp directories when the callback throws', () => { + let createdDir = ''; + assert.throws(() => { + withTempDir('ecc-test-', dirPath => { + createdDir = dirPath; + assert.ok(fs.existsSync(createdDir)); + throw new Error('fixture failure'); + }); + }, /fixture failure/); + + assert.ok(createdDir); + assert.ok(!fs.existsSync(createdDir)); +}); + function writeFile(rootDir, relativePath, content) { const targetPath = path.join(rootDir, relativePath); fs.mkdirSync(path.dirname(targetPath), { recursive: true }); @@ -125,6 +148,43 @@ test('readFrontmatter parses array tools field', () => { cleanup(testRoot); }); +test('readFrontmatter preserves scoped tools in legacy flow sequences', () => { + const { readFrontmatter } = require(SCRIPT); + withTempDir('ecc-test-', tempDir => { + writeFile(tempDir, 'agent.md', [ + '---', + 'name: scoped-agent', + 'tools: [Agent(worker, researcher), Read, Bash(git commit:*, git status:*)]', + '---', + 'body', + ].join('\n')); + + const fm = readFrontmatter(path.join(tempDir, 'agent.md')); + assert.deepStrictEqual(fm.tools, [ + 'Agent(worker, researcher)', + 'Read', + 'Bash(git commit:*, git status:*)', + ]); + }); +}); + +test('readFrontmatter normalizes comma-separated scalar tools to an array', () => { + const { readFrontmatter } = require(SCRIPT); + testRoot = createTempDir('ecc-test-'); + writeFile(testRoot, 'agent.md', [ + '---', + 'name: test-agent', + 'tools: Bash, Read, Write', + '---', + '# Body', + ].join('\n')); + + const fm = readFrontmatter(path.join(testRoot, 'agent.md')); + assert.ok(Array.isArray(fm.tools)); + assert.deepStrictEqual(fm.tools, ['Bash', 'Read', 'Write']); + cleanup(testRoot); +}); + test('readFrontmatter handles quoted values', () => { const { readFrontmatter } = require(SCRIPT); testRoot = createTempDir('ecc-test-'); @@ -202,7 +262,7 @@ test('loadAgents loads agent markdown files', () => { 'name: typescript-reviewer', 'description: Reviews TypeScript code', 'model: claude-sonnet-4-6', - 'tools: [Bash, Read, Write, Grep]', + 'tools: Bash, Read, Write, Grep', '---', '# TypeScript Reviewer', 'You are a TypeScript code reviewer.', @@ -212,7 +272,7 @@ test('loadAgents loads agent markdown files', () => { 'name: python-reviewer', 'description: Reviews Python code', 'model: claude-opus-4-8', - 'tools: [Bash, Read]', + 'tools: Bash, Read', '---', '# Python Reviewer', ].join('\n')); diff --git a/tests/scripts/gemini-adapt-agents.test.js b/tests/scripts/gemini-adapt-agents.test.js index 4afc419d4..d8db764a9 100644 --- a/tests/scripts/gemini-adapt-agents.test.js +++ b/tests/scripts/gemini-adapt-agents.test.js @@ -99,6 +99,36 @@ function runTests() { } })) passed++; else failed++; + if (test('adapts comma-separated scalar Claude Code tools', () => { + const tempDir = createTempDir(); + const agentsDir = path.join(tempDir, '.gemini', 'agents'); + + try { + writeAgent( + agentsDir, + 'docs-lookup.md', + [ + '---', + 'name: docs-lookup', + 'description: Documentation lookup agent', + 'tools: Read, Grep, mcp__context7__resolve-library-id, mcp__context7__query-docs', + 'model: sonnet', + '---', + '', + 'Body' + ].join('\n') + ); + + const result = run([agentsDir]); + assert.strictEqual(result.code, 0, result.stderr); + + const updated = fs.readFileSync(path.join(agentsDir, 'docs-lookup.md'), 'utf8'); + assert.ok(updated.includes('tools: ["read_file", "grep_search", "mcp_context7_resolve_library_id", "mcp_context7_query_docs"]')); + } finally { + cleanupTempDir(tempDir); + } + })) passed++; else failed++; + if (test('defaults to the cwd .gemini/agents directory', () => { const tempDir = createTempDir(); const agentsDir = path.join(tempDir, '.gemini', 'agents');