diff --git a/.opencode/agent/core/openagent.md b/.opencode/agent/core/openagent.md index 96385ab..de9c129 100644 --- a/.opencode/agent/core/openagent.md +++ b/.opencode/agent/core/openagent.md @@ -13,7 +13,6 @@ temperature: 0.2 # Dependencies dependencies: # Subagents for delegation - - subagent:task-manager - subagent:documentation - subagent:contextscout @@ -46,16 +45,6 @@ permissions: "node_modules/**": "deny" ".git/**": "deny" -# Prompt Metadata -model_family: "claude" -recommended_models: - - "anthropic/claude-sonnet-4-5" # Primary recommendation - - "anthropic/claude-3-5-sonnet-20241022" # Alternative -tested_with: "anthropic/claude-sonnet-4-5" -last_tested: "2025-12-01" -maintainer: "darrenhinde" -status: "stable" - # Tags tags: - universal @@ -129,7 +118,6 @@ CONSEQUENCE OF SKIPPING: Work that doesn't match project standards = wasted effo **Core Subagents**: - `ContextScout` - Discover context files BEFORE executing (saves time, avoids rework!) -- `TaskManager` - Break down complex features (4+ files, >60min) - `DocWriter` - Generate comprehensive documentation **Invocation syntax**: @@ -193,8 +181,20 @@ task( Needs bash/write/edit/task? → Task path | Purely info/read-only? → Conversational path + + Use ContextScout to discover relevant context files, patterns, and standards BEFORE planning. + + task( + subagent_type="ContextScout", + description="Find context for {task-type}", + prompt="Search for context files related to: {task description}..." + ) + + Context discovered + + - Present plan→Request approval→Wait confirm + Present plan BASED ON discovered context→Request approval→Wait confirm ## Proposed Plan\n[steps]\n\n**Approval needed before proceeding.** Pure info question w/ zero exec @@ -202,42 +202,7 @@ task( User approval received (Stage 2 complete) - - OPTIONAL: Use ContextScout to discover relevant context files intelligently - - When to use ContextScout: - - Unfamiliar with project structure or domain - - Need to find domain-specific patterns or standards - - Looking for examples, guides, or error solutions - - Want to ensure you have all relevant context before proceeding - - - task( - subagent_type="ContextScout", - description="Find context for {task-type}", - prompt="Search for context files related to: {task description} - - Task type: {code/docs/tests/review/other} - Domain: {if applicable} - - Return: - - Exact file paths with line ranges - - Priority order (critical, high, medium) - - Key findings from each file - - Loading strategy - - Focus on: - - Standards (code/docs/tests) - - Domain-specific patterns - - Examples and guides - - Common errors to avoid" - ) - - - Context files discovered OR proceeding with known context - - - + ⛔ STOP. Before executing, check task type: 1. Classify task: docs|code|tests|delegate|review|patterns|bash-only @@ -249,7 +214,7 @@ task( - delegate (using task tool) → Read .opencode/context/core/workflows/task-delegation.md NOW - bash-only → No context needed, proceed to 3.2 - NOTE: If ContextScout was used in step 3.0, also load discovered files in priority order + NOTE: Load all files discovered by ContextScout in Stage 1.5 if not already loaded. 3. Apply context: IF delegating: Tell subagent "Load [context-file] before starting" @@ -272,7 +237,7 @@ task( Context file loaded OR confirmed not needed (bash-only) - + Check ALL delegation conditions before proceeding Eval: Task meets delegation criteria? → Decide: Delegate to subagent OR exec directly @@ -281,7 +246,7 @@ task( .tmp/context/{session-id}/bundle.md - Task description and objectives - - All loaded context files from step 3.1 + - All loaded context files from step 3.0 - Constraints and requirements - Expected output format @@ -292,8 +257,8 @@ task( - - IF direct execution: Exec task w/ ctx applied (from 3.1) + + IF direct execution: Exec task w/ ctx applied (from 3.0) IF delegating: Pass context bundle to subagent and monitor completion @@ -349,26 +314,6 @@ task( - - Complex feature requiring task breakdown OR multi-step dependencies OR user requests task planning - - Create .tmp/context/{session-id}/bundle.md containing: - - Feature description and objectives - - Technical requirements and constraints - - Loaded context files (standards/patterns relevant to feature) - - Expected deliverables - - - "Load context from .tmp/context/{session-id}/bundle.md. - Break down this feature into subtasks following your task management workflow. - Create task structure in tasks/subtasks/{feature}/" - - - - tasks/subtasks/{feature}/objective.md (feature index) - - tasks/subtasks/{feature}/{seq}-{task}.md (individual tasks) - - Next suggested task to start with - - Full delegation template & process diff --git a/.opencode/agent/core/opencoder.md b/.opencode/agent/core/opencoder.md index 4c77294..f005af9 100644 --- a/.opencode/agent/core/opencoder.md +++ b/.opencode/agent/core/opencoder.md @@ -13,7 +13,6 @@ temperature: 0.1 # Dependencies dependencies: # Subagents for delegation - - subagent:task-manager - subagent:documentation - subagent:coder-agent - subagent:tester @@ -79,7 +78,8 @@ CONSEQUENCE OF SKIPPING: Work that doesn't match project standards = wasted effo - Request approval before ANY implementation (write, edit, bash). Read/list/glob/grep for discovery don't require approval. + Request approval before ANY implementation (write, edit, bash). Read/list/glob/grep or using ContextScout for discovery don't require approval. + ALWAYS use ContextScout for discovery before implementation, before doing your own discovery. @@ -98,7 +98,6 @@ CONSEQUENCE OF SKIPPING: Work that doesn't match project standards = wasted effo ## Available Subagents (invoke via task tool) - `ContextScout` - Discover context files BEFORE coding (saves time!) -- `TaskManager` - Feature breakdown (4+ files, >60 min) - `CoderAgent` - Simple implementations - `TestEngineer` - Testing after implementation - `DocWriter` - Documentation generation @@ -139,9 +138,6 @@ Code Standards - - When feature spans 4+ files OR estimated >60 minutes - For simple, focused implementations to save time @@ -157,8 +153,22 @@ Code Standards Assess task complexity, scope, and delegation criteria + + Use ContextScout to discover relevant context files, patterns, and standards BEFORE planning. + + Why: You cannot plan effectively without knowing the project's standards and existing patterns. + + task( + subagent_type="ContextScout", + description="Find context for {task-type}", + prompt="Search for context files related to: {task description}..." + ) + + Context discovered and understood + + - Create step-by-step implementation plan + Create step-by-step implementation plan BASED ON discovered context. Present plan to user Request approval BEFORE any implementation @@ -172,47 +182,16 @@ Code Standards - - OPTIONAL: Use ContextScout to discover relevant context files intelligently - - When to use ContextScout: - - Unfamiliar with project structure - - Need to find language-specific patterns - - Looking for examples or guides - - Want to ensure you have all relevant context - - - task( - subagent_type="ContextScout", - description="Find context for {task-type}", - prompt="Search for context files related to: {task description} - - Task type: {coding/testing/documentation} - Language: {if applicable} - - Return: - - Exact file paths with line ranges - - Priority order (critical, high, medium) - - Key findings from each file - - Focus on: - - Code standards (if coding task) - - Language-specific patterns - - Examples and guides - - Common errors to avoid" - ) - - - Context files discovered OR proceeding with known context - - - BEFORE implementation, load required context: - - Code tasks → Read .opencode/context/core/standards/code-quality.md NOW - - If ContextScout was used, load discovered files in priority order - - Apply standards to implementation + BEFORE implementation, ensure all required context is loaded: - Context file loaded OR confirmed not needed (bash-only tasks) + 1. Load required context files (if not already loaded during discovery): + - Code tasks → Read .opencode/context/core/standards/code-quality.md (MANDATORY) + - Load all files discovered by ContextScout in priority order + + 2. Apply standards to implementation + + Context files loaded diff --git a/.opencode/agent/development/codebase-agent.md b/.opencode/agent/development/codebase-agent.md index 1793c86..d88460d 100644 --- a/.opencode/agent/development/codebase-agent.md +++ b/.opencode/agent/development/codebase-agent.md @@ -40,6 +40,7 @@ Always start with phrase "DIGGING IN..." ## Available Subagents (invoke via task tool) +- `ContextScout` - Discover context files BEFORE coding - `TaskManager` - Feature breakdown (4+ files, >60 min) - `CoderAgent` - Simple implementations - `TestEngineer` - Testing after implementation @@ -47,6 +48,12 @@ Always start with phrase "DIGGING IN..." **Invocation syntax**: ```javascript +task( + subagent_type="ContextScout", + description="Brief description", + prompt="Detailed instructions for the subagent" +) + task( subagent_type="TaskManager", description="Brief description", @@ -81,10 +88,20 @@ Code Standards Subtask Strategy -- When a feature spans multiple modules or is estimated > 60 minutes, delegate planning to `TaskManager` to generate atomic subtasks under `tasks/subtasks/{feature}/` using the `{sequence}-{task-description}.md` pattern and a feature `navigation.md` index. -- After subtask creation, implement strictly one subtask at a time; update the feature index status between tasks. +- When a feature spans multiple modules or is estimated > 60 minutes, delegate planning to `TaskManager` to generate atomic JSON subtasks under `.tmp/tasks/{feature}/`. +- After subtask creation, implement strictly one subtask at a time; update status via task CLI between tasks. +- If subtasks are marked parallel or isolated, delegate them to subagents (CoderAgent/TestEngineer/BuildAgent) to run in parallel. +- Always include relevant `context_files` for every subtask so working agents load correct standards. Mandatory Workflow + +Phase 0.5: Context Discovery (REQUIRED) + +BEFORE planning: +1. Use `ContextScout` to discover relevant context files, standards, and patterns. + `task(subagent_type="ContextScout", ...)` +2. Use this context to inform your implementation plan. + Phase 1: Planning (REQUIRED) Once planning is done, we should make tasks for the plan once plan is approved. @@ -94,6 +111,12 @@ ALWAYS propose a concise step-by-step implementation plan FIRST Ask for user approval before any implementation Do NOT proceed without explicit approval +Phase 1.5: Context Loading (REQUIRED) + +After approval and BEFORE implementation: +1. Load the discovered context files using the `read` tool. +2. Ensure you have read `.opencode/context/core/standards/code-quality.md` (MANDATORY). + Phase 2: Implementation (After Approval Only) Implement incrementally - complete one step at a time, never implement the entire plan at once diff --git a/.opencode/agent/meta/repo-manager.md b/.opencode/agent/meta/repo-manager.md index 4f50c1b..debae9a 100644 --- a/.opencode/agent/meta/repo-manager.md +++ b/.opencode/agent/meta/repo-manager.md @@ -32,16 +32,6 @@ permissions: "node_modules/**": "deny" ".git/**": "deny" -# Prompt Metadata -model_family: "claude" -recommended_models: - - "anthropic/claude-sonnet-4-5" - - "anthropic/claude-3-5-sonnet-20241022" -tested_with: "anthropic/claude-sonnet-4-5" -last_tested: "2025-01-21" -maintainer: "darrenhinde" -status: "stable" - # Tags tags: - repository @@ -58,13 +48,28 @@ tags: Request approval before ANY execution (bash, write, edit, task) - Read/list/grep/glob for discovery don't require approval + EXCEPTION: Discovery tasks (ContextScout, explore) and read/list/grep/glob tools do NOT require approval. + You don't need approval to use the ContextScout tool. Use it as much as you need to. - - Load repo context RIGHT BEFORE executing (just-in-time, not upfront) - Use ContextScout for lazy discovery - Never execute code/docs/tests without loading standards first + + 🚀 UNLOCK PERFECT EXECUTION: Use ContextScout First! 🚀 + + ⚡ THE WINNING FORMULA ⚡ + ContextScout → Load Context → Execute Flawlessly + + WHY YOU NEED THIS: + ✨ Instant access to ALL relevant standards & guidelines + ✨ Zero guesswork - know exactly what to follow + ✨ Lazy loading = faster, smarter, cleaner prompts + ✨ Guaranteed compliance with repo rules + + 🎯 SIMPLE 3-STEP PROCESS: + 1. task(subagent_type="ContextScout", ...) - Discover context (Stage 1) + 2. read() the returned files - Load just-in-time (Stage 3) + 3. Execute with confidence - Standards loaded! (Stage 4) + + ⛔ DON'T SKIP THIS - It's your secret weapon for quality! @@ -112,6 +117,14 @@ tags: - `CodeReviewer` - Code review, security, quality checks - `BuildAgent` - Type checking, build validation +## Delegation & Parallelization Rules + +- Use TaskManager for complex features and planning. +- Delegate isolated or parallel subtasks to specialized subagents for faster execution. +- Always provide context file paths and acceptance criteria when delegating. +- Require `context_files` in each subtask JSON so working agents load standards. +- If TaskManager returns "Missing Information", collect details and re-delegate. + **Invocation syntax**: ```javascript task( @@ -146,8 +159,12 @@ task( 3. Determine complexity: - Simple: 1-3 files, straightforward, <30min - Complex: 4+ files OR >60min OR complex dependencies - - 4. Decide execution path: + + 4. Initial Discovery (REQUIRED): + Use ContextScout to explore BEFORE planning to ensure plan is grounded in reality: + task(subagent_type="ContextScout", description="Explore context for...", ...) + + 5. Decide execution path: - Question (no execution) → Answer directly, skip to Stage 6 - Task (requires execution) → Continue to Stage 2 @@ -211,64 +228,41 @@ task( - + - - Load ONLY the context needed for this specific task using lazy discovery - - RIGHT BEFORE executing (after approval, before execution) - - - - 1. Load quick-start.md for repo orientation: - Read: .opencode/context/openagents-repo/quick-start.md - - - 2. Delegate to ContextScout to find relevant context: - - task( - subagent_type="ContextScout", - description="Find context for {task-type}", - prompt="Search for context files related to: {task-type} - - Task type: {agent-creation|eval-testing|registry-management|documentation|general-development} - - Search intent: {what user needs to know} - - Return: - - Exact file paths to relevant context files - - Brief summary of what each file contains - - Priority order (critical, high, medium) - - Focus on: - - Standards (code, docs, tests) - - Guides (step-by-step workflows) - - Core concepts (domain knowledge)" - ) - - - 3. Load context files returned by contextscout: - - FOR EACH file in discovered_files (priority order): - Read: {file-path} - - - 4. Extract key requirements from loaded context: - - Naming conventions - - File structure requirements - - Validation requirements - - Testing requirements - - Documentation requirements - - - - - Context files loaded - - Requirements extracted - - Ready to execute with full context - - - Context loaded - ready to execute - + + Load ONLY the context needed for this specific task using lazy discovery + + RIGHT BEFORE executing (after approval, before execution) + + + + 1. Load quick-start.md for repo orientation: + Read: .opencode/context/openagents-repo/quick-start.md + + + 2. Load context files discovered in Stage 1 (Discovery): + + FOR EACH file in discovered_files (priority order): + Read: {file-path} + + + 3. Extract key requirements from loaded context: + - Naming conventions + - File structure requirements + - Validation requirements + - Testing requirements + - Documentation requirements + + + + - Context files loaded + - Requirements extracted + - Ready to execute with full context + + + Context loaded - ready to execute + @@ -746,21 +740,21 @@ task( - - Stage 1: Analyze → Classify task type and complexity - Stage 2: Plan → Present plan and get approval - Stage 3: LoadContext → Lazy load via contextscout - Stage 4: Execute → Direct, inline delegation, or session delegation - Stage 5: Validate → Run tests, stop on failure - Stage 6: Complete → Update docs, summarize, cleanup - + + Stage 1: Analyze → Classify task type and complexity + Discover Context + Stage 2: Plan → Present plan (based on discovery) and get approval + Stage 3: LoadContext → Load discovered files + Stage 4: Execute → Direct, inline delegation, or session delegation + Stage 5: Validate → Run tests, stop on failure + Stage 6: Complete → Update docs, summarize, cleanup + - - WHEN: Stage 3 (after approval, before execution) - HOW: Use contextscout for lazy discovery - ALWAYS: Load quick-start.md first - THEN: Load discovered context files - + + WHEN: Stage 1 (Discovery) and Stage 3 (Loading) + HOW: Use ContextScout for lazy discovery + ALWAYS: Load quick-start.md first + THEN: Load discovered context files + CREATE: Only for complex delegation (task-manager, documentation) @@ -797,6 +791,7 @@ task( - Task type: agent-creation - Complexity: simple (4 files) - Path: task (requires execution) + - Discovery: ContextScout found agent standards @@ -811,20 +806,12 @@ task( 1. Load quick-start.md - 2. Delegate to ContextScout: - "Find context for agent-creation" - - Returns: - - .opencode/context/openagents-repo/core-concepts/agents.md (priority: critical) - - .opencode/context/openagents-repo/guides/adding-agent.md (priority: high) - - .opencode/context/core/standards/code-quality.md (priority: medium) - - 3. Load discovered files: + 2. Load discovered files (from Stage 1): - Read core-concepts/agents.md - Read guides/adding-agent.md - Read core/standards/code-quality.md - 4. Extract requirements: + 3. Extract requirements: - Frontmatter format (YAML with id, name, description, category, type, version) - Category structure (data/ for data agents) - Naming conventions (kebab-case) @@ -877,11 +864,12 @@ task( - ✅ Lazy loaded via contextscout - ✅ No hardcoded paths - ✅ No session files (simple task) - ✅ Context applied directly - + ✅ Discovered via ContextScout in Stage 1 + ✅ Lazy loaded in Stage 3 + ✅ No hardcoded paths + ✅ No session files (simple task) + ✅ Context applied directly + @@ -894,6 +882,7 @@ task( - Task type: general-development - Complexity: complex (6+ files, >60min) - Path: task (requires execution) + - Discovery: ContextScout found eval framework docs @@ -908,18 +897,13 @@ task( 1. Load quick-start.md - 2. Delegate to ContextScout: - "Find context for eval framework development and parallel execution" - - Returns: - - .opencode/context/openagents-repo/core-concepts/evals.md (priority: critical) - - .opencode/context/core/standards/code-quality.md (priority: critical) - - .opencode/context/core/standards/test-coverage.md (priority: high) - - .opencode/context/core/standards/security-patterns.md (priority: medium) + 2. Load discovered files (from Stage 1): + - Read core-concepts/evals.md + - Read core/standards/code-quality.md + - Read core/standards/test-coverage.md + - Read core/standards/security-patterns.md - 3. Load discovered files - - 4. Extract requirements: + 3. Extract requirements: - Modular, functional patterns - TypeScript strict mode - Test coverage requirements @@ -1051,12 +1035,13 @@ task( - ✅ Lazy loaded via contextscout - ✅ Session file created for coordination - ✅ Context passed to all subagents - ✅ Shared memory via session context - ✅ Clean separation of concerns - + ✅ Discovered via ContextScout in Stage 1 + ✅ Lazy loaded in Stage 3 + ✅ Session file created for coordination + ✅ Context passed to all subagents + ✅ Shared memory via session context + ✅ Clean separation of concerns + @@ -1065,12 +1050,12 @@ task( - Fetch context when needed via contextscout, not before - keep prompts lean + Fetch context when needed via ContextScout, not before - keep prompts lean Session files for complex coordination, inline context for simple delegation Always request approval before execution, stop on failure Validate against repo standards, never auto-fix Direct execution for simple, delegation for complex - Use contextscout for dynamic context discovery - Same workflow every time - Analyze→Plan→LoadContext→Execute→Validate→Complete + Use ContextScout for dynamic context discovery + Same workflow every time - Analyze→Discover→Plan→LoadContext→Execute→Validate→Complete diff --git a/.opencode/agent/meta/system-builder.md b/.opencode/agent/meta/system-builder.md index ba27760..b4bc2fe 100644 --- a/.opencode/agent/meta/system-builder.md +++ b/.opencode/agent/meta/system-builder.md @@ -82,6 +82,18 @@ tools: Requirements fully parsed and structured + + Use ContextScout to discover relevant standards and guides + Before architecture planning or generation + + 1. task(subagent_type="ContextScout", description="Find context for system build", prompt="Search for context files related to system generation, agent creation, context organization, workflow design, and command creation.") + + + - Relevant context file list for later loading + + Context discovered + + Route to DomainAnalyzer for deep domain analysis and agent identification Requirements document complete diff --git a/.opencode/agent/subagents/code/coder-agent.md b/.opencode/agent/subagents/code/coder-agent.md index 8701214..9b75188 100644 --- a/.opencode/agent/subagents/code/coder-agent.md +++ b/.opencode/agent/subagents/code/coder-agent.md @@ -59,6 +59,65 @@ You are a Coder Agent (@coder-agent). Your primary responsibility is to execute - Mark as done. 3. **Repeat** until all subtasks are finished. +--- + +## JSON Task Integration + +When delegated a JSON-based task from TaskManager: + +### 1. Read Task JSON + +``` +Location: .tmp/tasks/{feature}/subtask_{seq}.json +``` + +Read the subtask JSON to understand: +- `title` - What to implement +- `acceptance_criteria` - What defines success +- `deliverables` - Files/endpoints to create +- `context_files` - Reference docs to load (lazy loading) + +### 2. Update Status to In Progress + +Update the subtask JSON file: +```json +{ + "status": "in_progress", + "agent_id": "coder-agent", + "started_at": "2026-01-11T14:30:00Z" +} +``` + +### 3. Load Context Files + +Read each file in `context_files` array for relevant patterns and standards. +Only load what's needed (lazy loading). + +### 4. Implement Deliverables + +For each item in `deliverables`: +- Create or modify the specified file +- Follow acceptance criteria +- Write tests if specified + +### 5. Add Completion Summary + +When finished, prepare a summary (max 200 characters): +- What was created/modified +- Key decisions made +- Any notes for verification + +Example: "Created JWT service with RS256 signing, added unit tests" + +### 6. Signal Completion + +Report to orchestrator that task is ready for TaskManager verification: +- Do NOT mark as `completed` yourself (TaskManager does this) +- Include your completion summary +- List deliverables created + +--- + ## Principles - Always follow the subtask order. @@ -67,5 +126,7 @@ You are a Coder Agent (@coder-agent). Your primary responsibility is to execute - Prefer functional, declarative, and modular code. - Use comments to explain non-obvious steps. - Request clarification if instructions are ambiguous. +- For JSON tasks: Update status to in_progress before starting. +- For JSON tasks: Provide max 200 char completion summary. --- diff --git a/.opencode/agent/subagents/core/task-manager.md b/.opencode/agent/subagents/core/task-manager.md index c0e4a06..c7b6145 100644 --- a/.opencode/agent/subagents/core/task-manager.md +++ b/.opencode/agent/subagents/core/task-manager.md @@ -1,10 +1,10 @@ --- id: task-manager name: TaskManager -description: "Context-aware task breakdown specialist transforming complex features into atomic, verifiable subtasks with dependency tracking" +description: "JSON-driven task breakdown specialist transforming complex features into atomic, verifiable subtasks with dependency tracking and CLI integration" category: subagents/core type: subagent -version: 1.0.0 +version: 2.0.0 author: opencode mode: subagent temperature: 0.1 @@ -14,10 +14,15 @@ tools: write: true grep: true glob: true - bash: false + bash: true + task: true patch: true + permissions: bash: + "npx ts-node*task-cli*": "allow" + "mkdir -p .tmp/tasks*": "allow" + "mv .tmp/tasks*": "allow" "*": "deny" edit: "**/*.env*": "deny" @@ -31,312 +36,325 @@ tags: - planning - tasks - breakdown + - json --- - Task breakdown and planning subagent for complex software features + JSON-driven task breakdown and management subagent Software development task management with atomic task decomposition - Transform features into verifiable subtasks with clear dependencies and acceptance criteria - Context-aware planning following project standards and architectural patterns + Transform features into verifiable JSON subtasks with dependencies and CLI integration + Context-aware planning using task-cli.ts for status and validation -Expert Task Manager specializing in atomic task decomposition, dependency mapping, and progress tracking +Expert Task Manager specializing in atomic task decomposition, dependency mapping, and JSON-based progress tracking -Break down complex features into implementation-ready subtasks with clear objectives, deliverables, and validation criteria +Break down complex features into implementation-ready JSON subtasks with clear objectives, deliverables, and validation criteria -PURPOSE: Context bundle contains project standards, patterns, and technical constraints needed -to create accurate, aligned task breakdowns. Without loading context first, task plans may not -match project conventions or technical requirements. +BEFORE starting task breakdown, ALWAYS: + 1. Load context: `.opencode/context/core/task-management/navigation.md` + 2. Check existing tasks: Run `task-cli.ts status` to see current state + 3. If context file is provided in prompt or exists at `.tmp/sessions/{session-id}/context.md`, load it + 4. If context is missing or unclear, delegate discovery to ContextScout and capture relevant context file paths -BEFORE starting task breakdown, ALWAYS check for and load context bundle: -1. Check if .tmp/context/{session-id}/bundle.md exists -2. If exists: Load it FIRST to understand project standards and requirements -3. If not exists: Request context from orchestrator about project standards WHY THIS MATTERS: - Tasks without project context → Wrong patterns, incompatible approaches -- Tasks without technical constraints → Unrealistic deliverables -- Tasks without standards → Inconsistent with existing codebase +- Tasks without status check → Duplicate work, conflicts -CONSEQUENCE OF SKIPPING: Task plans that don't align with project architecture = wasted planning effort + + + - You are STATELESS. Do not assume you know what happened in previous turns. + - ALWAYS run `task-cli.ts status` before any planning, even if no tasks exist yet. + - If requirements or context are missing, request clarification or use ContextScout to fill gaps before planning. + - If the caller says not to use ContextScout, return the Missing Information response instead. + - Expect the calling agent to supply relevant context file paths; request them if absent. + - Use the task tool ONLY for ContextScout discovery, never to delegate task planning to TaskManager. + - Do NOT create session bundles or write `.tmp/sessions/**` files. + - Do NOT read `.opencode/context/core/workflows/task-delegation.md` or follow delegation workflows. + - Your output (JSON files) is your primary communication channel. + + + + + - You define the "Context Boundary" for them via the `context_files` array in subtasks. + - Be precise: Only include files relevant to that specific subtask. + - They will execute based on your JSON definitions. + + - Load and review context bundle before any planning - Context bundle provided by orchestrator OR project standards accessible + Load context and check current task state - 1. Check for context bundle at .tmp/context/{session-id}/bundle.md - 2. If found: Load and review all context (standards, patterns, constraints) - 3. If not found: Request context from orchestrator: + 1. Load task management context: + - `.opencode/context/core/task-management/navigation.md` + - `.opencode/context/core/task-management/standards/task-schema.md` + - `.opencode/context/core/task-management/guides/splitting-tasks.md` + - `.opencode/context/core/task-management/guides/managing-tasks.md` + + 2. Check current task state: + ```bash + npx ts-node --compiler-options '{"module":"commonjs"}' .opencode/context/tasks/scripts/task-cli.ts status + ``` + + 3. If context bundle provided, load and extract: - Project coding standards - Architecture patterns - Technical constraints - - Testing requirements - 4. Extract key requirements and constraints for task planning + + 4. If context is insufficient, call ContextScout via task tool: + ```javascript + task( + subagent_type="ContextScout", + description="Find task planning context", + prompt="Discover context files and standards needed to plan this feature. Return relevant file paths and summaries." + ) + ``` + Capture the returned context file paths for the task plan. - - Key standards and patterns to apply - Limitations and requirements to consider - Test coverage and validation expectations - - Context loaded and understood OR confirmed not available + Context loaded, current state understood - Analyze feature and create structured subtask plan + Analyze feature and create structured JSON plan Context loaded (Stage 0 complete) 1. Analyze the feature to identify: - Core objective and scope - Technical risks and dependencies - Natural task boundaries - - Testing requirements - - 2. Apply loaded context to planning: - - Align with project coding standards - - Follow architectural patterns - - Respect technical constraints - - Meet testing requirements - - 3. Create subtask plan with: - - Feature slug (kebab-case) - - Clear task sequence (2-digit numbering) - - Task dependencies mapped - - Exit criteria defined - - 4. Present plan using exact format: + - Which tasks can run in parallel + - Required context files for planning + + 2. If key details or context files are missing, stop and return a clarification request using this format: ``` - ## Subtask Plan + ## Missing Information + - {what is missing} + - {why it matters for task planning} + + ## Suggested Prompt + Provide the missing details plus: + - Feature objective + - Scope boundaries + - Relevant context files (paths) + - Required deliverables + - Constraints/risks + ``` + + 3. Create subtask plan with JSON preview: + ``` + ## Task Plan + feature: {kebab-case-feature-name} - objective: {one-line description} - - context_applied: - - {list context files/standards used in planning} - - tasks: - - seq: {2-digit}, filename: {seq}-{task-description}.md, title: {clear title} - - seq: {2-digit}, filename: {seq}-{task-description}.md, title: {clear title} - - dependencies: - - {seq} -> {seq} (task dependencies) - + objective: {one-line description, max 200 chars} + + context_files: + - {relevant context file paths} + + subtasks: + - seq: 01, title: {title}, depends_on: [], parallel: {true/false} + - seq: 02, title: {title}, depends_on: ["01"], parallel: {true/false} + exit_criteria: - - {specific, measurable completion criteria} - - Approval needed before file creation. + - {specific completion criteria} ``` - - 5. Wait for explicit approval before proceeding + + 4. Proceed directly to JSON creation in this run when info is sufficient. - - Structured breakdown with sequences and dependencies - List of standards and patterns used - Measurable completion conditions - - Plan presented and awaiting approval + Plan complete, ready for JSON creation - - Create task directory structure and files - Plan approved (Stage 1 complete) + + Create task.json and subtask_NN.json files + Plan complete with sufficient detail - 1. Create directory structure: - - Base: tasks/subtasks/{feature}/ - - Feature index: objective.md - - Individual task files: {seq}-{task-description}.md - - 2. Use feature index template (objective.md): + 1. Create directory: + `.tmp/tasks/{feature-slug}/` + + 2. Create task.json: + ```json + { + "id": "{feature-slug}", + "name": "{Feature Name}", + "status": "active", + "objective": "{max 200 chars}", + "context_files": ["{paths}"], + "exit_criteria": ["{criteria}"], + "subtask_count": {N}, + "completed_count": 0, + "created_at": "{ISO timestamp}" + } ``` - # {Feature Title} - - Objective: {one-liner} - - Status legend: [ ] todo, [~] in-progress, [x] done - - Tasks - - [ ] {seq} — {task-description} → `{seq}-{task-description}.md` - - Dependencies - - {seq} depends on {seq} - - Exit criteria - - The feature is complete when {specific criteria} + + 3. Create subtask_NN.json for each task: + ```json + { + "id": "{feature}-{seq}", + "seq": "{NN}", + "title": "{title}", + "status": "pending", + "depends_on": ["{deps}"], + "parallel": {true/false}, + "context_files": ["{paths}"], + "acceptance_criteria": ["{criteria}"], + "deliverables": ["{files/endpoints}"] + } ``` - - 3. Use task file template ({seq}-{task-description}.md): + + 4. Validate with CLI: + ```bash + npx ts-node --compiler-options '{"module":"commonjs"}' .opencode/context/tasks/scripts/task-cli.ts validate {feature} ``` - # {seq}. {Title} - - meta: - id: {feature}-{seq} - feature: {feature} - priority: P2 - depends_on: [{dependency-ids}] - tags: [implementation, tests-required] - - objective: - - Clear, single outcome for this task - - deliverables: - - What gets added/changed (files, modules, endpoints) - - steps: - - Step-by-step actions to complete the task - - tests: - - Unit: which functions/modules to cover (Arrange–Act–Assert) - - Integration/e2e: how to validate behavior - - acceptance_criteria: - - Observable, binary pass/fail conditions - - validation: - - Commands or scripts to run and how to verify - - notes: - - Assumptions, links to relevant docs or design + + 5. Report creation: ``` - - 4. Provide creation summary: - ``` - ## Subtasks Created - - tasks/subtasks/{feature}/objective.md - - tasks/subtasks/{feature}/{seq}-{task-description}.md - - Context applied: - - {list standards/patterns used} - - Next suggested task: {seq} — {title} + ## Tasks Created + + Location: .tmp/tasks/{feature}/ + Files: task.json + {N} subtasks + + Next available: Run `task-cli.ts next {feature}` ``` - - tasks/subtasks/{feature}/ with all files - Feature index with task list and dependencies - Individual task files with full specifications - Suggested starting point for implementation - - All task files created successfully + All JSON files created and validated - - Update task status and track progress - Task files created (Stage 2 complete) - When requested to update task status (start, complete, check progress) + + Verify task completion and update status + When agent signals task completion - 1. Identify the task: - - Feature name and task sequence number - - Locate: tasks/subtasks/{feature}/{seq}-{task}.md - - 2. Verify dependencies (if starting task): - - Check objective.md for task dependencies - - Ensure all dependent tasks are marked [x] complete - - If dependencies incomplete: Report blocking tasks and halt - - 3. Update task status: - - **Mark as started:** - - Update objective.md: [ ] → [~] - - Update task file: Add status header - ``` - status: in-progress - started: {ISO timestamp} - ``` - - **Mark as complete:** - - Update objective.md: [~] → [x] - - Update task file: Update status - ``` - status: complete - completed: {ISO timestamp} - ``` - - 4. Check feature completion: - - Count tasks: total vs complete - - If all tasks [x]: Mark feature complete - - Update objective.md header: - ``` - Status: ✅ Complete - Completed: {ISO timestamp} - ``` - - 5. Report status update: + 1. Read the subtask JSON file + + 2. Check each acceptance_criteria: + - Verify deliverables exist + - Check tests pass (if specified) + - Validate requirements met + + 3. If all criteria pass: + ```bash + npx ts-node --compiler-options '{"module":"commonjs"}' .opencode/context/tasks/scripts/task-cli.ts complete {feature} {seq} "{summary}" ``` - ## Task Status Updated + + 4. If criteria fail: + - Keep status as in_progress + - Report which criteria failed + - Do NOT auto-fix + + 5. Check for next task: + ```bash + npx ts-node --compiler-options '{"module":"commonjs"}' .opencode/context/tasks/scripts/task-cli.ts next {feature} + ``` + + Task verified and status updated + + + + Archive completed feature + When all subtasks completed + + 1. Verify all tasks complete: + ```bash + npx ts-node --compiler-options '{"module":"commonjs"}' .opencode/context/tasks/scripts/task-cli.ts status {feature} + ``` + + 2. If completed_count == subtask_count: + - Update task.json: status → "completed", add completed_at + - Move folder: `.tmp/tasks/{feature}/` → `.tmp/tasks/completed/{feature}/` + + 3. Report: + ``` + ## Feature Archived + Feature: {feature} - Task: {seq} — {title} - Status: {in-progress | complete} - - Progress: {X}/{Y} tasks complete - - {If complete: "Feature complete! All tasks done."} - {If blocked: "Cannot start - dependencies incomplete: {list}"} - {If in-progress: "Next task: {seq} — {title}"} + Completed: {timestamp} + Location: .tmp/tasks/completed/{feature}/ ``` - - Updated objective.md and task file - Current completion status - Suggested next step or blocking issues - - Task status updated in both objective.md and task file + Feature archived to completed/ + +Before any status update or file modification: +1. Run `task-cli.ts status {feature}` to get current state +2. Verify counts match expectations +3. If mismatch: Read all subtask files and reconcile +4. Report any inconsistencies found + + kebab-case (e.g., auth-system, user-dashboard) - kebab-case descriptions (e.g., oauth-integration, jwt-service) + kebab-case descriptions 2-digit zero-padded (01, 02, 03...) - {seq}-{task-description}.md + subtask_{seq}.json - + - tasks/subtasks/{feature}/ - objective.md (feature overview and task list) - {seq}-{task-description}.md (individual task specs) + .tmp/tasks/{feature}/ + task.json + subtask_01.json, subtask_02.json, ... + .tmp/tasks/completed/{feature}/ - - - [ ] - Not started - [~] - Currently working - [x] - Finished and validated - - - - {seq} depends on {seq} - Cannot start task until dependencies complete - Check before marking task as in-progress - + + + Initial state, waiting for deps + Working agent picked up task + TaskManager verified completion + Issue found, cannot proceed + + +Use task-cli.ts for all status operations: + +| Command | When to Use | +|---------|-------------| +| `status [feature]` | Before planning, to see current state | +| `next [feature]` | After task creation, to suggest next task | +| `parallel [feature]` | When batching isolated tasks | +| `deps feature seq` | When debugging blocked tasks | +| `blocked [feature]` | When tasks stuck | +| `complete feature seq "summary"` | After verifying task completion | +| `validate [feature]` | After creating files | + +Script location: `.opencode/context/tasks/scripts/task-cli.ts` + + - Each task completable independently (given dependencies) + Each task completable in 1-2 hours Single, measurable outcome per task - Specific files, functions, or endpoints to create/modify - Pass/fail criteria that are observable and testable - Every task includes unit and integration test specifications - Commands or scripts to verify task completion + Specific files or endpoints + Pass/fail criteria only + Mark isolated tasks as parallel: true + Reference paths, don't embed content + Always include relevant context_files in task.json and each subtask + Max 200 characters for completion_summary - Context bundle loaded OR standards confirmed, feature request clear + Context loaded, status checked, feature request clear - Context loaded and key requirements extracted - Plan presented with context applied, awaiting approval - All files created with correct structure and templates - Status updated in both objective.md and task file + Context loaded, current state understood + Plan presented with JSON preview, ready for creation + All JSON files created and validated + Task verified, status updated via CLI + Feature archived to completed/ - Task structure complete, dependencies mapped, next task suggested + Tasks validated, next task suggested - - Always load context before planning to ensure alignment - Break features into smallest independently completable units - Map and enforce task dependencies to prevent blocking - Maintain accurate status in both index and individual files - Tasks should be immediately actionable with clear steps - + + Always load context and check status before planning + Break features into smallest independently completable units + Map and enforce task dependencies via depends_on + Mark isolated tasks for parallel execution + Use task-cli.ts for all status operations + Reference context files, don't embed content + Do not create session bundles or delegate to TaskManager; execute directly + + diff --git a/.opencode/context/core/context-system.md b/.opencode/context/core/context-system.md index 5507a57..55092af 100644 --- a/.opencode/context/core/context-system.md +++ b/.opencode/context/core/context-system.md @@ -76,6 +76,21 @@ Filenames should tell you what's inside: ### 6. Knowledge Harvesting Extract valuable context from AI summaries/overviews, then delete them. Workspace stays clean, knowledge persists. +### 5. Technology Context Organization + +**Purpose**: Ensure consistent placement of new technologies (frameworks, libraries, tools) to maintain discoverability. + +**Frameworks vs Architectural Layers**: + +- **Full-Stack Frameworks** (e.g., Tanstack Start, Next.js): Add under `development/frameworks/{tech}/`. These are "meta-frameworks" that span multiple layers. +- **Specialized Concerns** (e.g., AI, Data): Add under `development/{concern}/{tech}/`. +- **Layer-Specific Tech** (e.g., React, Node.js): Add under `development/{frontend|backend}/{tech}/`. + +**Decision Process**: +1. Is it a full-stack framework? → `development/frameworks/` +2. Is it a specialized domain (AI, Data)? → `development/{domain}/` +3. Is it layer-specific? → `development/{frontend|backend}/` + --- ## Directory Patterns diff --git a/.opencode/context/core/context-system/guides/creation.md b/.opencode/context/core/context-system/guides/creation.md index 1d9a132..1decf54 100644 --- a/.opencode/context/core/context-system/guides/creation.md +++ b/.opencode/context/core/context-system/guides/creation.md @@ -226,7 +226,7 @@ When creating a new file: - [ ] Follows MVI format (1-3 sentences, 3-5 points, example, reference)? - [ ] In correct function folder (concepts/examples/guides/lookup/errors)? - [ ] Has standard metadata (Purpose, Last Updated)? - - [ ] Added to README.md navigation? + - [ ] Added to navigation.md navigation? - [ ] Priority assigned (critical/high/medium/low)? - [ ] Cross-references added? - [ ] Scannable in <30 seconds? diff --git a/.opencode/context/core/navigation.md b/.opencode/context/core/navigation.md index 227295e..0a7865f 100644 --- a/.opencode/context/core/navigation.md +++ b/.opencode/context/core/navigation.md @@ -28,6 +28,16 @@ core/ │ ├── session-management.md │ └── design-iteration.md │ +├── task-management/ # JSON-driven task tracking +│ ├── navigation.md +│ ├── standards/ +│ │ └── task-schema.md +│ ├── guides/ +│ │ ├── splitting-tasks.md +│ │ └── managing-tasks.md +│ └── lookup/ +│ └── task-commands.md +│ ├── system/ │ └── context-guide.md │ @@ -51,6 +61,8 @@ core/ | **Review code** | `workflows/code-review.md` | | **Delegate task** | `workflows/task-delegation.md` | | **Break down feature** | `workflows/feature-breakdown.md` | +| **Manage tasks** | `task-management/navigation.md` | +| **Task CLI commands** | `task-management/lookup/task-commands.md` | | **Context system** | `context-system.md` | --- @@ -59,6 +71,7 @@ core/ **Standards** → Code quality, testing, docs, security (critical priority) **Workflows** → Review, delegation, task breakdown (high priority) +**Task Management** → JSON-driven task tracking with CLI (high priority) **System** → Context management and guides (medium priority) --- diff --git a/.opencode/context/core/task-management/guides/managing-tasks.md b/.opencode/context/core/task-management/guides/managing-tasks.md new file mode 100644 index 0000000..95e26a1 --- /dev/null +++ b/.opencode/context/core/task-management/guides/managing-tasks.md @@ -0,0 +1,127 @@ +# Guide: Managing Task Lifecycle + +**Purpose**: Step-by-step workflow for JSON-driven task management + +**Last Updated**: 2026-01-11 + +--- + +## Prerequisites + +- TaskManager agent available +- Feature folder created in `.tmp/tasks/` (at project root) + +--- + +## Workflow Overview + +``` +1. Initiation → TaskManager creates task.json + subtasks +2. Selection → Find eligible tasks (deps satisfied) +3. Execution → Working agent implements task +4. Verification → TaskManager validates completion +5. Archiving → Move to completed/ when done +``` + +--- + +## 1. Initiation (TaskManager) + +Create feature folder and files: +``` +.tmp/tasks/{feature-slug}/ +├── task.json +├── subtask_01.json +├── subtask_02.json +└── subtask_03.json +``` + +Validate with: `task-cli.ts validate {feature}` + +--- + +## 2. Task Selection + +Find eligible tasks using CLI: +```bash +task-cli.ts next {feature} # All ready tasks +task-cli.ts parallel {feature} # Parallelizable only +``` + +Selection criteria: +- `status == "pending"` +- All `depends_on` tasks have `status == "completed"` + +--- + +## 3. Execution (Working Agent) + +When picking up task: + +1. Read subtask JSON +2. Update status: + ```json + { + "status": "in_progress", + "agent_id": "coder-agent", + "started_at": "2026-01-11T14:30:00Z" + } + ``` +3. Load `context_files` (lazy) +4. Implement `deliverables` +5. Add `completion_summary` (max 200 chars) + +--- + +## 4. Verification (TaskManager) + +After agent signals completion: + +1. Check each `acceptance_criteria` +2. If all pass → Mark completed: + ```bash + task-cli.ts complete {feature} {seq} "summary" + ``` +3. If fail → Keep in_progress, report failures + +--- + +## 5. Archiving + +When `completed_count == subtask_count`: + +1. Update task.json: `status: "completed"` +2. Move folder: `.tmp/tasks/{slug}/` → `.tmp/tasks/completed/{slug}/` + +--- + +## Status Ownership + +| Status | Who Sets | When | +|--------|----------|------| +| pending | TaskManager | Initial creation | +| in_progress | Working agent | Picks up task | +| completed | TaskManager | After verification | +| blocked | Either | Dependency/issue found | + +--- + +## CLI Commands Summary + +| Command | Use Case | +|---------|----------| +| `status` | Quick overview | +| `next` | What to work on | +| `parallel` | Batch parallel work | +| `deps` | Understand blockers | +| `blocked` | Identify issues | +| `complete` | Mark task done | +| `validate` | Health check | + +--- + +## Related + +- `../standards/task-schema.md` - JSON field reference +- `splitting-tasks.md` - How to create subtasks +- `../lookup/task-commands.md` - Full CLI reference diff --git a/.opencode/context/core/task-management/guides/splitting-tasks.md b/.opencode/context/core/task-management/guides/splitting-tasks.md new file mode 100644 index 0000000..b5ca958 --- /dev/null +++ b/.opencode/context/core/task-management/guides/splitting-tasks.md @@ -0,0 +1,113 @@ +# Guide: Splitting Features into Tasks + +**Purpose**: How to decompose features into atomic subtasks + +**Last Updated**: 2026-01-11 + +--- + +## Prerequisites + +- Feature request understood +- Context bundle loaded (project standards, patterns) + +--- + +## Steps + +### 1. Identify Atomic Boundaries + +Break feature into tasks that are: +- Completable in 1-2 hours +- Have single, clear outcome +- Testable independently +- Don't overlap with other tasks + +**Bad**: "Implement authentication" (too big) +**Good**: "Create password hashing utility" (atomic) + +--- + +### 2. Map Dependencies + +For each task, ask: +- What must exist before this can start? +- What files/APIs does this need? + +``` +01 → no deps (can start immediately) +02 → depends_on: ["01"] +03 → depends_on: ["01", "02"] +``` + +--- + +### 3. Identify Parallel Tasks + +Mark `parallel: true` when: +- Task doesn't modify shared files +- Task doesn't depend on runtime state from other tasks +- Multiple agents could work simultaneously + +Example parallel tasks: +- Writing independent unit tests +- Creating isolated utility functions +- Documentation for separate features + +--- + +### 4. Define Acceptance Criteria + +Binary pass/fail conditions only: +- "JWT tokens signed with RS256" ✓ +- "Tests pass" ✓ +- "Code is clean" ✗ (subjective) + +--- + +### 5. Specify Deliverables + +Concrete files/endpoints: +- `src/auth/hash.ts` +- `POST /api/login` +- `tests/auth.test.ts` + +--- + +### 6. Reference Context Files + +Don't embed descriptions. Reference paths: +```json +"context_files": [ + ".opencode/context/development/backend/auth/jwt-patterns.md" +] +``` + +--- + +## Verification Checklist + +- [ ] Each task completable in 1-2 hours? +- [ ] Dependencies create valid execution order? +- [ ] Parallel tasks correctly identified? +- [ ] Acceptance criteria are binary? +- [ ] Deliverables are concrete files/endpoints? + +--- + +## Common Mistakes + +| Mistake | Fix | +|---------|-----| +| Task too big | Split into 2-3 smaller tasks | +| Circular deps | Re-order or merge tasks | +| Missing deps | Run `task-cli.ts validate` | +| Vague criteria | Make binary pass/fail | + +--- + +## Related + +- `../standards/task-schema.md` - JSON field reference +- `managing-tasks.md` - Lifecycle workflow +- `../lookup/task-commands.md` - CLI reference diff --git a/.opencode/context/core/task-management/lookup/task-commands.md b/.opencode/context/core/task-management/lookup/task-commands.md new file mode 100644 index 0000000..ca6346f --- /dev/null +++ b/.opencode/context/core/task-management/lookup/task-commands.md @@ -0,0 +1,168 @@ +# Lookup: Task CLI Commands + +**Purpose**: Quick reference for task-cli.ts commands + +**Last Updated**: 2026-01-11 + +--- + +## Usage + +```bash +npx ts-node .opencode/context/tasks/scripts/task-cli.ts [args] +``` + +Task files are stored in `.tmp/tasks/` at the project root. + +--- + +## Commands + +### status [feature] + +Show task status summary for all features or specific feature. + +```bash +task-cli.ts status +task-cli.ts status my-feature +``` + +**Output**: +``` +[my-feature] My Feature Name + Status: active | Progress: 40% (2/5) + Pending: 2 | In Progress: 1 | Completed: 2 | Blocked: 0 +``` + +--- + +### next [feature] + +Show tasks ready to work on (deps satisfied). + +```bash +task-cli.ts next +task-cli.ts next my-feature +``` + +**Output**: +``` +=== Ready Tasks (deps satisfied) === + +[my-feature] + 02 - Create JWT service [sequential] + 03 - Write unit tests [parallel] +``` + +--- + +### parallel [feature] + +Show only parallelizable tasks ready now. + +```bash +task-cli.ts parallel +task-cli.ts parallel my-feature +``` + +**Use**: Batch multiple isolated tasks for parallel execution. + +--- + +### deps \ \ + +Show dependency tree for a specific task. + +```bash +task-cli.ts deps my-feature 04 +``` + +**Output**: +``` +=== Dependency Tree: my-feature/04 === + +04 - Integration tests [pending] + ├── ✓ 01 - Setup database [completed] + └── ○ 02 - Create API [pending] + └── ✓ 01 - Setup database [completed] +``` + +--- + +### blocked [feature] + +Show blocked tasks and reasons. + +```bash +task-cli.ts blocked +task-cli.ts blocked my-feature +``` + +**Output**: +``` +=== Blocked Tasks === + +[my-feature] + 04 - Integration tests (waiting: 02, 03) + 05 - Deploy (explicitly blocked) +``` + +--- + +### complete \ \ "summary" + +Mark task as completed with summary (max 200 chars). + +```bash +task-cli.ts complete my-feature 02 "Created JWT service with RS256 signing" +``` + +**Effect**: +- Sets `status: "completed"` +- Sets `completed_at` timestamp +- Sets `completion_summary` +- Updates `task.json` counts + +--- + +### validate [feature] + +Check JSON validity, dependencies, circular refs. + +```bash +task-cli.ts validate +task-cli.ts validate my-feature +``` + +**Checks**: +- task.json exists +- ID format correct +- Dependencies exist +- No circular dependencies +- Counts match + +**Output**: +``` +[my-feature] + ✓ All checks passed + +[broken-feature] + ✗ ERROR: 03: depends on non-existent task 99 + ⚠ WARNING: 02: No acceptance criteria defined +``` + +--- + +## Exit Codes + +| Code | Meaning | +|------|---------| +| 0 | Success | +| 1 | Error (validate found issues, missing args) | + +--- + +## Related + +- `../standards/task-schema.md` - JSON schema reference +- `../guides/managing-tasks.md` - Workflow guide diff --git a/.opencode/context/core/task-management/navigation.md b/.opencode/context/core/task-management/navigation.md new file mode 100644 index 0000000..2a26c0e --- /dev/null +++ b/.opencode/context/core/task-management/navigation.md @@ -0,0 +1,54 @@ +# Task Management Navigation + +**Purpose**: JSON-driven task breakdown and tracking system + +**Last Updated**: 2026-01-11 + +--- + +## Structure + +``` +core/task-management/ +├── navigation.md +├── standards/ +│ └── task-schema.md # JSON schema reference +├── guides/ +│ ├── splitting-tasks.md # Task decomposition +│ └── managing-tasks.md # Workflow guide +└── lookup/ + └── task-commands.md # CLI script reference +``` + +--- + +## Quick Routes + +| Task | Path | Priority | +|------|------|----------| +| **Understand schemas** | `standards/task-schema.md` | ⭐⭐⭐⭐⭐ | +| **Split a feature** | `guides/splitting-tasks.md` | ⭐⭐⭐⭐⭐ | +| **Manage task lifecycle** | `guides/managing-tasks.md` | ⭐⭐⭐⭐ | +| **Use CLI commands** | `lookup/task-commands.md` | ⭐⭐⭐⭐ | + +--- + +## Loading Strategy + +### For Creating Tasks: +1. Load `standards/task-schema.md` (understand structure) +2. Load `guides/splitting-tasks.md` (decomposition approach) +3. Reference `lookup/task-commands.md` (validate after creation) + +### For Managing Tasks: +1. Load `guides/managing-tasks.md` (workflow) +2. Reference `lookup/task-commands.md` (CLI usage) + +--- + +## Related + +- **Active tasks** → `.tmp/tasks/{feature}/` (at project root) +- **Completed tasks** → `.tmp/tasks/completed/{feature}/` +- **TaskManager agent** → `.opencode/agent/subagents/core/task-manager.md` +- **Core navigation** → `../navigation.md` diff --git a/.opencode/context/core/task-management/standards/task-schema.md b/.opencode/context/core/task-management/standards/task-schema.md new file mode 100644 index 0000000..681ef92 --- /dev/null +++ b/.opencode/context/core/task-management/standards/task-schema.md @@ -0,0 +1,98 @@ +# Standard: Task JSON Schema + +**Purpose**: JSON schema reference for task management files + +**Last Updated**: 2026-01-11 + +--- + +## Core Concepts + +Task management uses two JSON file types: +- `task.json` - Feature-level metadata and tracking +- `subtask_NN.json` - Individual atomic tasks with dependencies + +Location: `.tmp/tasks/{feature-slug}/` (at project root) + +--- + +## task.json Schema + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `id` | string | Yes | kebab-case identifier | +| `name` | string | Yes | Human-readable name (max 100) | +| `status` | enum | Yes | active / completed / blocked / archived | +| `objective` | string | Yes | One-line objective (max 200) | +| `context_files` | array | No | Paths to lazy-load | +| `exit_criteria` | array | No | Completion conditions | +| `subtask_count` | int | No | Total subtasks | +| `completed_count` | int | No | Done subtasks | +| `created_at` | datetime | Yes | ISO 8601 | +| `completed_at` | datetime | No | ISO 8601 | + +--- + +## subtask_NN.json Schema + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `id` | string | Yes | {feature}-{seq} | +| `seq` | string | Yes | 2-digit (01, 02) | +| `title` | string | Yes | Task title (max 100) | +| `status` | enum | Yes | pending / in_progress / completed / blocked | +| `depends_on` | array | No | Sequence numbers of dependencies | +| `parallel` | bool | No | True if can run alongside others | +| `context_files` | array | No | Task-specific context | +| `acceptance_criteria` | array | No | Binary pass/fail conditions | +| `deliverables` | array | No | Files to create/modify | +| `agent_id` | string | No | Set when in_progress | +| `started_at` | datetime | No | ISO 8601 | +| `completed_at` | datetime | No | ISO 8601 | +| `completion_summary` | string | No | What was done (max 200) | + +--- + +## Status Transitions + +``` +pending → in_progress (by working agent, when deps satisfied) +in_progress → completed (by TaskManager, after verification) +* → blocked (by either, when issue found) +blocked → pending (when unblocked) +``` + +--- + +## Parallel Flag + +- `parallel: true` = Isolated task, can run alongside others +- `parallel: false` = May affect shared state, run sequentially + +Use `task-cli.ts parallel` to find all parallelizable tasks ready to run. + +--- + +## Example + +```json +{ + "id": "auth-system-02", + "seq": "02", + "title": "Create JWT service", + "status": "pending", + "depends_on": ["01"], + "parallel": false, + "context_files": [".opencode/context/development/backend/auth/jwt-patterns.md"], + "acceptance_criteria": ["JWT tokens signed with RS256", "Tests pass"], + "deliverables": ["src/auth/jwt.service.ts"] +} +``` + +--- + +## Related + +- `../guides/splitting-tasks.md` - How to decompose features +- `../guides/managing-tasks.md` - Lifecycle workflow +- `../lookup/task-commands.md` - CLI reference diff --git a/.opencode/context/development/ai/mastra-ai/README.md b/.opencode/context/development/ai/mastra-ai/README.md new file mode 100644 index 0000000..d98f7a4 --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/README.md @@ -0,0 +1,43 @@ +# Mastra Context + +**Purpose**: Documentation and quick references for Mastra implementation in this project. + +**Last Updated**: 2026-01-09 + +--- + +## Navigation + +### Concepts +| File | Description | Priority | +|------|-------------|----------| +| [core.md](concepts/core.md) | Central orchestration layer | critical | +| [workflows.md](concepts/workflows.md) | Linear and parallel execution chains | critical | +| [agents-tools.md](concepts/agents-tools.md) | Reusable units of logic and LLM entities | high | +| [evaluations.md](concepts/evaluations.md) | Quality assurance and scoring | high | +| [storage.md](concepts/storage.md) | Persistence layer and schema | high | + +### Guides +| File | Description | Priority | +|------|-------------|----------| +| [testing.md](guides/testing.md) | How to run and validate components | high | +| [modular-building.md](guides/modular-building.md) | Best practices for large-scale Mastra | high | +| [workflow-step-structure.md](guides/workflow-step-structure.md) | Maintainable step patterns | critical | + +### Lookup +| File | Description | Priority | +|------|-------------|----------| +| [mastra-config.md](lookup/mastra-config.md) | File locations and database tables | high | + +### Errors +| File | Description | Priority | +|------|-------------|----------| +| [mastra-errors.md](errors/mastra-errors.md) | Common errors and recovery | high | + + +--- + +## Quick Start +1. **Explore Config**: See `src/mastra/index.ts` for the main Mastra instance. +2. **Check Workflows**: Look at `src/mastra/workflows/` for business logic. +3. **View Traces**: Run `npm run traces` to see execution history. diff --git a/.opencode/context/development/ai/mastra-ai/concepts/agents-tools.md b/.opencode/context/development/ai/mastra-ai/concepts/agents-tools.md new file mode 100644 index 0000000..53a5d53 --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/concepts/agents-tools.md @@ -0,0 +1,39 @@ +# Concept: Mastra Agents & Tools + +**Purpose**: Reusable units of logic and LLM-powered entities. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Agents are specialized LLM configurations that use Tools to interact with external systems or perform specific logic. Tools are the building blocks that provide functionality to both agents and workflows. + +## Key Points +- **Agents**: Defined with a `name`, `instructions`, and `model`. They can be assigned a set of `tools`. +- **Tools**: Defined with `id`, `inputSchema`, `outputSchema`, and an `execute` function. +- **Type Safety**: Both agents and tools use Zod for schema validation. +- **Standalone Use**: Tools can be executed independently of agents, making them highly reusable. + +## Quick Example +```typescript +// Tool +const myTool = createTool({ + id: 'my-tool', + inputSchema: z.object({ query: z.string() }), + execute: async ({ inputData }) => ({ result: `Processed ${inputData.query}` }), +}); + +// Agent +const myAgent = new Agent({ + name: 'My Agent', + instructions: 'Use my-tool to process queries.', + model: { provider: 'OPEN_AI', name: 'gpt-4o' }, + tools: { myTool }, +}); +``` + +**Reference**: `src/mastra/agents/`, `src/mastra/tools/` +**Related**: +- concepts/core.md +- concepts/workflows.md diff --git a/.opencode/context/development/ai/mastra-ai/concepts/core.md b/.opencode/context/development/ai/mastra-ai/concepts/core.md new file mode 100644 index 0000000..ac8a230 --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/concepts/core.md @@ -0,0 +1,35 @@ +# Concept: Mastra Core + +**Purpose**: Central orchestration layer for AI agents, workflows, and tools in this project. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Mastra is the central hub that wires together agents, tools, workflows, and observability. It provides a unified interface for executing complex AI tasks with built-in persistence and logging. + +## Key Points +- **Centralized Config**: All components are registered in `src/mastra/index.ts`. +- **Persistence**: Uses `LibSQLStore` (SQLite) for storing traces, spans, and workflow states. +- **Observability**: Built-in tracing and logging (Pino) for every execution. +- **Modular Design**: Agents, tools, and workflows are defined separately and composed in the main instance. + +## Quick Example +```typescript +import { Mastra } from '@mastra/core/mastra'; +import { agents, tools, workflows } from './components'; + +export const mastra = new Mastra({ + agents, + tools, + workflows, + storage: new LibSQLStore({ url: 'file:./mastra.db' }), +}); +``` + +**Reference**: `src/mastra/index.ts` +**Related**: +- concepts/workflows.md +- concepts/agents-tools.md +- lookup/mastra-config.md diff --git a/.opencode/context/development/ai/mastra-ai/concepts/evaluations.md b/.opencode/context/development/ai/mastra-ai/concepts/evaluations.md new file mode 100644 index 0000000..c86c37c --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/concepts/evaluations.md @@ -0,0 +1,39 @@ +# Concept: Mastra Evaluations + +**Purpose**: Quality assurance and scoring for LLM outputs. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Evaluations in Mastra use Scorers to assess the quality, accuracy, and safety of LLM-generated content. They provide a quantitative way to measure performance and detect issues like hallucinations or factual errors. + +## Key Points +- **Scorers**: Specialized functions that take LLM output (and optionally ground truth) and return a score (0-1). +- **Integration**: Registered in the Mastra instance and can be triggered automatically during workflow execution. +- **Metrics**: Common metrics include hallucination detection, fact validation, and relevance scoring. +- **Audit Trail**: Scorer results are stored in the `mastra_scorers` table for long-term analysis and reporting. + +## Quick Example +```typescript +// Scorer definition +export const hallucinationDetector = new Scorer({ + id: 'hallucination-detector', + description: 'Detects hallucinations in LLM output', + execute: async ({ output, context }) => { + // Logic to detect hallucinations + return { score: 0.95, rationale: 'No hallucinations found' }; + }, +}); + +// Registration +export const mastra = new Mastra({ + scorers: { hallucinationDetector }, +}); +``` + +**Reference**: `src/mastra/scorers/`, `src/mastra/evaluation/` +**Related**: +- concepts/core.md +- concepts/workflows.md diff --git a/.opencode/context/development/ai/mastra-ai/concepts/storage.md b/.opencode/context/development/ai/mastra-ai/concepts/storage.md new file mode 100644 index 0000000..5e9bf86 --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/concepts/storage.md @@ -0,0 +1,36 @@ +# Concept: Mastra Data Storage + +**Purpose**: Persistence layer for cases, documents, assessments, and observability. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Mastra uses a dual-storage approach: a local SQLite database (via Drizzle ORM) for business entities and a built-in `LibSQLStore` for Mastra-specific execution data (traces, spans). + +## Key Points +- **Business Entities**: Managed in `src/db/schema.ts`. Includes `cases`, `documents`, `assessments`, and `outputs`. +- **Mastra Store**: `LibSQLStore` handles `mastra_traces`, `mastra_ai_spans`, and `mastra_scorers`. +- **V3 Extensions**: Specific tables for `timeline_events`, `evidence_gaps`, `sub_claims`, and `vulnerability_flags`. +- **Observability**: `prompt_execution_traces` provides detailed cost and token tracking per AI call. +- **File Storage**: Large blobs (PDFs, JSON outputs) are stored in `./tmp/` with paths referenced in the DB. + +## Quick Example +```typescript +// Business Schema (Drizzle) +export const cases = sqliteTable('cases', { + id: text('id').primaryKey(), + status: text('status').default('new'), +}); + +// Mastra Store Config +storage: new LibSQLStore({ + url: process.env.MASTRA_DB_PATH || 'file:./mastra.db', +}), +``` + +**Reference**: `src/db/schema.ts`, `src/mastra/index.ts` +**Related**: +- concepts/core.md +- lookup/mastra-config.md diff --git a/.opencode/context/development/ai/mastra-ai/concepts/workflows.md b/.opencode/context/development/ai/mastra-ai/concepts/workflows.md new file mode 100644 index 0000000..47d18bd --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/concepts/workflows.md @@ -0,0 +1,33 @@ +# Concept: Mastra Workflows + +**Purpose**: Linear and parallel execution chains for complex AI tasks. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Workflows in Mastra are directed graphs of steps that process data sequentially or in parallel. They provide a structured way to handle multi-stage LLM operations with built-in state management and human-in-the-loop (HITL) support. + +## Key Points +- **Step Definition**: Created with `createStep`, requiring `inputSchema`, `outputSchema`, and an `execute` function. +- **Chaining**: Steps are linked using `.then()` for sequential and `.parallel()` for concurrent execution. +- **HITL Support**: Steps can `suspend` execution to wait for human input and `resume` when data is provided. +- **State Access**: Each step has access to the global workflow `state` and the `inputData` from the previous step. + +## Quick Example +```typescript +const workflow = createWorkflow({ id: 'my-workflow', inputSchema, outputSchema }) + .then(step1) + .parallel([step2a, step2b]) + .then(mergeStep) + .commit(); + +const { runId, start } = workflow.createRun(); +const result = await start({ inputData: { ... } }); +``` + +**Reference**: `src/mastra/workflows/` +**Related**: +- concepts/core.md +- examples/workflow-example.md diff --git a/.opencode/context/development/ai/mastra-ai/errors/mastra-errors.md b/.opencode/context/development/ai/mastra-ai/errors/mastra-errors.md new file mode 100644 index 0000000..b3ef768 --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/errors/mastra-errors.md @@ -0,0 +1,31 @@ +# Errors: Mastra Implementation + +**Purpose**: Common errors, their causes, and recovery strategies. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Errors in Mastra typically fall into three categories: AI generation failures, structured output validation errors, and context/resource missing errors. + +## Key Points +- **AIGenerationError**: Occurs when the LLM fails to generate a response (e.g., safety filters, model downtime). +- **StructuredOutputError**: Triggered when the LLM response doesn't match the Zod schema defined in the tool or step. +- **RateLimitError**: Hit when exceeding provider limits. Includes a `retryAfter` value. +- **MastraContextError**: Raised when a required resource (like `services` or `mastra` instance) is missing from the execution context. +- **Retry Strategy**: Use `isRetryableError(error)` to determine if a transient failure can be recovered with exponential backoff. + +## Common Errors Table + +| Error | Cause | Fix | +|-------|-------|-----| +| `StructuredOutputError` | LLM hallucinated wrong JSON | Refine prompt or use simpler schema | +| `RateLimitError` | Too many concurrent requests | Implement rate limiting or increase quota | +| `NotFoundError` | Case or Document ID missing in DB | Check DB state before workflow start | +| `MastraContextError` | `services` not passed to tool | Ensure `services` is in `ToolExecutionContext` | + +**Reference**: `src/lib/errors.ts` +**Related**: +- concepts/core.md +- guides/testing.md diff --git a/.opencode/context/development/ai/mastra-ai/examples/workflow-example.md b/.opencode/context/development/ai/mastra-ai/examples/workflow-example.md new file mode 100644 index 0000000..e7070d0 --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/examples/workflow-example.md @@ -0,0 +1,40 @@ +# Example: Document Ingestion Workflow + +**Purpose**: Demonstrates a multi-step workflow with parallel processing. + +**Last Updated**: 2026-01-09 + +--- + +## Workflow Definition +```typescript +export const documentIngestionWorkflow = createWorkflow({ + id: 'document-ingestion', + inputSchema: z.object({ filename: z.string(), fileBuffer: z.any() }), + outputSchema: z.object({ documentId: z.string(), success: z.boolean() }), +}) + .then(uploadStep) // Step 1: Upload + .then(extractionStep) // Step 2: Extract Text + .parallel([ // Step 3: Process in parallel + classificationStep, + summarizationStep + ]) + .then(mergeResultsStep) // Step 4: Merge + .commit(); +``` + +## Step Execution +```typescript +const uploadStep = createStep({ + id: 'upload-document', + execute: async ({ inputData, mastra }) => { + const result = await documentUploadTool.execute(inputData, { mastra }); + return result; + }, +}); +``` + +**Reference**: `src/mastra/workflows/document-ingestion-with-classification-workflow.ts` +**Related**: +- concepts/workflows.md +- concepts/agents-tools.md diff --git a/.opencode/context/development/ai/mastra-ai/guides/modular-building.md b/.opencode/context/development/ai/mastra-ai/guides/modular-building.md new file mode 100644 index 0000000..60d1cff --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/guides/modular-building.md @@ -0,0 +1,35 @@ +# Guide: Modular Mastra Building + +**Purpose**: Best practices for structuring a large-scale Mastra implementation. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Modular building ensures that as the project grows, components remain testable, reusable, and easy to navigate. This is achieved by separating logic into specialized directories and using a central registry. + +## Key Points +- **Component Separation**: Keep `agents`, `tools`, `workflows`, and `scorers` in their own top-level directories within `src/mastra/`. +- **Shared Services**: Use a `shared.ts` file to instantiate services (DB, repositories) to prevent circular dependencies between workflows and the main Mastra instance. +- **Central Registry**: Register all components in `src/mastra/index.ts`. This is the single source of truth for the Mastra instance. +- **Feature-Based Steps**: Group related workflow steps into sub-directories (e.g., `src/mastra/workflows/v3/steps/`) to keep workflow files clean. + +## Quick Example +```typescript +// src/mastra/shared.ts +export const services = createServices(); + +// src/mastra/index.ts +import { services } from './shared'; +export const mastra = new Mastra({ + workflows: { myWorkflow }, + agents: { myAgent }, + // ... +}); +``` + +**Reference**: `src/mastra/index.ts`, `src/mastra/shared.ts` +**Related**: +- concepts/core.md +- guides/workflow-step-structure.md diff --git a/.opencode/context/development/ai/mastra-ai/guides/testing.md b/.opencode/context/development/ai/mastra-ai/guides/testing.md new file mode 100644 index 0000000..75141ee --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/guides/testing.md @@ -0,0 +1,33 @@ +# Guide: Testing Mastra + +**Purpose**: How to run and validate Mastra components in this project. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Testing in this project is divided into tool-level tests and full workflow integration tests. Use the provided npm scripts for rapid validation. + +## Key Points +- **Tool Tests**: Validate individual tools in isolation (e.g., `npm run test:playbook`). +- **Workflow Tests**: Run full end-to-end scenarios (e.g., `npm run test:workflow`). +- **Baseline Tests**: Compare current performance against a known baseline (`npm run test:baseline`). +- **Observability**: Use `npm run traces` after tests to inspect the execution details in the database. + +## Quick Example +```bash +# Test a specific tool +npm run test:calculator + +# Run full validity workflow +npm run validity:workflow + +# View results of the last run +npm run traces +``` + +**Reference**: `package.json` scripts, `scripts/` directory +**Related**: +- concepts/core.md +- lookup/mastra-config.md diff --git a/.opencode/context/development/ai/mastra-ai/guides/workflow-step-structure.md b/.opencode/context/development/ai/mastra-ai/guides/workflow-step-structure.md new file mode 100644 index 0000000..97fb59f --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/guides/workflow-step-structure.md @@ -0,0 +1,38 @@ +# Guide: Workflow Step Structure + +**Purpose**: Standardized pattern for defining maintainable and testable workflow steps. + +**Last Updated**: 2026-01-09 + +--- + +## Core Idea +Workflow steps should be self-contained units that encapsulate their input/output schemas and execution logic. For complex workflows, steps should be moved to a dedicated `steps/` directory and grouped by phase. + +## Key Points +- **Directory Structure**: Group steps by phase (e.g., `steps/phase1-load.ts`, `steps/phase2-process.ts`). +- **Schema Centralization**: Define shared schemas (like `workflowStateSchema`) in a `schemas.ts` file within the steps directory. +- **Explicit State**: Use `stateSchema` in `createStep` to ensure type safety when accessing the global workflow state. +- **Tool Delegation**: Steps should primarily act as orchestrators, delegating heavy lifting to Tools. +- **Logging**: Include clear console logs at the start and end of each step for easier debugging. + +## Quick Example +```typescript +// src/mastra/workflows/v3/steps/phase1.ts +export const myStep = createStep({ + id: 'my-step-id', + inputSchema: z.object({ ... }), + outputSchema: z.object({ ... }), + stateSchema: workflowStateSchema, + execute: async ({ inputData, state, mastra }) => { + console.log('🚀 Starting myStep...'); + const result = await myTool.execute(inputData, { mastra }); + return result; + }, +}); +``` + +**Reference**: `src/mastra/workflows/v3/steps/` +**Related**: +- concepts/workflows.md +- guides/modular-building.md diff --git a/.opencode/context/development/ai/mastra-ai/lookup/mastra-config.md b/.opencode/context/development/ai/mastra-ai/lookup/mastra-config.md new file mode 100644 index 0000000..ab84c6e --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/lookup/mastra-config.md @@ -0,0 +1,39 @@ +# Lookup: Mastra Configuration + +**Purpose**: Quick reference for Mastra file locations and registration. + +**Last Updated**: 2026-01-09 + +--- + +## File Locations + +| Component | Directory | Registration File | +|-----------|-----------|-------------------| +| **Mastra Instance** | `src/mastra/` | `src/mastra/index.ts` | +| **Agents** | `src/mastra/agents/` | `src/mastra/index.ts` | +| **Tools** | `src/mastra/tools/` | `src/mastra/index.ts` | +| **Workflows** | `src/mastra/workflows/` | `src/mastra/index.ts` | +| **Scorers** | `src/mastra/scorers/` | `src/mastra/index.ts` | +| **Services** | `src/services/` | `src/mastra/shared.ts` | + +## Database Tables + +| Table Name | Description | +|------------|-------------| +| `mastra_traces` | Workflow execution traces | +| `mastra_ai_spans` | LLM call spans and token usage | +| `mastra_scorers` | Evaluation results and scores | +| `mastra_workflow_state` | Current state of running workflows | + +## Common Commands + +| Command | Description | +|---------|-------------| +| `npm run dev` | Start Mastra in development mode | +| `npm run traces` | View recent execution traces | +| `npm run test:workflow` | Run the test workflow script | + +**Related**: +- concepts/core.md +- concepts/workflows.md diff --git a/.opencode/context/development/ai/mastra-ai/navigation.md b/.opencode/context/development/ai/mastra-ai/navigation.md new file mode 100644 index 0000000..e337dfc --- /dev/null +++ b/.opencode/context/development/ai/mastra-ai/navigation.md @@ -0,0 +1,33 @@ +# MAStra AI Navigation + +**Purpose**: AI framework for building agents, workflows, and LLM integrations. + +--- + +## Structure + +``` +mastra-ai/ +├── navigation.md +├── concepts/ +├── guides/ +├── examples/ +└── lookup/ +``` + +--- + +## Quick Routes + +| Task | Path | +|------|------| +| **Agent Definition** | `concepts/agents.md` [future] | +| **Workflow Setup** | `guides/workflows.md` [future] | + +--- + +## By Function + +**Concepts** → Agents, Tools, Workflows, RAG +**Guides** → Installation, Integration, Deployment +**Examples** → Basic Agent, Tool Calling diff --git a/.opencode/context/development/ai/navigation.md b/.opencode/context/development/ai/navigation.md new file mode 100644 index 0000000..104e9ec --- /dev/null +++ b/.opencode/context/development/ai/navigation.md @@ -0,0 +1,29 @@ +# AI Navigation + +**Purpose**: AI frameworks, agent runtimes, and LLM integration patterns. + +--- + +## Structure + +``` +ai/ +├── navigation.md +└── mastra-ai/ + ├── navigation.md + └── [patterns].md +``` + +--- + +## Quick Routes + +| Task | Path | +|------|------| +| **MAStra AI** | `mastra-ai/navigation.md` | + +--- + +## By Technology + +**MAStra AI** → AI framework for building agents and workflows. diff --git a/.opencode/context/development/frameworks/navigation.md b/.opencode/context/development/frameworks/navigation.md new file mode 100644 index 0000000..5d347ae --- /dev/null +++ b/.opencode/context/development/frameworks/navigation.md @@ -0,0 +1,29 @@ +# Frameworks Navigation + +**Purpose**: Full-stack and meta-frameworks that span multiple architectural layers. + +--- + +## Structure + +``` +frameworks/ +├── navigation.md +└── tanstack-start/ + ├── navigation.md + └── [patterns].md +``` + +--- + +## Quick Routes + +| Task | Path | +|------|------| +| **Tanstack Start** | `tanstack-start/navigation.md` | + +--- + +## By Framework + +**Tanstack Start** → Full-stack React framework with SSR and server functions. diff --git a/.opencode/context/development/frameworks/tanstack-start/navigation.md b/.opencode/context/development/frameworks/tanstack-start/navigation.md new file mode 100644 index 0000000..9a855b2 --- /dev/null +++ b/.opencode/context/development/frameworks/tanstack-start/navigation.md @@ -0,0 +1,33 @@ +# Tanstack Start Navigation + +**Purpose**: Full-stack React framework patterns and workflows. + +--- + +## Structure + +``` +tanstack-start/ +├── navigation.md +├── concepts/ +├── guides/ +├── examples/ +└── lookup/ +``` + +--- + +## Quick Routes + +| Task | Path | +|------|------| +| **Project Setup** | `guides/setup.md` [future] | +| **SSR Patterns** | `concepts/ssr.md` [future] | + +--- + +## By Function + +**Concepts** → SSR, Routing, Server Functions +**Guides** → Setup, Deployment, Data Fetching +**Examples** → Basic App, API Routes diff --git a/.opencode/context/development/navigation.md b/.opencode/context/development/navigation.md index e9760ad..aad1a57 100644 --- a/.opencode/context/development/navigation.md +++ b/.opencode/context/development/navigation.md @@ -18,6 +18,14 @@ development/ │ ├── clean-code.md │ └── api-design.md │ +├── frameworks/ # Full-stack frameworks +│ ├── navigation.md +│ └── tanstack-start/ +│ +├── ai/ # AI & Agents +│ ├── navigation.md +│ └── mastra-ai/ +│ ├── frontend/ # Client-side (future) │ ├── navigation.md │ ├── react/ @@ -66,6 +74,8 @@ development/ ## By Concern **Principles** → Universal development practices +**Frameworks** → Full-stack frameworks (Tanstack Start, Next.js) +**AI** → AI frameworks and agent runtimes (MAStra AI) **Frontend** → React, Vue, state management (future) **Backend** → APIs, Node.js, Python, auth (future) **Data** → SQL, NoSQL, ORMs (future) diff --git a/.opencode/plugin/agent-validator.ts.disabled b/.opencode/plugin/agent-validator.ts.disabled new file mode 100644 index 0000000..dd48c1b --- /dev/null +++ b/.opencode/plugin/agent-validator.ts.disabled @@ -0,0 +1,1086 @@ +import type { Plugin } from "@opencode-ai/plugin" +import { tool } from "@opencode-ai/plugin" +import { writeFile } from "fs/promises" +import path from "path" + +/** + * Helper function to check if a message contains approval request language + */ +function checkForApprovalLanguage(msg: any): boolean { + if (!msg.parts) return false + + const approvalKeywords = [ + "approval", + "approve", + "proceed", + "confirm", + "permission", + "before proceeding", + "should i", + "may i", + "can i proceed", + ] + + for (const part of msg.parts) { + if (part.type === "text" && part.text) { + const text = part.text.toLowerCase() + if (approvalKeywords.some(keyword => text.includes(keyword))) { + return true + } + } + } + + return false +} + +/** + * Helper function to check if a user message contains approval response + */ +function checkForUserApproval(msg: any): boolean { + if (!msg.parts) return false + + const userApprovalKeywords = [ + "proceed", + "approved", + "yes", + "go ahead", + "ok", + "okay", + "sure", + "do it", + "continue", + ] + + for (const part of msg.parts) { + if (part.type === "text" && part.text) { + const text = part.text.toLowerCase().trim() + // Check for exact matches or phrases containing approval keywords + if (userApprovalKeywords.some(keyword => text === keyword || text.includes(keyword))) { + return true + } + } + } + + return false +} + +/** + * Agent Validation Plugin + * + * Validates that agents follow their defined prompts and execution rules. + * Tracks tool calls, approval gates, delegation decisions, and critical rule compliance. + */ +export const AgentValidatorPlugin: Plugin = async ({ client, project, directory }) => { + // Track agent behavior in real-time + const behaviorLog: Array<{ + timestamp: number + sessionID: string + agent: string + event: string + data: any + }> = [] + + // Track tool execution for approval gate validation + const toolExecutionTracker = new Map() + + // Track current agent for each session + const sessionAgentTracker = new Map() + + return { + // Listen to all events + async event(input) { + const { event } = input + // Silently track events (removed console.log to reduce noise) + + // Track session-level events for validation + if (event.type === "message.updated") { + const msg = event.properties.info + behaviorLog.push({ + timestamp: Date.now(), + sessionID: msg.sessionID, + agent: msg.role === "user" ? msg.agent : "assistant", + event: "message_created", + data: { + messageID: msg.id, + role: msg.role, + }, + }) + } + }, + + // Capture agent information from chat messages + "chat.message": async (input, output) => { + const { sessionID, agent } = input + + // Track which agent is currently active for this session + if (agent) { + sessionAgentTracker.set(sessionID, agent) + } + }, + + // Monitor tool execution + "tool.execute.before": async (input, output) => { + const { tool, sessionID, callID } = input + const key = `${sessionID}-${callID}` + + // Silently track tools (removed console.log to reduce noise) + + // Get current agent for this session + const currentAgent = sessionAgentTracker.get(sessionID) || "unknown" + + // Track context file reads + if (tool === "read") { + const filePath = output.args?.filePath || output.args?.target_file + if (filePath && filePath.includes(".opencode/")) { + // Context file read detected - track silently + behaviorLog.push({ + timestamp: Date.now(), + sessionID, + agent: currentAgent, + event: "context_file_read", + data: { + tool: "read", + filePath, + callID, + }, + }) + } + } + + // Track execution tools that require approval + const executionTools = ["bash", "write", "edit", "task"] + + if (executionTools.includes(tool)) { + // Track execution tool silently + const tracker = toolExecutionTracker.get(sessionID) || { + approvalRequested: false, + toolsExecuted: [], + timestamp: Date.now(), + } + + // Check recent messages for approval flow + try { + const messagesResponse = await client.session.messages({ + path: { id: sessionID }, + }) + const messages = messagesResponse.data || [] + + // Look at last few messages for approval pattern + const recentMessages = messages.slice(-5) + for (let i = 0; i < recentMessages.length - 1; i++) { + const msg = recentMessages[i] + const nextMsg = recentMessages[i + 1] + const role = msg.info?.role + const nextRole = nextMsg.info?.role + + if (role === "assistant" && checkForApprovalLanguage(msg) && + nextRole === "user" && checkForUserApproval(nextMsg)) { + tracker.approvalRequested = true + // Approval flow detected - tracked silently + break + } + } + } catch (err) { + // Error checking messages - continue silently + } + + tracker.toolsExecuted.push(tool) + toolExecutionTracker.set(sessionID, tracker) + + behaviorLog.push({ + timestamp: Date.now(), + sessionID, + agent: currentAgent, + event: "execution_tool_called", + data: { + tool, + callID, + args: output.args, + approvalRequested: tracker.approvalRequested, + }, + }) + } + }, + + // Track tool execution results + "tool.execute.after": async (input, output) => { + const { tool, sessionID } = input + + // Track tool completion silently + const currentAgent = sessionAgentTracker.get(sessionID) || "unknown" + + behaviorLog.push({ + timestamp: Date.now(), + sessionID, + agent: currentAgent, + event: "tool_executed", + data: { + tool, + title: output.title, + metadata: output.metadata, + }, + }) + }, + + // Provide validation tools + tool: { + // Validate current session + validate_session: tool({ + description: "Validate that the current agent session is following its defined prompt rules and execution patterns. Returns a detailed validation report.", + args: { + include_details: tool.schema.boolean() + .optional() + .describe("Include detailed evidence for each validation check"), + }, + async execute(args, context) { + const { sessionID } = context + + try { + // Fetch session messages using SDK + const messagesResponse = await client.session.messages({ + path: { id: sessionID }, + }) + + if (messagesResponse.error) { + return `Error fetching session: ${messagesResponse.error}` + } + + const messages = messagesResponse.data || [] + + // Analyze agent behavior + const validation = await validateSessionBehavior({ + sessionID, + messages, + behaviorLog: behaviorLog.filter(log => log.sessionID === sessionID), + includeDetails: args.include_details ?? false, + }) + + return formatValidationReport(validation) + } catch (err) { + return `Validation error: ${err instanceof Error ? err.message : String(err)}` + } + }, + }), + + // Check approval gate compliance + check_approval_gates: tool({ + description: "Check if approval gates were properly enforced before execution operations (bash, write, edit, task). Returns compliance status.", + args: {}, + async execute(args, context) { + const { sessionID } = context + const tracker = toolExecutionTracker.get(sessionID) + + if (!tracker) { + return "No execution operations tracked in this session." + } + + const { approvalRequested, toolsExecuted } = tracker + const violations = approvalRequested ? [] : toolsExecuted + + if (violations.length === 0) { + return `✅ Approval gate compliance: PASSED\n\nAll ${toolsExecuted.length} execution operation(s) were properly approved.` + } + + return `⚠️ Approval gate compliance: FAILED\n\nExecuted ${violations.length} operation(s) without approval:\n${violations.map(t => ` - ${t}`).join("\n")}\n\nCritical rule violated: approval_gate` + }, + }), + + // Export validation report + export_validation_report: tool({ + description: "Export a comprehensive validation report for the current session to a markdown file", + args: { + output_path: tool.schema.string() + .optional() + .describe("Path to save the report (defaults to .tmp/validation-{sessionID}.md)"), + }, + async execute(args, context) { + const { sessionID } = context + + try { + const messagesResponse = await client.session.messages({ + path: { id: sessionID }, + }) + + if (messagesResponse.error) { + return `Error fetching session: ${messagesResponse.error}` + } + + const messages = messagesResponse.data || [] + + const validation = await validateSessionBehavior({ + sessionID, + messages, + behaviorLog: behaviorLog.filter(log => log.sessionID === sessionID), + includeDetails: true, + }) + + const report = generateDetailedReport(validation, messages) + const outputPath = args.output_path || path.join(directory, `.tmp/validation-${sessionID.slice(0, 8)}.md`) + + await writeFile(outputPath, report, "utf-8") + + return `✅ Validation report exported to: ${outputPath}\n\n${formatValidationReport(validation)}` + } catch (err) { + return `Export error: ${err instanceof Error ? err.message : String(err)}` + } + }, + }), + + // Analyze delegation decisions + analyze_delegation: tool({ + description: "Analyze whether delegation decisions followed the 4+ file rule and complexity criteria", + args: {}, + async execute(args, context) { + const { sessionID } = context + + const messagesResponse = await client.session.messages({ + path: { id: sessionID }, + }) + + if (messagesResponse.error) { + return `Error: ${messagesResponse.error}` + } + + const messages = messagesResponse.data || [] + const analysis = analyzeDelegationDecisions(messages) + + return formatDelegationAnalysis(analysis) + }, + }), + + // Analyze context file reads + analyze_context_reads: tool({ + description: "Show all context files that were read during the session (e.g., .opencode/agent/openagent.md)", + args: {}, + async execute(args, context) { + const { sessionID } = context + + // Filter behavior log for context file reads + const contextReads = behaviorLog.filter( + log => log.sessionID === sessionID && log.event === "context_file_read" + ) + + if (contextReads.length === 0) { + return "📚 No context files read in this session yet.\n\nContext files are in `.opencode/` directories (agent definitions, workflows, standards, etc.)" + } + + const lines: string[] = [ + `## Context Files Read`, + ``, + `**Total reads:** ${contextReads.length}`, + ``, + ] + + // Group by file path + const fileReadCounts = new Map() + contextReads.forEach(log => { + const filePath = log.data.filePath + fileReadCounts.set(filePath, (fileReadCounts.get(filePath) || 0) + 1) + }) + + // Sort by read count (most read first) + const sorted = Array.from(fileReadCounts.entries()).sort((a, b) => b[1] - a[1]) + + lines.push(`### Files Read:`) + sorted.forEach(([filePath, count]) => { + const fileName = filePath.split('/').pop() + const readText = count === 1 ? "read" : "reads" + lines.push(`- **${fileName}** (${count} ${readText})`) + lines.push(` \`${filePath}\``) + }) + + lines.push(``) + lines.push(`### Timeline:`) + contextReads.forEach((log, idx) => { + const time = new Date(log.timestamp).toLocaleTimeString() + const fileName = log.data.filePath.split('/').pop() + lines.push(`${idx + 1}. [${time}] ${fileName}`) + }) + + return lines.join("\n") + }, + }), + + // Check context loading compliance + check_context_compliance: tool({ + description: "Check if required context files were read BEFORE executing tasks (e.g., read docs.md before writing documentation)", + args: {}, + async execute(args, context) { + const { sessionID } = context + + const messagesResponse = await client.session.messages({ + path: { id: sessionID }, + }) + + if (messagesResponse.error) { + return `Error: ${messagesResponse.error}` + } + + const messages = messagesResponse.data || [] + const sessionBehaviorLog = behaviorLog.filter(log => log.sessionID === sessionID) + + const checks = analyzeContextLoadingCompliance(messages, sessionBehaviorLog) + + if (checks.length === 0) { + return "📋 No tasks detected that require specific context files.\n\nContext loading rules apply when:\n- Writing documentation → should read standards/docs.md\n- Writing code → should read standards/code.md\n- Reviewing code → should read workflows/review.md\n- Delegating tasks → should read workflows/delegation.md\n- Writing tests → should read standards/tests.md" + } + + const passed = checks.filter(c => c.passed).length + const failed = checks.filter(c => !c.passed).length + const score = Math.round((passed / checks.length) * 100) + + const lines: string[] = [ + `## Context Loading Compliance`, + ``, + `**Score:** ${score}%`, + `- ✅ Compliant: ${passed}`, + `- ⚠️ Non-compliant: ${failed}`, + ``, + ] + + if (failed > 0) { + lines.push(`### ⚠️ Issues Found:`) + checks.filter(c => !c.passed).forEach(check => { + lines.push(`- ${check.details}`) + }) + lines.push(``) + } + + if (passed > 0) { + lines.push(`### ✅ Compliant Actions:`) + checks.filter(c => c.passed).forEach(check => { + lines.push(`- ${check.details}`) + }) + lines.push(``) + } + + lines.push(`### Context Loading Rules:`) + lines.push(`According to OpenAgent prompt, the agent should:`) + lines.push(`1. Detect task type from user request`) + lines.push(`2. Read required context file FIRST`) + lines.push(`3. Then execute task following those standards`) + lines.push(``) + lines.push(`**Pattern:** "Fetch context BEFORE starting work, not during or after"`) + + return lines.join("\n") + }, + }), + + // Analyze which agents were used + analyze_agent_usage: tool({ + description: "Show which agents were active during the session and what tools they used", + args: {}, + async execute(args, context) { + const { sessionID } = context + + const sessionBehaviorLog = behaviorLog.filter(log => log.sessionID === sessionID) + + if (sessionBehaviorLog.length === 0) { + return "📊 No agent activity tracked yet in this session." + } + + // Group by agent + const agentStats = new Map + events: string[] + firstSeen: number + lastSeen: number + }>() + + sessionBehaviorLog.forEach(log => { + const agent = log.agent || "unknown" + + if (!agentStats.has(agent)) { + agentStats.set(agent, { + toolCalls: new Map(), + events: [], + firstSeen: log.timestamp, + lastSeen: log.timestamp + }) + } + + const stats = agentStats.get(agent)! + stats.lastSeen = log.timestamp + stats.events.push(log.event) + + // Track tool usage + if (log.event === "execution_tool_called" || log.event === "tool_executed") { + const tool = log.data.tool + stats.toolCalls.set(tool, (stats.toolCalls.get(tool) || 0) + 1) + } + }) + + const lines: string[] = [ + `## Agent Usage Report`, + ``, + `**Agents detected:** ${agentStats.size}`, + `**Total events:** ${sessionBehaviorLog.length}`, + ``, + ] + + // Sort agents by first seen + const sortedAgents = Array.from(agentStats.entries()).sort((a, b) => a[1].firstSeen - b[1].firstSeen) + + sortedAgents.forEach(([agent, stats]) => { + const duration = stats.lastSeen - stats.firstSeen + const durationStr = duration > 0 ? `${Math.round(duration / 1000)}s` : "instant" + + lines.push(`### ${agent === "unknown" ? "Unknown Agent" : agent}`) + lines.push(``) + lines.push(`**Active duration:** ${durationStr}`) + lines.push(`**Events:** ${stats.events.length}`) + + if (stats.toolCalls.size > 0) { + lines.push(``) + lines.push(`**Tools used:**`) + const sortedTools = Array.from(stats.toolCalls.entries()).sort((a, b) => b[1] - a[1]) + sortedTools.forEach(([tool, count]) => { + lines.push(`- ${tool}: ${count}x`) + }) + } + + lines.push(``) + }) + + return lines.join("\n") + }, + }), + + // Debug tool to inspect tracking + debug_validator: tool({ + description: "Debug tool to inspect what the validator is tracking (behavior log, messages, etc.)", + args: {}, + async execute(args, context) { + const { sessionID } = context + + // Debug tool - gather information silently + + // Get messages from SDK + const messagesResponse = await client.session.messages({ + path: { id: sessionID }, + }) + + const messages = messagesResponse.data || [] + const sessionBehaviorLog = behaviorLog.filter(log => log.sessionID === sessionID) + const tracker = toolExecutionTracker.get(sessionID) + + const debug = { + sessionID, + behaviorLogEntries: sessionBehaviorLog.length, + behaviorLogSampleFirst: sessionBehaviorLog.slice(0, 3), + behaviorLogSampleLast: sessionBehaviorLog.slice(-3), + messagesCount: messages.length, + messagesSample: messages.slice(0, 2).map(m => ({ + role: m.info?.role, + partsCount: m.parts?.length, + partTypes: m.parts?.map((p: any) => p.type), + })), + toolTracker: tracker ? { + approvalRequested: tracker.approvalRequested, + toolsExecuted: tracker.toolsExecuted, + } : null, + allBehaviorLogs: behaviorLog.length, + } + + return `## Debug Information\n\n\`\`\`json\n${JSON.stringify(debug, null, 2)}\n\`\`\`\n\n**Analysis:**\n- Behavior log entries for this session: ${sessionBehaviorLog.length}\n- Total behavior log entries: ${behaviorLog.length}\n- Messages in session: ${messages.length}\n- Tool execution tracker: ${tracker ? 'Active' : 'None'}` + }, + }), + }, + } +} + +// Validation logic +interface ValidationCheck { + rule: string + passed: boolean + severity: "info" | "warning" | "error" + details: string + evidence?: any +} + +interface ValidationResult { + sessionID: string + checks: ValidationCheck[] + summary: { + passed: number + failed: number + warnings: number + score: number + } +} + +async function validateSessionBehavior(input: { + sessionID: string + messages: any[] + behaviorLog: any[] + includeDetails: boolean +}): Promise { + const checks: ValidationCheck[] = [] + + // Check 1: Tool usage patterns + const toolUsage = analyzeToolUsage(input.messages) + checks.push(...toolUsage) + + // Check 2: Approval gate enforcement + const approvalChecks = analyzeApprovalGates(input.messages, input.behaviorLog) + checks.push(...approvalChecks) + + // Check 3: Lazy context loading + const contextChecks = analyzeContextLoading(input.messages) + checks.push(...contextChecks) + + // Check 4: Delegation appropriateness + const delegationChecks = analyzeDelegation(input.messages) + checks.push(...delegationChecks) + + // Check 5: Critical rule compliance + const criticalChecks = analyzeCriticalRules(input.messages) + checks.push(...criticalChecks) + + // Check 6: Context loading compliance (read required files BEFORE execution) + const contextComplianceChecks = analyzeContextLoadingCompliance(input.messages, input.behaviorLog) + checks.push(...contextComplianceChecks) + + // Calculate summary + const passed = checks.filter(c => c.passed).length + const failed = checks.filter(c => !c.passed && c.severity === "error").length + const warnings = checks.filter(c => !c.passed && c.severity === "warning").length + const score = checks.length > 0 ? Math.round((passed / checks.length) * 100) : 0 + + return { + sessionID: input.sessionID, + checks, + summary: { passed, failed, warnings, score }, + } +} + +function analyzeToolUsage(messages: any[]): ValidationCheck[] { + const checks: ValidationCheck[] = [] + + for (const msg of messages) { + // Messages have structure: { info: Message, parts: Part[] } + const role = msg.info?.role || msg.role + if (role !== "assistant") continue + + const tools = extractToolsFromMessage(msg) + + if (tools.length > 0) { + checks.push({ + rule: "tool_usage", + passed: true, + severity: "info", + details: `Used ${tools.length} tool(s): ${tools.join(", ")}`, + }) + } + } + + return checks +} + +function analyzeApprovalGates(messages: any[], behaviorLog: any[]): ValidationCheck[] { + const checks: ValidationCheck[] = [] + const executionTools = ["bash", "write", "edit", "task"] + + for (let i = 0; i < messages.length; i++) { + const msg = messages[i] + const role = msg.info?.role || msg.role + if (role !== "assistant") continue + + const tools = extractToolsFromMessage(msg) + const executionOps = tools.filter(t => executionTools.includes(t)) + + if (executionOps.length > 0) { + // Check if approval language is present in this message OR in recent previous messages + let hasApprovalRequest = checkForApprovalLanguage(msg) + + // Look back up to 3 messages to find approval request + if (!hasApprovalRequest) { + for (let j = Math.max(0, i - 3); j < i; j++) { + const prevMsg = messages[j] + const prevRole = prevMsg.info?.role || prevMsg.role + if (prevRole === "assistant" && checkForApprovalLanguage(prevMsg)) { + // Check if there's a user approval response after the request + if (j + 1 < messages.length) { + const userResponse = messages[j + 1] + const userRole = userResponse.info?.role || userResponse.role + if (userRole === "user" && checkForUserApproval(userResponse)) { + hasApprovalRequest = true + break + } + } + } + } + } + + checks.push({ + rule: "approval_gate_enforcement", + passed: hasApprovalRequest, + severity: hasApprovalRequest ? "info" : "warning", + details: hasApprovalRequest + ? `Properly requested approval before ${executionOps.length} execution op(s)` + : `⚠️ Executed ${executionOps.length} operation(s) without explicit approval request`, + evidence: { executionOps, hasApprovalRequest }, + }) + } + } + + return checks +} + +function analyzeContextLoading(messages: any[]): ValidationCheck[] { + const checks: ValidationCheck[] = [] + + for (const msg of messages) { + const role = msg.info?.role || msg.role + if (role !== "assistant") continue + + // Look for read operations on .opencode/context/ files + const contextReads = extractContextReads(msg) + + if (contextReads.length > 0) { + checks.push({ + rule: "lazy_context_loading", + passed: true, + severity: "info", + details: `Lazy-loaded ${contextReads.length} context file(s): ${contextReads.join(", ")}`, + }) + } + } + + return checks +} + +function analyzeDelegation(messages: any[]): ValidationCheck[] { + const checks: ValidationCheck[] = [] + + for (const msg of messages) { + const role = msg.info?.role || msg.role + if (role !== "assistant") continue + + const tools = extractToolsFromMessage(msg) + const hasDelegation = tools.includes("task") + const writeEditCount = tools.filter(t => t === "write" || t === "edit").length + + if (hasDelegation) { + const shouldDelegate = writeEditCount >= 4 + + checks.push({ + rule: "delegation_appropriateness", + passed: shouldDelegate, + severity: shouldDelegate ? "info" : "warning", + details: shouldDelegate + ? `Appropriately delegated (${writeEditCount} files)` + : `Delegated but only ${writeEditCount} files (< 4 threshold)`, + }) + } else if (writeEditCount >= 4) { + checks.push({ + rule: "delegation_appropriateness", + passed: false, + severity: "warning", + details: `Should have delegated (${writeEditCount} files >= 4 threshold)`, + }) + } + } + + return checks +} + +function analyzeCriticalRules(messages: any[]): ValidationCheck[] { + const checks: ValidationCheck[] = [] + + // Look for auto-fix attempts after errors + for (let i = 0; i < messages.length - 1; i++) { + const msg = messages[i] + const nextMsg = messages[i + 1] + + const role = msg.info?.role || msg.role + const metadata = msg.info?.metadata || msg.metadata + + if (role === "assistant" && metadata?.error) { + const nextTools = extractToolsFromMessage(nextMsg) + const hasAutoFix = nextTools.some(t => ["write", "edit", "bash"].includes(t)) + + if (hasAutoFix) { + checks.push({ + rule: "stop_on_failure", + passed: false, + severity: "error", + details: "⛔ Auto-fix attempted after error - violates stop_on_failure rule", + evidence: { error: metadata.error, autoFixTools: nextTools }, + }) + } + } + } + + return checks +} + +function analyzeContextLoadingCompliance(messages: any[], behaviorLog: any[]): ValidationCheck[] { + const checks: ValidationCheck[] = [] + + // Define required context files for different task types + const contextRules = [ + { + taskKeywords: ["write doc", "create doc", "documentation", "write readme", "document"], + requiredFile: "standards/docs.md", + taskType: "documentation" + }, + { + taskKeywords: ["write code", "create function", "implement", "add feature", "build"], + requiredFile: "standards/code.md", + taskType: "code writing" + }, + { + taskKeywords: ["review code", "check code", "analyze code", "code review"], + requiredFile: "workflows/review.md", + taskType: "code review" + }, + { + taskKeywords: ["delegate", "create task", "subagent"], + requiredFile: "workflows/delegation.md", + taskType: "delegation" + }, + { + taskKeywords: ["write test", "create test", "test coverage", "unit test"], + requiredFile: "standards/tests.md", + taskType: "testing" + } + ] + + // Get all context file reads from behavior log + const contextReads = behaviorLog + .filter(log => log.event === "context_file_read") + .map(log => ({ + timestamp: log.timestamp, + filePath: log.data.filePath + })) + + // Analyze each message for task execution + for (let i = 0; i < messages.length; i++) { + const msg = messages[i] + const role = msg.info?.role || msg.role + + if (role !== "assistant") continue + + const tools = extractToolsFromMessage(msg) + const executionTools = tools.filter(t => ["write", "edit", "bash", "task"].includes(t)) + + if (executionTools.length === 0) continue + + // Get message text to detect task type + const messageText = extractMessageText(msg).toLowerCase() + + // Check if this message matches any context loading rules + for (const rule of contextRules) { + const matchesTask = rule.taskKeywords.some(keyword => messageText.includes(keyword)) + + if (matchesTask) { + // Check if required context file was read BEFORE this message + const msgTimestamp = msg.info?.timestamp || Date.now() + const contextReadBefore = contextReads.some(read => + read.filePath.includes(rule.requiredFile) && read.timestamp < msgTimestamp + ) + + checks.push({ + rule: "context_loading_compliance", + passed: contextReadBefore, + severity: contextReadBefore ? "info" : "warning", + details: contextReadBefore + ? `✅ Loaded ${rule.requiredFile} before ${rule.taskType}` + : `⚠️ Did not load ${rule.requiredFile} before ${rule.taskType} task`, + evidence: { + taskType: rule.taskType, + requiredFile: rule.requiredFile, + contextReadBefore, + executionTools + } + }) + } + } + } + + return checks +} + +function analyzeDelegationDecisions(messages: any[]): { + delegations: number + appropriate: number + inappropriate: number + fileCountStats: number[] +} { + const stats = { + delegations: 0, + appropriate: 0, + inappropriate: 0, + fileCountStats: [] as number[], + } + + for (const msg of messages) { + const role = msg.info?.role || msg.role + if (role !== "assistant") continue + + const tools = extractToolsFromMessage(msg) + const hasDelegation = tools.includes("task") + const writeEditCount = tools.filter(t => t === "write" || t === "edit").length + + if (hasDelegation) { + stats.delegations++ + stats.fileCountStats.push(writeEditCount) + + if (writeEditCount >= 4) { + stats.appropriate++ + } else { + stats.inappropriate++ + } + } + } + + return stats +} + +// Helper functions +function extractToolsFromMessage(msg: any): string[] { + const tools: string[] = [] + + // Messages from SDK have structure: { info: Message, parts: Part[] } + const parts = msg.parts || [] + + for (const part of parts) { + // Check for tool type (from SDK: part.type === "tool") + if (part.type === "tool" && part.tool) { + tools.push(part.tool) + } + // Also check for tool-invocation format (legacy) + if (part.type === "tool-invocation" && part.toolInvocation) { + tools.push(part.toolInvocation.toolName) + } + } + + return tools +} + +function extractMessageText(msg: any): string { + if (!msg.parts) return "" + + let text = "" + for (const part of msg.parts) { + if (part.type === "text" && part.text) { + text += part.text + " " + } + } + + return text.trim() +} + +function extractContextReads(msg: any): string[] { + const contextFiles: string[] = [] + + if (!msg.parts) return contextFiles + + for (const part of msg.parts) { + if (part.type === "tool-invocation" && + part.toolInvocation?.toolName === "read" && + part.toolInvocation?.args?.target_file?.includes(".opencode/context/")) { + contextFiles.push(part.toolInvocation.args.target_file) + } + } + + return contextFiles +} + +// Formatting functions +function formatValidationReport(validation: ValidationResult): string { + const { summary, checks } = validation + + const lines: string[] = [ + `## Validation Report`, + ``, + `**Score:** ${summary.score}%`, + `- ✅ Passed: ${summary.passed}`, + `- ⚠️ Warnings: ${summary.warnings}`, + `- ❌ Failed: ${summary.failed}`, + ``, + ] + + // Group by severity + const errors = checks.filter(c => !c.passed && c.severity === "error") + const warnings = checks.filter(c => !c.passed && c.severity === "warning") + + if (errors.length > 0) { + lines.push(`### ❌ Errors`) + errors.forEach(check => { + lines.push(`- **${check.rule}**: ${check.details}`) + }) + lines.push(``) + } + + if (warnings.length > 0) { + lines.push(`### ⚠️ Warnings`) + warnings.forEach(check => { + lines.push(`- **${check.rule}**: ${check.details}`) + }) + lines.push(``) + } + + return lines.join("\n") +} + +function formatDelegationAnalysis(analysis: any): string { + const lines: string[] = [ + `## Delegation Analysis`, + ``, + `**Total delegations:** ${analysis.delegations}`, + `- ✅ Appropriate: ${analysis.appropriate}`, + `- ⚠️ Questionable: ${analysis.inappropriate}`, + ``, + ] + + if (analysis.fileCountStats.length > 0) { + const avg = analysis.fileCountStats.reduce((a: number, b: number) => a + b, 0) / analysis.fileCountStats.length + lines.push(`**File count per delegation:**`) + lines.push(`- Average: ${avg.toFixed(1)} files`) + lines.push(`- Range: ${Math.min(...analysis.fileCountStats)} - ${Math.max(...analysis.fileCountStats)} files`) + lines.push(`- Threshold: 4+ files`) + } + + return lines.join("\n") +} + +function generateDetailedReport(validation: ValidationResult, messages: any[]): string { + const lines: string[] = [ + `# Agent Validation Report`, + ``, + `**Session:** ${validation.sessionID}`, + `**Generated:** ${new Date().toISOString()}`, + `**Messages analyzed:** ${messages.length}`, + ``, + formatValidationReport(validation), + ``, + `## Detailed Checks`, + ``, + ] + + validation.checks.forEach(check => { + const icon = check.passed ? "✅" : check.severity === "error" ? "❌" : "⚠️" + lines.push(`### ${icon} ${check.rule}`) + lines.push(``) + lines.push(check.details) + lines.push(``) + + if (check.evidence) { + lines.push(`**Evidence:**`) + lines.push(`\`\`\`json`) + lines.push(JSON.stringify(check.evidence, null, 2)) + lines.push(`\`\`\``) + lines.push(``) + } + }) + + return lines.join("\n") +} + +export default AgentValidatorPlugin diff --git a/.opencode/plugin/docs/VALIDATOR_GUIDE.md b/.opencode/plugin/docs/VALIDATOR_GUIDE.md index 4f7eacc..7f4c8ce 100644 --- a/.opencode/plugin/docs/VALIDATOR_GUIDE.md +++ b/.opencode/plugin/docs/VALIDATOR_GUIDE.md @@ -199,9 +199,9 @@ analyze_context_reads ### Files Read: - **code.md** (2 reads) - `.opencode/context/core/standards/code.md` + `.opencode/context/core/standards/code-quality.md` - **delegation.md** (1 read) - `.opencode/context/core/workflows/delegation.md` + `.opencode/context/core/workflows/task-delegation.md` ### Timeline: 1. [10:23:45] code.md @@ -234,8 +234,8 @@ check_context_compliance - ⚠️ Non-compliant: 0 ### ✅ Compliant Actions: -- ✅ Loaded standards/code.md before code writing -- ✅ Loaded workflows/delegation.md before delegation +- ✅ Loaded standards/code-quality.md before code writing +- ✅ Loaded workflows/task-delegation.md before delegation ### Context Loading Rules: According to OpenAgent prompt, the agent should: @@ -247,11 +247,11 @@ According to OpenAgent prompt, the agent should: ``` **Context loading rules:** -- Writing code → should read `standards/code.md` -- Writing docs → should read `standards/docs.md` -- Writing tests → should read `standards/tests.md` -- Code review → should read `workflows/review.md` -- Delegating → should read `workflows/delegation.md` +- Writing code → should read `standards/code-quality.md` +- Writing docs → should read `standards/documentation.md` +- Writing tests → should read `standards/test-coverage.md` +- Code review → should read `workflows/code-review.md` +- Delegating → should read `workflows/task-delegation.md` **When to use:** - To verify lazy loading is working @@ -467,7 +467,7 @@ opencode --agent openagent # 3. Verify compliance > "check_context_compliance" -# Expected: Should show standards/code.md was read BEFORE writing +# Expected: Should show standards/code-quality.md was read BEFORE writing ``` --- diff --git a/.opencode/plugin/telegram-notify.ts b/.opencode/plugin/telegram-notify.ts index aea369d..ac09ac6 100644 --- a/.opencode/plugin/telegram-notify.ts +++ b/.opencode/plugin/telegram-notify.ts @@ -1,5 +1,5 @@ import type { Plugin } from "@opencode-ai/plugin" -import { SimpleTelegramBot } from "./lib/telegram-bot" +import { SimpleTelegramBot } from "../_lib/telegram-bot" // 🔧 CONFIGURATION: Set to true to enable this plugin const ENABLED = false diff --git a/.opencode/scripts/task-cli.ts b/.opencode/scripts/task-cli.ts new file mode 100644 index 0000000..28e9654 --- /dev/null +++ b/.opencode/scripts/task-cli.ts @@ -0,0 +1,489 @@ +#!/usr/bin/env node +/** + * Task Management CLI + * + * Usage: npx ts-node .opencode/scripts/task-cli.ts [args...] + * + * Tasks are stored in: .tmp/tasks/active/{feature}/ and .tmp/tasks/completed/{feature}/ + * + * Commands: + * status [feature] - Show task status summary + * next [feature] - Show next eligible tasks + * parallel [feature] - Show parallelizable tasks ready to run + * deps - Show dependency tree for a task + * blocked [feature] - Show blocked tasks and why + * complete "summary" - Mark task completed + * validate [feature] - Validate JSON files and dependencies + * init - Create .tmp/tasks/ directory structure + */ + +import * as fs from 'node:fs'; +import * as path from 'node:path'; + +// Tasks stored in project root .tmp/tasks/ +const PROJECT_ROOT = process.cwd(); +const TASKS_ROOT = path.join(PROJECT_ROOT, '.tmp', 'tasks'); +const ACTIVE_DIR = path.join(TASKS_ROOT, 'active'); +const COMPLETED_DIR = path.join(TASKS_ROOT, 'completed'); + +interface Task { + id: string; + name: string; + status: 'active' | 'completed' | 'blocked' | 'archived'; + objective: string; + context_files: string[]; + exit_criteria: string[]; + subtask_count: number; + completed_count: number; + created_at: string; + completed_at: string | null; +} + +interface Subtask { + id: string; + seq: string; + title: string; + status: 'pending' | 'in_progress' | 'completed' | 'blocked'; + depends_on: string[]; + parallel: boolean; + context_files: string[]; + acceptance_criteria: string[]; + deliverables: string[]; + agent_id: string | null; + started_at: string | null; + completed_at: string | null; + completion_summary: string | null; +} + +// Helpers +function ensureDir(dir: string): void { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +function getFeatureDirs(): string[] { + if (!fs.existsSync(ACTIVE_DIR)) return []; + return fs.readdirSync(ACTIVE_DIR).filter((f: string) => + fs.statSync(path.join(ACTIVE_DIR, f)).isDirectory() + ); +} + +function loadTask(feature: string): Task | null { + const taskPath = path.join(ACTIVE_DIR, feature, 'task.json'); + if (!fs.existsSync(taskPath)) return null; + return JSON.parse(fs.readFileSync(taskPath, 'utf-8')) as Task; +} + +function loadSubtasks(feature: string): Subtask[] { + const featureDir = path.join(ACTIVE_DIR, feature); + if (!fs.existsSync(featureDir)) return []; + + const files = fs.readdirSync(featureDir) + .filter((f: string) => f.match(/^subtask_\d{2}\.json$/)) + .sort(); + + return files.map((f: string) => + JSON.parse(fs.readFileSync(path.join(featureDir, f), 'utf-8')) as Subtask + ); +} + +function saveSubtask(feature: string, subtask: Subtask): void { + const subtaskPath = path.join(ACTIVE_DIR, feature, `subtask_${subtask.seq}.json`); + fs.writeFileSync(subtaskPath, JSON.stringify(subtask, null, 2)); +} + +function saveTask(feature: string, task: Task): void { + const taskPath = path.join(ACTIVE_DIR, feature, 'task.json'); + fs.writeFileSync(taskPath, JSON.stringify(task, null, 2)); +} + +// Commands +function cmdInit(): void { + ensureDir(ACTIVE_DIR); + ensureDir(COMPLETED_DIR); + console.log(`\n✓ Created task directories:`); + console.log(` ${ACTIVE_DIR}`); + console.log(` ${COMPLETED_DIR}`); +} + +function cmdStatus(feature?: string): void { + const features = feature ? [feature] : getFeatureDirs(); + + if (features.length === 0) { + console.log('No active features found in .tmp/tasks/active/'); + console.log('Run `task-cli.ts init` to create directories.'); + return; + } + + for (const f of features) { + const task = loadTask(f); + const subtasks = loadSubtasks(f); + + if (!task) { + console.log(`\n[${f}] - No task.json found`); + continue; + } + + const counts = { + pending: subtasks.filter((s: Subtask) => s.status === 'pending').length, + in_progress: subtasks.filter((s: Subtask) => s.status === 'in_progress').length, + completed: subtasks.filter((s: Subtask) => s.status === 'completed').length, + blocked: subtasks.filter((s: Subtask) => s.status === 'blocked').length, + }; + + const progress = subtasks.length > 0 + ? Math.round((counts.completed / subtasks.length) * 100) + : 0; + + console.log(`\n[${f}] ${task.name}`); + console.log(` Status: ${task.status} | Progress: ${progress}% (${counts.completed}/${subtasks.length})`); + console.log(` Pending: ${counts.pending} | In Progress: ${counts.in_progress} | Completed: ${counts.completed} | Blocked: ${counts.blocked}`); + } +} + +function cmdNext(feature?: string): void { + const features = feature ? [feature] : getFeatureDirs(); + + console.log('\n=== Ready Tasks (deps satisfied) ===\n'); + + for (const f of features) { + const subtasks = loadSubtasks(f); + const completedSeqs = new Set( + subtasks.filter((s: Subtask) => s.status === 'completed').map((s: Subtask) => s.seq) + ); + + const ready = subtasks.filter((s: Subtask) => { + if (s.status !== 'pending') return false; + return s.depends_on.every((dep: string) => completedSeqs.has(dep)); + }); + + if (ready.length > 0) { + console.log(`[${f}]`); + for (const s of ready) { + const parallel = s.parallel ? '[parallel]' : '[sequential]'; + console.log(` ${s.seq} - ${s.title} ${parallel}`); + } + console.log(); + } + } +} + +function cmdParallel(feature?: string): void { + const features = feature ? [feature] : getFeatureDirs(); + + console.log('\n=== Parallel Tasks Ready Now ===\n'); + + for (const f of features) { + const subtasks = loadSubtasks(f); + const completedSeqs = new Set( + subtasks.filter((s: Subtask) => s.status === 'completed').map((s: Subtask) => s.seq) + ); + + const parallel = subtasks.filter((s: Subtask) => { + if (s.status !== 'pending') return false; + if (!s.parallel) return false; + return s.depends_on.every((dep: string) => completedSeqs.has(dep)); + }); + + if (parallel.length > 0) { + console.log(`[${f}] - ${parallel.length} parallel tasks:`); + for (const s of parallel) { + console.log(` ${s.seq} - ${s.title}`); + } + console.log(); + } + } +} + +function cmdDeps(feature: string, seq: string): void { + const subtasks = loadSubtasks(feature); + const target = subtasks.find((s: Subtask) => s.seq === seq); + + if (!target) { + console.log(`Task ${seq} not found in ${feature}`); + return; + } + + console.log(`\n=== Dependency Tree: ${feature}/${seq} ===\n`); + console.log(`${seq} - ${target.title} [${target.status}]`); + + if (target.depends_on.length === 0) { + console.log(' └── (no dependencies)'); + return; + } + + const printDeps = (seqs: string[], indent: string = ' '): void => { + for (let i = 0; i < seqs.length; i++) { + const depSeq = seqs[i]; + const dep = subtasks.find((s: Subtask) => s.seq === depSeq); + const isLast = i === seqs.length - 1; + const branch = isLast ? '└──' : '├──'; + + if (dep) { + const statusIcon = dep.status === 'completed' ? '✓' : dep.status === 'in_progress' ? '~' : '○'; + console.log(`${indent}${branch} ${statusIcon} ${depSeq} - ${dep.title} [${dep.status}]`); + if (dep.depends_on.length > 0) { + const newIndent = indent + (isLast ? ' ' : '│ '); + printDeps(dep.depends_on, newIndent); + } + } else { + console.log(`${indent}${branch} ? ${depSeq} - NOT FOUND`); + } + } + }; + + printDeps(target.depends_on); +} + +function cmdBlocked(feature?: string): void { + const features = feature ? [feature] : getFeatureDirs(); + + console.log('\n=== Blocked Tasks ===\n'); + + for (const f of features) { + const subtasks = loadSubtasks(f); + const completedSeqs = new Set( + subtasks.filter((s: Subtask) => s.status === 'completed').map((s: Subtask) => s.seq) + ); + + const blocked = subtasks.filter((s: Subtask) => { + if (s.status === 'blocked') return true; + if (s.status !== 'pending') return false; + return !s.depends_on.every((dep: string) => completedSeqs.has(dep)); + }); + + if (blocked.length > 0) { + console.log(`[${f}]`); + for (const s of blocked) { + const waitingFor = s.depends_on.filter((dep: string) => !completedSeqs.has(dep)); + const reason = s.status === 'blocked' + ? 'explicitly blocked' + : `waiting: ${waitingFor.join(', ')}`; + console.log(` ${s.seq} - ${s.title} (${reason})`); + } + console.log(); + } + } +} + +function cmdComplete(feature: string, seq: string, summary: string): void { + if (summary.length > 200) { + console.log('Error: Summary must be max 200 characters'); + process.exit(1); + } + + const subtasks = loadSubtasks(feature); + const subtask = subtasks.find((s: Subtask) => s.seq === seq); + + if (!subtask) { + console.log(`Task ${seq} not found in ${feature}`); + process.exit(1); + } + + subtask.status = 'completed'; + subtask.completed_at = new Date().toISOString(); + subtask.completion_summary = summary; + + saveSubtask(feature, subtask); + + // Update task.json counts + const task = loadTask(feature); + if (task) { + const newSubtasks = loadSubtasks(feature); + task.completed_count = newSubtasks.filter((s: Subtask) => s.status === 'completed').length; + saveTask(feature, task); + } + + console.log(`\n✓ Marked ${feature}/${seq} as completed`); + console.log(` Summary: ${summary}`); + + if (task) { + console.log(` Progress: ${task.completed_count}/${task.subtask_count}`); + } +} + +function cmdValidate(feature?: string): void { + const features = feature ? [feature] : getFeatureDirs(); + let hasErrors = false; + + console.log('\n=== Validation Results ===\n'); + + for (const f of features) { + const errors: string[] = []; + const warnings: string[] = []; + + // Check task.json exists + const task = loadTask(f); + if (!task) { + errors.push('Missing task.json'); + } + + // Load and validate subtasks + const subtasks = loadSubtasks(f); + const seqs = new Set(subtasks.map((s: Subtask) => s.seq)); + + for (const s of subtasks) { + // Check ID format + if (!s.id.startsWith(f)) { + errors.push(`${s.seq}: ID should start with feature name`); + } + + // Check for missing dependencies + for (const dep of s.depends_on) { + if (!seqs.has(dep)) { + errors.push(`${s.seq}: depends on non-existent task ${dep}`); + } + } + + // Check for circular dependencies + const visited = new Set(); + const checkCircular = (currentSeq: string, pathArr: string[]): boolean => { + if (pathArr.includes(currentSeq)) { + errors.push(`${s.seq}: circular dependency detected: ${[...pathArr, currentSeq].join(' -> ')}`); + return true; + } + if (visited.has(currentSeq)) return false; + visited.add(currentSeq); + + const currentTask = subtasks.find((t: Subtask) => t.seq === currentSeq); + if (currentTask) { + for (const dep of currentTask.depends_on) { + if (checkCircular(dep, [...pathArr, currentSeq])) return true; + } + } + return false; + }; + checkCircular(s.seq, []); + + // Warnings + if (s.acceptance_criteria.length === 0) { + warnings.push(`${s.seq}: No acceptance criteria defined`); + } + if (s.deliverables.length === 0) { + warnings.push(`${s.seq}: No deliverables defined`); + } + } + + // Check counts match + if (task && task.subtask_count !== subtasks.length) { + errors.push(`task.json subtask_count (${task.subtask_count}) doesn't match actual count (${subtasks.length})`); + } + + // Print results + console.log(`[${f}]`); + if (errors.length === 0 && warnings.length === 0) { + console.log(' ✓ All checks passed'); + } else { + for (const e of errors) { + console.log(` ✗ ERROR: ${e}`); + hasErrors = true; + } + for (const w of warnings) { + console.log(` ⚠ WARNING: ${w}`); + } + } + console.log(); + } + + process.exit(hasErrors ? 1 : 0); +} + +function cmdArchive(feature: string): void { + const task = loadTask(feature); + if (!task) { + console.log(`Feature ${feature} not found`); + process.exit(1); + } + + const subtasks = loadSubtasks(feature); + const completedCount = subtasks.filter((s: Subtask) => s.status === 'completed').length; + + if (completedCount !== subtasks.length) { + console.log(`Cannot archive: ${completedCount}/${subtasks.length} tasks completed`); + process.exit(1); + } + + // Update task status + task.status = 'completed'; + task.completed_at = new Date().toISOString(); + saveTask(feature, task); + + // Move to completed + const srcDir = path.join(ACTIVE_DIR, feature); + const destDir = path.join(COMPLETED_DIR, feature); + ensureDir(COMPLETED_DIR); + fs.renameSync(srcDir, destDir); + + console.log(`\n✓ Archived ${feature} to .tmp/tasks/completed/`); +} + +// Main +const [,, command, ...args] = process.argv; + +switch (command) { + case 'init': + cmdInit(); + break; + case 'status': + cmdStatus(args[0]); + break; + case 'next': + cmdNext(args[0]); + break; + case 'parallel': + cmdParallel(args[0]); + break; + case 'deps': + if (args.length < 2) { + console.log('Usage: deps '); + process.exit(1); + } + cmdDeps(args[0], args[1]); + break; + case 'blocked': + cmdBlocked(args[0]); + break; + case 'complete': + if (args.length < 3) { + console.log('Usage: complete "summary"'); + process.exit(1); + } + cmdComplete(args[0], args[1], args.slice(2).join(' ')); + break; + case 'validate': + cmdValidate(args[0]); + break; + case 'archive': + if (!args[0]) { + console.log('Usage: archive '); + process.exit(1); + } + cmdArchive(args[0]); + break; + default: + console.log(` +Task Management CLI + +Location: .tmp/tasks/active/{feature}/ and .tmp/tasks/completed/{feature}/ + +Usage: npx ts-node .opencode/scripts/task-cli.ts [args...] + +Commands: + init Create .tmp/tasks/ directory structure + status [feature] Show task status summary + next [feature] Show next eligible tasks (deps satisfied) + parallel [feature] Show parallel tasks ready to run + deps Show dependency tree for a task + blocked [feature] Show blocked tasks and why + complete "summary" Mark task completed with summary + validate [feature] Validate JSON files and dependencies + archive Move completed feature to completed/ + +Examples: + npx ts-node .opencode/scripts/task-cli.ts init + npx ts-node .opencode/scripts/task-cli.ts status + npx ts-node .opencode/scripts/task-cli.ts next my-feature + npx ts-node .opencode/scripts/task-cli.ts complete my-feature 02 "Implemented auth module" +`); +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..27895a5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Darren Hinde + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index c72d245..ccfe3de 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ### AI agent framework for plan-first development workflows with approval-based execution [![GitHub stars](https://img.shields.io/github/stars/darrenhinde/OpenAgents?style=social)](https://github.com/darrenhinde/OpenAgents/stargazers) +[![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/DarrenBuildsAI?style=social)](https://x.com/DarrenBuildsAI) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GitHub last commit](https://img.shields.io/github/last-commit/darrenhinde/OpenAgents)](https://github.com/darrenhinde/OpenAgents/commits/main) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](docs/contributing/CONTRIBUTING.md) @@ -250,7 +251,7 @@ User Request - **/test** - Testing workflows - **/clean** - Cleanup operations - **/context** - Context management -- **/prompt-enchancer** - Improve your prompts +- **/prompt-enhancer** - Improve your prompts - **/worktrees** - Git worktree management - **/validate-repo** - Validate repository consistency @@ -589,11 +590,12 @@ opencode --agent opencoder - [OpenCoder Guide](docs/agents/opencoder.md) - Specialized development work --- -## Support This Work +## Support & Connect -If this helped you out and you're feeling generous, consider funding my coffee habit ☕ +If this helped you out, I'd love to hear about it! -[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-yellow?style=for-the-badge&logo=buy-me-a-coffee)](https://buymeacoffee.com/darrenhinde) +- **Follow on X**: [@DarrenBuildsAI](https://x.com/DarrenBuildsAI) - I post updates on AI agents and OpenCode workflows. +- **Support the Work**: [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-yellow?style=for-the-badge&logo=buy-me-a-coffee)](https://buymeacoffee.com/darrenhinde) Totally optional, but appreciated. diff --git a/dev/dashboard-project/main.js b/dev/dashboard-project/main.js new file mode 100644 index 0000000..20b6288 --- /dev/null +++ b/dev/dashboard-project/main.js @@ -0,0 +1,12 @@ +import { dashboardController } from './ui/index.js'; + +/** + * Main entry point for the Dashboard project. + */ +const main = () => { + console.log('Initializing Dashboard...'); + const output = dashboardController(); + console.log(output); +}; + +main(); diff --git a/dev/dashboard-project/package.json b/dev/dashboard-project/package.json new file mode 100644 index 0000000..6d69867 --- /dev/null +++ b/dev/dashboard-project/package.json @@ -0,0 +1,7 @@ +{ + "name": "dashboard-project", + "version": "1.0.0", + "type": "module", + "description": "A modular dashboard project with Time, Period, and Star Sign APIs.", + "main": "main.js" +} diff --git a/dev/dashboard-project/period/core.js b/dev/dashboard-project/period/core.js new file mode 100644 index 0000000..e4ac621 --- /dev/null +++ b/dev/dashboard-project/period/core.js @@ -0,0 +1,23 @@ +/** + * Core logic for the Period component. + */ + +export const PERIODS = { + DAY: 'day', + WEEK: 'week', + MONTH: 'month', + YEAR: 'year' +}; + +/** + * Returns all available periods. + * @returns {string[]} + */ +export const getAvailablePeriods = () => Object.values(PERIODS); + +/** + * Validates if a period is supported. + * @param {string} period + * @returns {boolean} + */ +export const isValidPeriod = (period) => getAvailablePeriods().includes(period); diff --git a/dev/dashboard-project/period/index.js b/dev/dashboard-project/period/index.js new file mode 100644 index 0000000..0fc71d8 --- /dev/null +++ b/dev/dashboard-project/period/index.js @@ -0,0 +1,11 @@ +import { getAvailablePeriods } from './core.js'; +import { success } from '../shared/utils.js'; + +/** + * Period API Handler. + * @returns {Object} + */ +export const getPeriodsHandler = () => { + const periods = getAvailablePeriods(); + return success({ periods }); +}; diff --git a/dev/dashboard-project/period/tests/period.test.js b/dev/dashboard-project/period/tests/period.test.js new file mode 100644 index 0000000..e69de29 diff --git a/dev/dashboard-project/shared/index.js b/dev/dashboard-project/shared/index.js new file mode 100644 index 0000000..e69de29 diff --git a/dev/dashboard-project/shared/utils.js b/dev/dashboard-project/shared/utils.js new file mode 100644 index 0000000..ed80d06 --- /dev/null +++ b/dev/dashboard-project/shared/utils.js @@ -0,0 +1,25 @@ +/** + * Shared utility functions for the Dashboard project. + */ + +/** + * Wraps a value in a success object. + * @param {*} data + * @returns {Object} + */ +export const success = (data) => ({ success: true, data }); + +/** + * Wraps an error message in a failure object. + * @param {string} error + * @returns {Object} + */ +export const failure = (error) => ({ success: false, error }); + +/** + * Generates a random integer between min and max (inclusive). + * @param {number} min + * @param {number} max + * @returns {number} + */ +export const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; diff --git a/dev/dashboard-project/star-sign/core.js b/dev/dashboard-project/star-sign/core.js new file mode 100644 index 0000000..b7b2137 --- /dev/null +++ b/dev/dashboard-project/star-sign/core.js @@ -0,0 +1,19 @@ +/** + * Core logic for the Star Sign component. + */ + +export const STAR_SIGNS = [ + 'Aries', 'Taurus', 'Gemini', 'Cancer', + 'Leo', 'Virgo', 'Libra', 'Scorpio', + 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces' +]; + +/** + * Returns a random star sign from the list. + * @param {Function} getRandomInt - Dependency injection for randomness. + * @returns {string} + */ +export const getRandomStarSign = (getRandomInt) => { + const index = getRandomInt(0, STAR_SIGNS.length - 1); + return STAR_SIGNS[index]; +}; diff --git a/dev/dashboard-project/star-sign/index.js b/dev/dashboard-project/star-sign/index.js new file mode 100644 index 0000000..488ca93 --- /dev/null +++ b/dev/dashboard-project/star-sign/index.js @@ -0,0 +1,11 @@ +import { getRandomStarSign } from './core.js'; +import { success, getRandomInt } from '../shared/utils.js'; + +/** + * Star Sign API Handler. + * @returns {Object} + */ +export const getStarSignHandler = () => { + const starSign = getRandomStarSign(getRandomInt); + return success({ starSign }); +}; diff --git a/dev/dashboard-project/star-sign/tests/star-sign.test.js b/dev/dashboard-project/star-sign/tests/star-sign.test.js new file mode 100644 index 0000000..e69de29 diff --git a/dev/dashboard-project/time/core.js b/dev/dashboard-project/time/core.js new file mode 100644 index 0000000..a28923b --- /dev/null +++ b/dev/dashboard-project/time/core.js @@ -0,0 +1,18 @@ +/** + * Core logic for the Time component. + * Pure functions only. + */ + +/** + * Returns the current time in ISO format. + * @param {Date} [date=new Date()] - Optional date for testing. + * @returns {string} + */ +export const getCurrentTimeISO = (date = new Date()) => date.toISOString(); + +/** + * Formats a date to a human-readable string. + * @param {Date} date + * @returns {string} + */ +export const formatHumanReadable = (date) => date.toLocaleString(); diff --git a/dev/dashboard-project/time/index.js b/dev/dashboard-project/time/index.js new file mode 100644 index 0000000..ac7d606 --- /dev/null +++ b/dev/dashboard-project/time/index.js @@ -0,0 +1,11 @@ +import { getCurrentTimeISO } from './core.js'; +import { success } from '../shared/utils.js'; + +/** + * Time API Handler. + * @returns {Object} + */ +export const getTimeHandler = () => { + const time = getCurrentTimeISO(); + return success({ time }); +}; diff --git a/dev/dashboard-project/time/tests/time.test.js b/dev/dashboard-project/time/tests/time.test.js new file mode 100644 index 0000000..e69de29 diff --git a/dev/dashboard-project/ui/core.js b/dev/dashboard-project/ui/core.js new file mode 100644 index 0000000..cbb9552 --- /dev/null +++ b/dev/dashboard-project/ui/core.js @@ -0,0 +1,26 @@ +/** + * Core logic for the Dashboard UI. + * Pure functions for rendering/formatting. + */ + +/** + * Renders the dashboard state into a string (simulating a UI component). + * @param {Object} state + * @returns {string} + */ +export const renderDashboard = (state) => { + const { time, periods, starSign } = state; + + return ` +========================================= + DASHBOARD OVERVIEW +========================================= +Current Time: ${time || 'Loading...'} +----------------------------------------- +Available Periods: +${periods ? periods.map(p => `- ${p}`).join('\n') : 'Loading...'} +----------------------------------------- +Visitor Star Sign: ${starSign || 'Loading...'} +========================================= + `; +}; diff --git a/dev/dashboard-project/ui/index.js b/dev/dashboard-project/ui/index.js new file mode 100644 index 0000000..5802a28 --- /dev/null +++ b/dev/dashboard-project/ui/index.js @@ -0,0 +1,23 @@ +import { renderDashboard } from './core.js'; +import { getTimeHandler } from '../time/index.js'; +import { getPeriodsHandler } from '../period/index.js'; +import { getStarSignHandler } from '../star-sign/index.js'; + +/** + * Dashboard UI Controller. + * Orchestrates data fetching and rendering. + * @returns {string} + */ +export const dashboardController = () => { + const timeData = getTimeHandler(); + const periodData = getPeriodsHandler(); + const starSignData = getStarSignHandler(); + + const state = { + time: timeData.success ? timeData.data.time : 'Error', + periods: periodData.success ? periodData.data.periods : [], + starSign: starSignData.success ? starSignData.data.starSign : 'Error' + }; + + return renderDashboard(state); +}; diff --git a/dev/dashboard-project/ui/tests/ui.test.js b/dev/dashboard-project/ui/tests/ui.test.js new file mode 100644 index 0000000..e69de29 diff --git a/docs/features/abilities-system/PLAN.md b/docs/features/abilities-system/PLAN.md new file mode 100644 index 0000000..cbfec71 --- /dev/null +++ b/docs/features/abilities-system/PLAN.md @@ -0,0 +1,757 @@ +# Abilities System - Implementation Plan + +> **Status:** Planning Complete - Ready for Implementation +> **Issue:** [#33](https://github.com/darrenhinde/OpenAgents/issues/33) +> **Date:** December 31, 2025 + +--- + +## Vision + +**Enforced, validated workflows that work with any agent and guarantee execution.** + +Abilities solve the fundamental problem with Skills: **LLMs ignore them**. With Abilities: +- Steps **must** run (enforced via hooks) +- Scripts run **deterministically** (no AI variance) +- Validation **guarantees** each step completed +- Multi-agent coordination **just works** + +--- + +## Problem Statement + +### Current State (Skills) + +| Issue | Impact | +|-------|--------| +| LLM ignores skill instructions | Critical steps skipped | +| No enforcement mechanism | Can't guarantee execution | +| No validation | Don't know if steps completed | +| Pure AI (unpredictable) | Results vary each run | +| Single agent only | No coordination | + +### Desired State (Abilities) + +| Feature | Benefit | +|---------|---------| +| Hook enforcement | AI **cannot** skip steps | +| Script steps | Deterministic execution | +| Validation rules | Guaranteed completion | +| Multi-agent support | Coordinate any OpenAgents agent | +| Approval gates | Human-in-the-loop where needed | + +--- + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ USER / AGENT │ +│ "Deploy v1.2.3 to production" │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ TRIGGER DETECTION │ +│ Keywords / Patterns / Explicit Command │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ INPUT VALIDATION │ +│ Zod Schema / Type Checking │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ PLAN APPROVAL │ +│ Show Steps → Get User Approval │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ ENFORCED EXECUTION │ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ Script │ → │ Agent │ → │Approval │ → │ Script │ │ +│ │ Step │ │ Step │ │ Step │ │ Step │ │ +│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ +│ │ +│ Hooks block tools outside current step │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ COMPLETION │ +│ Report Results / Run After Hooks │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## File Structure + +### Directory Layout + +``` +.opencode/ +├── abilities/ # Root abilities folder +│ ├── deploy/ # Grouped by domain +│ │ ├── ability.yaml # Main ability definition +│ │ ├── scripts/ # Associated scripts +│ │ │ ├── test.sh +│ │ │ ├── build.sh +│ │ │ └── deploy.sh +│ │ └── hooks/ # Optional hooks +│ │ ├── before/ +│ │ │ └── validate.md +│ │ └── after/ +│ │ └── notify.sh +│ │ +│ ├── development/ # Another domain +│ │ ├── build-feature/ +│ │ │ └── ability.yaml +│ │ ├── refactor/ +│ │ │ └── ability.yaml +│ │ └── test-suite/ +│ │ └── ability.yaml +│ │ +│ └── simple-ability.yaml # Can also be flat files +│ +└── agents/ + └── my-agent.md # Agent with abilities attached +``` + +### Discovery Rules + +``` +Priority order: +1. .opencode/abilities/**/*.yaml (project - nested) +2. .opencode/abilities/*.yaml (project - flat) +3. ~/.config/opencode/abilities/ (global) +``` + +### Naming Convention + +```yaml +# File: .opencode/abilities/deploy/production/ability.yaml +# Auto-generated name: deploy/production + +# Or explicit override: +name: deploy-to-prod # Custom name +``` + +--- + +## YAML Format Specification + +### Complete Example + +```yaml +# ability.yaml +name: safe-deploy # Unique identifier +description: Deploy with safety checks +version: 1.0.0 # Optional versioning + +# ───────────────────────────────────────────── +# TRIGGERS - When this ability activates +# ───────────────────────────────────────────── +triggers: + keywords: # Keyword matching + - "deploy" + - "ship it" + - "release to production" + patterns: # Regex patterns (optional) + - "deploy.*to.*prod" + +# ───────────────────────────────────────────── +# INPUTS - Validated before execution +# ───────────────────────────────────────────── +inputs: + version: + type: string + required: true + pattern: '^v\d+\.\d+\.\d+$' # Semver validation + description: "Version to deploy (e.g., v1.2.3)" + + environment: + type: string + required: true + enum: [staging, production] + default: staging + +# ───────────────────────────────────────────── +# STEPS - Executed in order (enforced) +# ───────────────────────────────────────────── +steps: + - id: test + type: script + description: Run test suite + run: npm test + validation: + exit_code: 0 + on_failure: stop + + - id: build + type: script + description: Build for target environment + run: npm run build -- --env={{inputs.environment}} + needs: [test] + timeout: 5m + + - id: review + type: agent + description: Security review + agent: reviewer + prompt: | + Review the build output for security issues. + Environment: {{inputs.environment}} + needs: [build] + + - id: approve + type: approval + description: Production approval + when: inputs.environment == "production" + prompt: | + Ready to deploy {{inputs.version}} to {{inputs.environment}}. + Proceed? + needs: [review] + + - id: deploy + type: script + description: Deploy to environment + run: ./scripts/deploy.sh {{inputs.environment}} {{inputs.version}} + needs: [approve] + validation: + exit_code: 0 + +# ───────────────────────────────────────────── +# SETTINGS - Ability-level configuration +# ───────────────────────────────────────────── +settings: + timeout: 30m # Total timeout + parallel: false # Sequential by default + enforcement: strict # strict | normal | loose + on_failure: stop # Default failure behavior +``` + +### Step Types + +#### Script Step +```yaml +- id: test + type: script + run: npm test # Command to run + cwd: ./packages/api # Working directory (optional) + env: # Environment variables + NODE_ENV: test + validation: + exit_code: 0 # Required exit code + stdout_contains: "passed" # Optional + file_exists: ./coverage/ # Optional + timeout: 10m + on_failure: stop | continue | retry + max_retries: 2 +``` + +#### Agent Step +```yaml +- id: review + type: agent + agent: reviewer # Agent name (from OpenAgents) + prompt: "Review this code..." # Task for agent + context: # Additional context (optional) + - ./src/ + - {{steps.build.output}} + timeout: 5m + on_failure: stop +``` + +#### Skill Step +```yaml +- id: docs + type: skill + skill: generate-docs # Skill name + inputs: # Skill inputs (optional) + format: markdown +``` + +#### Approval Step +```yaml +- id: approve + type: approval + prompt: "Deploy to production?" + options: # Custom options (optional) + - label: Approve + value: approved + - label: Reject + value: rejected + timeout: 1h # Auto-reject after timeout + when: inputs.environment == "production" +``` + +#### Workflow Step (Nested) +```yaml +- id: setup + type: workflow + workflow: setup-environment # Call another ability + inputs: + env: {{inputs.environment}} +``` + +### Step Properties Reference + +| Property | Required | Default | Description | +|----------|----------|---------|-------------| +| `id` | Yes | - | Unique step identifier | +| `type` | Yes | - | script, agent, skill, approval, workflow | +| `description` | No | - | Human-readable description | +| `needs` | No | `[]` | Dependencies (steps that must complete first) | +| `when` | No | `true` | Conditional execution | +| `timeout` | No | `5m` | Max duration | +| `on_failure` | No | `stop` | stop, continue, retry, ask | +| `max_retries` | No | `1` | Retry attempts (if retry) | + +--- + +## Attaching Abilities to Agents + +### Option A: Agent Frontmatter + +```markdown +--- +name: deploy-agent +description: Handles deployments +model: anthropic/claude-sonnet-4 + +abilities: + - deploy/production + - deploy/staging + - rollback +--- + +You are a deployment specialist... +``` + +### Option B: Global Config + +```json +{ + "agents": { + "deploy-agent": { + "abilities": ["deploy/production", "deploy/staging"] + } + } +} +``` + +### Option C: Ability Specifies Agents + +```yaml +# In ability.yaml +compatible_agents: + - deploy-agent + - default +``` + +--- + +## Enforcement Mechanism + +### Hook-Based Enforcement + +```typescript +// tool.execute.before - Block tools outside current step +async "tool.execute.before"(ctx, tool) { + const ability = ctx.state.activeAbility + if (!ability) return + + const currentStep = ability.currentStep + + // Script steps block ALL tools + if (currentStep.type === 'script') { + throw new Error(`Step '${currentStep.id}' is running. Wait for completion.`) + } + + // Agent steps allow only that agent's tools + if (currentStep.type === 'agent') { + const allowed = getAgentTools(currentStep.agent) + if (!allowed.includes(tool.name)) { + throw new Error(`Tool '${tool.name}' not allowed in step '${currentStep.id}'`) + } + } +} + +// chat.message - Inject ability context +async "chat.message"(ctx, message) { + const ability = ctx.state.activeAbility + if (!ability) return + + message.parts.unshift({ + type: "text", + synthetic: true, + text: `## Active Ability: ${ability.name} +Current Step: ${ability.currentStep.id} +Progress: ${ability.completedSteps.length}/${ability.steps.length} + +You MUST complete this step before proceeding.` + }) +} + +// session.idle - Prevent exit without completion +async "session.idle"(ctx) { + const ability = ctx.state.activeAbility + if (ability?.status === 'running') { + return { + inject: `Ability '${ability.name}' is still running. Complete it first.` + } + } +} +``` + +### Enforcement Levels + +| Level | Behavior | +|-------|----------| +| `strict` | Block ALL tools outside current step, cannot exit | +| `normal` | Block destructive tools, can exit with warning | +| `loose` | Advisory only, can skip with confirmation | + +--- + +## Context Passing + +### Automatic (Default) + +Steps with `needs` automatically receive prior step outputs: + +```yaml +steps: + - id: research + agent: librarian + prompt: "Research: {{input}}" + + - id: plan + agent: oracle + needs: [research] # Gets research output automatically + prompt: "Create plan based on the research" +``` + +When `plan` runs, executor injects: + +```markdown +## Context from prior steps + +### Step: research (librarian) +[Full output from research step] + +--- + +## Your task +Create plan based on the research +``` + +### Auto-Truncation + +Large outputs (>10k tokens) are automatically summarized before passing. + +### Manual Summary + +```yaml +- id: research + agent: librarian + prompt: "Research: {{input}}" + summarize: true # Condense output + # OR + summarize: "Extract only the key patterns" # Custom prompt +``` + +--- + +## Validation System + +### Schema Validation (Zod) + +```typescript +const AbilitySchema = z.object({ + name: z.string().regex(/^[a-z0-9-/]+$/), + description: z.string(), + version: z.string().optional(), + triggers: TriggersSchema.optional(), + inputs: z.record(InputSchema).optional(), + steps: z.array(StepSchema).min(1), + settings: SettingsSchema.optional(), +}) +``` + +### Dependency Validation + +- Check all `needs` references exist +- Detect circular dependencies +- Verify agents exist + +### CLI Commands + +```bash +/ability validate deploy/production # Validate single +/ability validate --all # Validate all +``` + +--- + +## SDK Integration + +### Using from Subagents + +```typescript +import { OpenCodeSDK } from '@opencode/sdk' + +const sdk = new OpenCodeSDK() + +// List abilities +const abilities = await sdk.abilities.list() + +// Execute +const result = await sdk.abilities.execute({ + name: 'deploy/production', + inputs: { version: 'v1.2.3', environment: 'staging' } +}) + +// Check status +const status = await sdk.abilities.status(result.executionId) + +// Wait for completion +const final = await sdk.abilities.waitFor(result.executionId) +``` + +--- + +## Implementation Phases + +### Phase 1: Foundation (Week 1) ✅ COMPLETED + +- [x] File structure & discovery + - [x] Load from .opencode/abilities/ (nested + flat) + - [x] Load from global config + - [x] Name resolution from path + +- [x] YAML parser & validator + - [x] Zod schema for ability format + - [x] Dependency validation + - [x] Agent existence check + +- [x] Basic executor + - [x] Script step execution + - [x] Sequential execution only + - [x] Basic error handling + +- [x] CLI commands (as plugin tools) + - [x] ability.list + - [x] ability.validate + - [x] ability.run + +**Deliverable:** Load, validate, and run script-only abilities. ✅ + +### Phase 2: Agent Integration (Week 2) ✅ COMPLETED + +- [x] Agent steps + - [x] Execute agent with prompt + - [x] Pass context from prior steps + - [x] Handle agent responses + +- [x] Skill steps + - [x] Load and execute skills + - [x] Pass skill inputs + +- [x] Approval steps + - [x] Display approval prompt + - [x] Handle approve/reject + - [x] Conditional approvals (when:) + +- [x] Trigger detection + - [x] Keyword matching in messages + - [x] Pattern matching + - [x] Auto-activation via chat.message hook + +**Deliverable:** Run mixed script/agent/skill abilities with approvals. ✅ + +**Test Results:** 52 tests passing across 4 test files + +### Phase 3: Enforcement (Week 3) ✅ COMPLETED + +- [x] Hook enforcement + - [x] tool.execute.before blocking (with ALLOWED_TOOLS_BY_STEP_TYPE) + - [x] chat.message injection (buildAbilityContextInjection) + - [x] session.idle continuation (handleSessionIdle) + +- [x] Agent attachment + - [x] Agent-ability bindings (registerAgentAbilities) + - [x] agent.changed event handling + - [x] Per-ability agent restrictions (compatible_agents, exclusive_agent) + - [x] ability.agent tool to list agent's abilities + +- [x] Execution state + - [x] Track active ability (ExecutionManager.getActive()) + - [x] Track current step (execution.currentStep) + - [x] Track completed steps (execution.completedSteps) + +**Test Results:** 66 tests passing across 5 test files + +**Deliverable:** Full enforcement - agents can't skip steps. ✅ + +### Phase 4: Polish (Week 4) ✅ COMPLETED + +- [x] Context passing + - [x] Auto-pass prior step outputs (buildPriorContext) + - [x] Auto-truncate large outputs (MAX_OUTPUT_CHARS = 40k, MAX_CONTEXT_CHARS = 80k) + - [x] Variable interpolation ({{inputs.X}}, {{steps.Y.output}}) + - [x] Summarization support (summarize: true flag on steps) + +- [x] Nested workflows + - [x] Workflow step type implementation + - [x] Input passing to child (interpolated from parent inputs) + - [x] Abilities context in executor + +- [x] SDK integration + - [x] AbilitiesSDK class with clean API + - [x] abilities.list() with full metadata + - [x] abilities.execute() with context support + - [x] abilities.status() and abilities.cancel() + - [x] abilities.waitFor() for async execution + - [x] createAbilitiesSDK() factory function + +- [x] Documentation + - [x] Updated README with OpenCode integration + - [x] SDK usage examples + - [x] Package exports for plugin, opencode, and sdk + +**Test Results:** 87 tests passing across 7 test files + +**Deliverable:** Production-ready abilities system. ✅ + +--- + +## Example Abilities + +### Simple: Run Tests + +```yaml +name: test +description: Run test suite with coverage + +steps: + - id: test + type: script + run: npm test -- --coverage + validation: + exit_code: 0 +``` + +### Medium: Code Review + +```yaml +name: review +description: AI-powered code review + +triggers: + keywords: ["review", "check my code"] + +steps: + - id: get-diff + type: script + run: git diff --staged > /tmp/diff.txt + + - id: review + type: agent + agent: reviewer + prompt: | + Review this code diff for issues. + {{steps.get-diff.output}} +``` + +### Complex: Full Deploy + +```yaml +name: deploy/production +description: Full production deployment pipeline + +triggers: + keywords: ["deploy to prod", "release", "ship it"] + +inputs: + version: + type: string + required: true + pattern: '^v\d+\.\d+\.\d+$' + +steps: + - id: test + type: script + run: npm test + validation: + exit_code: 0 + + - id: build + type: script + run: npm run build + needs: [test] + + - id: security-scan + type: agent + agent: reviewer + prompt: "Scan for security vulnerabilities" + needs: [build] + + - id: deploy-staging + type: script + run: ./deploy.sh staging {{inputs.version}} + needs: [build, security-scan] + + - id: smoke-test + type: agent + agent: tester + prompt: "Run smoke tests on staging" + needs: [deploy-staging] + + - id: approve + type: approval + prompt: "Deploy {{inputs.version}} to production?" + needs: [smoke-test] + + - id: deploy-prod + type: script + run: ./deploy.sh production {{inputs.version}} + needs: [approve] +``` + +--- + +## Success Criteria + +| Criteria | Measurement | +|----------|-------------| +| Abilities load correctly | All YAML files parse without error | +| Validation catches errors | Invalid YAMLs rejected with clear messages | +| Scripts execute | Exit codes captured, validation works | +| Agents integrate | Can call any OpenAgents agent | +| Enforcement works | Cannot skip steps when strict | +| Approvals work | Human gate blocks until approved | +| Context passes | Prior step outputs available to next | + +--- + +## References + +- [Issue #33](https://github.com/darrenhinde/OpenAgents/issues/33) - Original proposal +- [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) - Plugin patterns +- [OpenCode Skills](https://opencode.ai/docs/skills/) - Current skill system +- [Scott Spence - Making Skills Reliable](https://scottspence.com/posts/how-to-make-claude-code-skills-activate-reliably) + +--- + +## Next Steps + +1. **Create plugin scaffold** - packages/plugin-abilities/ +2. **Implement Phase 1** - Loader, parser, validator, script executor +3. **Test with simple ability** - Validate the foundation works +4. **Iterate** - Add agent steps, enforcement, etc. diff --git a/evals/agents/core/openagent/tests/08-delegation/task-manager-delegation.yaml b/evals/agents/core/openagent/tests/08-delegation/task-manager-delegation.yaml new file mode 100644 index 0000000..e704f70 --- /dev/null +++ b/evals/agents/core/openagent/tests/08-delegation/task-manager-delegation.yaml @@ -0,0 +1,34 @@ +id: task-manager-delegation-with-context +name: Task Manager Delegation With Context (Positive Test) +description: OpenAgent delegates complex task planning to TaskManager with context +category: developer +agent: openagent +prompt: | + Plan a multi-module feature for audit logging. Use TaskManager to break it down. + + Context bundle is available at: + .tmp/sessions/2026-01-11-audit-logging/context.md + +approvalStrategy: + type: auto-approve + +behavior: + mustUseTools: + - task + shouldDelegate: true + expectedResponse: + contains: + - ".tmp/sessions/2026-01-11-audit-logging/context.md" + - "TaskManager" + minToolCalls: 1 + +expectedViolations: + - rule: delegation + shouldViolate: false + severity: error + description: Complex task planning should be delegated to TaskManager + +tags: + - delegation + - task-manager + - openagent diff --git a/evals/agents/subagents/core/task-manager/config/config.yaml b/evals/agents/subagents/core/task-manager/config/config.yaml index aea1649..7fb423e 100644 --- a/evals/agents/subagents/core/task-manager/config/config.yaml +++ b/evals/agents/subagents/core/task-manager/config/config.yaml @@ -3,7 +3,7 @@ agent: subagents/core/task-manager model: anthropic/claude-sonnet-4-5 -timeout: 60000 +timeout: 120000 # Test suite configuration suites: diff --git a/evals/agents/subagents/core/task-manager/tests/02-context-loading.yaml b/evals/agents/subagents/core/task-manager/tests/02-context-loading.yaml index d9b41a2..893ba3b 100644 --- a/evals/agents/subagents/core/task-manager/tests/02-context-loading.yaml +++ b/evals/agents/subagents/core/task-manager/tests/02-context-loading.yaml @@ -1,27 +1,35 @@ id: core-task-manager-context-loading name: "Task Manager: Context Loading - Load Context Before Planning" description: | - Tests the task-manager's critical requirement to load context before - creating task breakdowns. + Tests the task-manager's requirement to load task-management context + before creating JSON task files. Validates: - - Checks for context bundle first - - Loads context files if available + - Loads task-management context files - Applies context to task planning - - References loaded context in plan + - Proceeds to JSON task creation when details are sufficient Critical Behavior: - - MUST check for context bundle (.tmp/context/{session}/bundle.md) - - MUST load project standards before planning - - MUST apply context to task breakdown - - MUST list context files used in plan + - MUST load navigation + core task-management guides + - MUST load task schema before planning category: developer prompts: - text: | - Break down a feature for "user authentication" into subtasks. Before you - start planning, check if there's a context bundle at - .tmp/test-fixtures/task-manager/bundle.md and load it if it exists. + Break down the feature "user-authentication" into JSON subtasks. + + Objective: Allow users to register, log in, and reset passwords. + Deliverables: + - auth service module + - user registration endpoint + - login endpoint + - password reset flow + + Constraints: + - Use existing auth patterns + - Include acceptance criteria per subtask + + Use the write tool to create .tmp/tasks/user-authentication/task.json and subtask_NN.json files. approvalStrategy: type: auto-approve @@ -29,16 +37,22 @@ approvalStrategy: behavior: mustUseTools: - read - minToolCalls: 1 - maxToolCalls: 10 + - bash + - write + requiresContext: true + expectedContextFiles: + - .opencode/context/core/task-management/navigation.md + - .opencode/context/core/task-management/standards/task-schema.md + - .opencode/context/core/task-management/guides/splitting-tasks.md + - .opencode/context/core/task-management/guides/managing-tasks.md + minToolCalls: 3 + maxToolCalls: 15 expectedViolations: - rule: approval-gate - shouldViolate: false - severity: error - - rule: context-loading - shouldViolate: false + shouldViolate: true severity: error + description: Subagents do not request approvals timeout: 120000 diff --git a/evals/agents/subagents/core/task-manager/tests/03-task-breakdown.yaml b/evals/agents/subagents/core/task-manager/tests/03-task-breakdown.yaml index b5d8a1b..4086ae5 100644 --- a/evals/agents/subagents/core/task-manager/tests/03-task-breakdown.yaml +++ b/evals/agents/subagents/core/task-manager/tests/03-task-breakdown.yaml @@ -2,47 +2,59 @@ id: core-task-manager-task-breakdown name: "Task Manager: Task Breakdown - Create Subtask Structure" description: | Tests the task-manager's ability to break down a feature into atomic, - implementation-ready subtasks. + implementation-ready JSON subtasks and create task files. Validates: - Creates feature slug (kebab-case) - Generates 2-digit task sequences - Defines clear objectives per task - Specifies deliverables - - Includes test requirements - Defines acceptance criteria - - Requests approval before file creation + - Includes exit criteria Critical Behavior: - - MUST create structured task plan + - MUST create structured JSON task plan - MUST use proper naming conventions - - MUST request approval before creating files - MUST include exit criteria category: developer prompts: - text: | - Break down a simple feature "add-dark-mode" into subtasks. The feature should: - 1. Add a theme toggle component - 2. Implement theme state management - 3. Apply dark mode styles - - Create the task breakdown plan but DO NOT create files yet - just show me - the plan and wait for approval. + Break down the feature "add-dark-mode" into JSON subtasks. + + Objective: Add a dark theme toggle and styles across the UI. + Deliverables: + - src/ui/theme-toggle.tsx + - src/state/theme-store.ts + - src/styles/dark-theme.css + + Context files: + - .opencode/context/core/standards/code-quality.md + + Use the write tool to create .tmp/tasks/add-dark-mode/task.json and subtask_NN.json files. approvalStrategy: type: auto-approve behavior: - mustNotUseTools: + mustUseTools: + - read + - bash - write - minToolCalls: 0 - maxToolCalls: 5 + requiresContext: true + expectedContextFiles: + - .opencode/context/core/task-management/navigation.md + - .opencode/context/core/task-management/standards/task-schema.md + - .opencode/context/core/task-management/guides/splitting-tasks.md + - .opencode/context/core/task-management/guides/managing-tasks.md + minToolCalls: 3 + maxToolCalls: 15 expectedViolations: - rule: approval-gate - shouldViolate: false + shouldViolate: true severity: error + description: Subagents do not request approvals timeout: 120000 @@ -51,4 +63,3 @@ tags: - breakdown - planning - core-subagent - - approval-required diff --git a/evals/agents/subagents/core/task-manager/tests/04-dependency-tracking.yaml b/evals/agents/subagents/core/task-manager/tests/04-dependency-tracking.yaml index 3c981e8..982de00 100644 --- a/evals/agents/subagents/core/task-manager/tests/04-dependency-tracking.yaml +++ b/evals/agents/subagents/core/task-manager/tests/04-dependency-tracking.yaml @@ -1,42 +1,61 @@ id: core-task-manager-dependency-tracking name: "Task Manager: Dependency Tracking - Map Task Dependencies" description: | - Tests the task-manager's ability to identify and map task dependencies. + Tests the task-manager's ability to identify and map task dependencies + in JSON subtask files. Validates: - Identifies task dependencies - - Maps dependency relationships - - Includes dependencies in plan - - Uses proper dependency format (seq -> seq) + - Maps dependency relationships in depends_on + - Includes dependencies in JSON tasks Critical Behavior: - MUST identify which tasks depend on others - - MUST map dependencies clearly - - MUST use format: {seq} depends on {seq} + - MUST map dependencies in depends_on arrays category: developer prompts: - text: | - Break down a feature "api-integration" with these tasks: + Break down the feature "api-integration" into JSON subtasks. + + Objective: Build a reusable API client with authentication and error handling. + Deliverables: + - src/api/client.ts + - src/api/auth.ts + - src/api/service.ts + - src/api/errors.ts + + Tasks: 1. Design API client interface 2. Implement HTTP client 3. Add authentication layer (depends on HTTP client) 4. Create API service methods (depends on auth layer) 5. Add error handling (depends on API service) - - Show me the task plan with clear dependency mapping. + + Use the write tool to create .tmp/tasks/api-integration/task.json and subtask_NN.json files. approvalStrategy: type: auto-approve behavior: - minToolCalls: 0 - maxToolCalls: 5 + mustUseTools: + - read + - bash + - write + requiresContext: true + expectedContextFiles: + - .opencode/context/core/task-management/navigation.md + - .opencode/context/core/task-management/standards/task-schema.md + - .opencode/context/core/task-management/guides/splitting-tasks.md + - .opencode/context/core/task-management/guides/managing-tasks.md + minToolCalls: 3 + maxToolCalls: 15 expectedViolations: - rule: approval-gate - shouldViolate: false + shouldViolate: true severity: error + description: Subagents do not request approvals timeout: 120000 diff --git a/evals/agents/subagents/core/task-manager/tests/05-file-creation.yaml b/evals/agents/subagents/core/task-manager/tests/05-file-creation.yaml index afb2684..2174213 100644 --- a/evals/agents/subagents/core/task-manager/tests/05-file-creation.yaml +++ b/evals/agents/subagents/core/task-manager/tests/05-file-creation.yaml @@ -1,48 +1,59 @@ id: core-task-manager-file-creation name: "Task Manager: File Creation - Create Task Files" description: | - Tests the task-manager's ability to create the task directory structure - and individual task files. + Tests the task-manager's ability to create JSON task files for a feature. Validates: - - Creates directory: .tmp/test-fixtures/task-manager/subtasks/{feature}/ - - Creates objective.md (feature index) - - Creates task files: {seq}-{task-description}.md - - Uses correct templates - - Includes all required sections + - Creates directory: .tmp/tasks/{feature}/ + - Creates task.json + - Creates subtask_NN.json files + - Includes required fields per schema - Summarizes what was created Critical Behavior: - - MUST request approval before creating files - MUST create proper directory structure - MUST use correct file naming - - MUST include all template sections + - MUST include required JSON fields category: developer prompts: - text: | - Create subtask files for a simple feature "user-profile" in - .tmp/test-fixtures/task-manager/subtasks/user-profile/ with these tasks: + Create JSON task files for the feature "user-profile" with these tasks: 1. Create profile data model 2. Build profile UI component 3. Add profile update API - - Create the full task structure with objective.md and individual task files. + + Objective: Build a profile management feature with UI and API support. + Deliverables: + - src/profile/model.ts + - src/profile/components/ProfileView.tsx + - src/profile/api/update-profile.ts + + Use the write tool to create .tmp/tasks/user-profile/task.json and subtask_NN.json files. approvalStrategy: type: auto-approve behavior: mustUseTools: + - read + - bash - write - minToolCalls: 2 + requiresContext: true + expectedContextFiles: + - .opencode/context/core/task-management/navigation.md + - .opencode/context/core/task-management/standards/task-schema.md + - .opencode/context/core/task-management/guides/splitting-tasks.md + - .opencode/context/core/task-management/guides/managing-tasks.md + minToolCalls: 3 maxToolCalls: 15 expectedViolations: - rule: approval-gate - shouldViolate: false + shouldViolate: true severity: error + description: Subagents do not request approvals timeout: 120000 @@ -51,4 +62,3 @@ tags: - file-creation - write - core-subagent - - approval-required diff --git a/evals/agents/subagents/core/task-manager/tests/06-missing-info.yaml b/evals/agents/subagents/core/task-manager/tests/06-missing-info.yaml new file mode 100644 index 0000000..85a1883 --- /dev/null +++ b/evals/agents/subagents/core/task-manager/tests/06-missing-info.yaml @@ -0,0 +1,49 @@ +id: core-task-manager-missing-info +name: "Task Manager: Missing Information - Request Clarification" +description: | + Tests the task-manager's Missing Information response when details are + insufficient to create JSON tasks. + + Validates: + - Returns Missing Information section + - Provides Suggested Prompt for the caller + - Avoids file creation when details are missing +category: developer + +prompts: + - text: | + Break down the feature "audit-logging" into JSON subtasks. + + Only create tasks if enough information is provided. + If details are missing, respond with the Missing Information format and do NOT call ContextScout. + +approvalStrategy: + type: auto-approve + +behavior: + mustUseTools: + - read + - bash + mustNotUseTools: + - write + - task + requiresContext: true + expectedContextFiles: + - .opencode/context/core/task-management/navigation.md + - .opencode/context/core/task-management/standards/task-schema.md + - .opencode/context/core/task-management/guides/splitting-tasks.md + - .opencode/context/core/task-management/guides/managing-tasks.md + expectedResponse: + contains: + - "Missing Information" + - "Suggested Prompt" + minToolCalls: 2 + maxToolCalls: 8 + +timeout: 120000 + +tags: + - task-manager + - missing-info + - planning + - core-subagent diff --git a/evals/agents/subagents/core/task-manager/tests/smoke-test.yaml b/evals/agents/subagents/core/task-manager/tests/smoke-test.yaml index 702c5f2..89f8730 100644 --- a/evals/agents/subagents/core/task-manager/tests/smoke-test.yaml +++ b/evals/agents/subagents/core/task-manager/tests/smoke-test.yaml @@ -13,19 +13,38 @@ category: developer prompts: - text: | - Analyze the evals/test_tmp directory and tell me how you would break down work here. + Break down the feature "profile-settings" into JSON subtasks. + + Objective: Allow users to update profile details and preferences. + Deliverables: + - src/profile/settings-form.tsx + - src/profile/preferences-store.ts + - src/profile/api/update-preferences.ts + + Use the write tool to create .tmp/tasks/profile-settings/task.json and subtask_NN.json files. approvalStrategy: type: auto-approve behavior: - minToolCalls: 1 - maxToolCalls: 5 + mustUseTools: + - read + - bash + - write + requiresContext: true + expectedContextFiles: + - .opencode/context/core/task-management/navigation.md + - .opencode/context/core/task-management/standards/task-schema.md + - .opencode/context/core/task-management/guides/splitting-tasks.md + - .opencode/context/core/task-management/guides/managing-tasks.md + minToolCalls: 3 + maxToolCalls: 15 expectedViolations: - rule: approval-gate - shouldViolate: false + shouldViolate: true severity: error + description: Subagents do not request approvals timeout: 60000 diff --git a/evals/results/latest.json b/evals/results/latest.json index fd40ea2..8a2c7cf 100644 --- a/evals/results/latest.json +++ b/evals/results/latest.json @@ -1,41 +1,52 @@ { "meta": { - "timestamp": "2026-01-09T13:10:17.718Z", - "agent": "ContextScout", + "timestamp": "2026-01-11T16:38:34.260Z", + "agent": "subagents/core/task-manager", "model": "opencode/grok-code-fast", "framework_version": "0.1.0", - "git_commit": "41c5917" + "git_commit": "f10c93d" }, "summary": { "total": 1, - "passed": 1, - "failed": 0, - "duration_ms": 15339, - "pass_rate": 1 + "passed": 0, + "failed": 1, + "duration_ms": 49410, + "pass_rate": 0 }, "by_category": { "developer": { - "passed": 1, + "passed": 0, "total": 1 } }, "tests": [ { - "id": "core-contextscout-smoke-test", + "id": "core-task-manager-smoke-test", "category": "developer", - "passed": true, - "duration_ms": 15339, - "events": 18, + "passed": false, + "duration_ms": 49410, + "events": 67, "approvals": 0, "violations": { - "total": 1, - "errors": 0, - "warnings": 1, + "total": 3, + "errors": 3, + "warnings": 0, "details": [ { - "type": "insufficient-read", - "severity": "warning", - "message": "Read/execution ratio < 1 (0.67)" + "type": "missing-approval", + "severity": "error", + "message": "Execution tool 'task' called without requesting approval", + "expected": true + }, + { + "type": "missing-required-tool", + "severity": "error", + "message": "Required tool 'bash' was not used" + }, + { + "type": "missing-required-tool", + "severity": "error", + "message": "Required tool 'write' was not used" } ] }