chore: install openagent opencode

Signed-off-by: Dmytro Stanchiev <git@dmytros.dev>
This commit is contained in:
2026-04-07 11:31:26 -04:00
parent b4c03ff25e
commit c2263602c4
204 changed files with 38010 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
# Context7 Skill
## Purpose
Fetches **live, version-specific documentation** for external libraries and frameworks using the Context7 API. Ensures you always get current API patterns instead of potentially outdated training data.
**Golden Rule**: Always fetch live docs for external libraries—training data may be outdated.
## Quick Start
### Recommended: Use ExternalScout Subagent
The **ExternalScout** subagent is the recommended way to fetch external documentation. It handles:
- Library detection
- Query optimization
- Documentation filtering and sorting
- Formatted results with code examples
**Invocation**:
```
Use ExternalScout to fetch documentation for [Library Name]: [your specific question]
```
**Example**:
```
Use ExternalScout to fetch documentation for Drizzle ORM: How do I set up modular schemas with PostgreSQL?
```
### Alternative: Direct Skill Usage
You can also invoke the Context7 skill directly via bash:
```bash
# Step 1: Search for library
curl -s "https://context7.com/api/v2/libs/search?libraryName=LIBRARY&query=TOPIC" | jq '.results[0]'
# Step 2: Fetch documentation
curl -s "https://context7.com/api/v2/context?libraryId=LIBRARY_ID&query=OPTIMIZED_QUERY&type=txt"
```
See `SKILL.md` for detailed API documentation.
## Supported Libraries
See `library-registry.md` for the complete list of supported libraries including:
- **Database & ORM**: Drizzle, Prisma
- **Authentication**: Better Auth, NextAuth.js, Clerk
- **Frontend**: Next.js, React, TanStack Query/Router/Start
- **Infrastructure**: Cloudflare Workers, AWS Lambda, Vercel
- **UI**: Shadcn/ui, Radix UI, Tailwind CSS
- **State**: Zustand, Jotai
- **Validation**: Zod, React Hook Form
- **Testing**: Vitest, Playwright
## Workflow
```
User Query
ContextScout (searches internal context)
No internal context found
ContextScout recommends ExternalScout
ExternalScout invoked
├─ Reads library-registry.md
├─ Detects library
├─ Loads query patterns
├─ Fetches from Context7 API
├─ Filters & sorts results
└─ Returns formatted documentation
User receives current, actionable docs
```
## Files
- **`SKILL.md`** - Context7 API documentation and usage
- **`library-registry.md`** - Supported libraries, aliases, and query patterns
- **`README.md`** - This file (overview and quick start)
## Adding New Libraries
To add a new library to the registry:
1. Edit `library-registry.md`
2. Add entry under appropriate category:
```markdown
#### Library Name
- **Aliases**: `alias1`, `alias2`, `package-name`
- **Docs**: https://example.com/docs
- **Context7**: `use context7 for library-name`
- **Common topics**: topic1, topic2, topic3
```
3. (Optional) Add query optimization patterns
4. ExternalScout will automatically detect the new library
## Related
- **ExternalScout**: `.opencode/agent/subagents/core/externalscout.md`
- **ContextScout**: `.opencode/agent/subagents/core/contextscout.md`

View File

@@ -0,0 +1,85 @@
---
name: context7
description: Retrieve up-to-date documentation for software libraries, frameworks, and components via the Context7 API. This skill should be used when looking up documentation for any programming library or framework, finding code examples for specific APIs or features, verifying correct usage of library functions, or obtaining current information about library APIs that may have changed since training.
---
# Context7
## Overview
This skill enables retrieval of current documentation for software libraries and components by querying the Context7 API via curl. Use it instead of relying on potentially outdated training data.
## Workflow
### Step 1: Search for the Library
To find the Context7 library ID, query the search endpoint:
```bash
curl -s "https://context7.com/api/v2/libs/search?libraryName=LIBRARY_NAME&query=TOPIC" | jq '.results[0]'
```
**Parameters:**
- `libraryName` (required): The library name to search for (e.g., "react", "nextjs", "fastapi", "axios")
- `query` (required): A description of the topic for relevance ranking
**Response fields:**
- `id`: Library identifier for the context endpoint (e.g., `/websites/react_dev_reference`)
- `title`: Human-readable library name
- `description`: Brief description of the library
- `totalSnippets`: Number of documentation snippets available
### Step 2: Fetch Documentation
To retrieve documentation, use the library ID from step 1:
```bash
curl -s "https://context7.com/api/v2/context?libraryId=LIBRARY_ID&query=TOPIC&type=txt"
```
**Parameters:**
- `libraryId` (required): The library ID from search results
- `query` (required): The specific topic to retrieve documentation for
- `type` (optional): Response format - `json` (default) or `txt` (plain text, more readable)
## Examples
### React hooks documentation
```bash
# Find React library ID
curl -s "https://context7.com/api/v2/libs/search?libraryName=react&query=hooks" | jq '.results[0].id'
# Returns: "/websites/react_dev_reference"
# Fetch useState documentation
curl -s "https://context7.com/api/v2/context?libraryId=/websites/react_dev_reference&query=useState&type=txt"
```
### Next.js routing documentation
```bash
# Find Next.js library ID
curl -s "https://context7.com/api/v2/libs/search?libraryName=nextjs&query=routing" | jq '.results[0].id'
# Fetch app router documentation
curl -s "https://context7.com/api/v2/context?libraryId=/vercel/next.js&query=app+router&type=txt"
```
### FastAPI dependency injection
```bash
# Find FastAPI library ID
curl -s "https://context7.com/api/v2/libs/search?libraryName=fastapi&query=dependencies" | jq '.results[0].id'
# Fetch dependency injection documentation
curl -s "https://context7.com/api/v2/context?libraryId=/fastapi/fastapi&query=dependency+injection&type=txt"
```
## Tips
- Use `type=txt` for more readable output
- Use `jq` to filter and format JSON responses
- Be specific with the `query` parameter to improve relevance ranking
- If the first search result is not correct, check additional results in the array
- URL-encode query parameters containing spaces (use `+` or `%20`)
- No API key is required for basic usage (rate-limited)

View File

@@ -0,0 +1,290 @@
# External Library Registry
## Purpose
This file lists external libraries/frameworks that should use **ExternalScout** (via Context7) for live documentation instead of relying on potentially outdated training data.
## When to Use This
**ContextScout** checks this list when:
1. User asks about a library/framework
2. No internal context exists in `.opencode/context/development/frameworks/`
3. Query matches a library name below
**Action**: Recommend **ExternalScout** subagent
---
## Supported Libraries
### Database & ORM
#### Drizzle ORM
- **Aliases**: `drizzle`, `drizzle-orm`, `drizzle orm`
- **Docs**: https://orm.drizzle.team/
- **Context7**: `use context7 for drizzle`
- **Common topics**: schema organization, migrations, relational queries, transactions, TypeScript types
#### Prisma
- **Aliases**: `prisma`
- **Docs**: https://www.prisma.io/docs
- **Context7**: `use context7 for prisma`
- **Common topics**: schema, migrations, client, relations, TypeScript
---
### Authentication
#### Better Auth
- **Aliases**: `better-auth`, `better auth`, `betterauth`
- **Docs**: https://www.better-auth.com/docs
- **Context7**: `use context7 for better-auth`
- **Common topics**: Next.js integration, Drizzle adapter, social providers, session management, 2FA
#### NextAuth.js
- **Aliases**: `nextauth`, `next-auth`, `nextauth.js`
- **Docs**: https://next-auth.js.org/
- **Context7**: `use context7 for nextauth`
- **Common topics**: providers, callbacks, sessions, JWT
#### Clerk
- **Aliases**: `clerk`
- **Docs**: https://clerk.com/docs
- **Context7**: `use context7 for clerk`
- **Common topics**: authentication, user management, organizations
---
### Frontend Frameworks
#### Next.js
- **Aliases**: `nextjs`, `next.js`, `next`
- **Docs**: https://nextjs.org/docs
- **Context7**: `use context7 for nextjs`
- **Common topics**: App Router, Server Actions, Server Components, routing, middleware, API routes
#### React
- **Aliases**: `react`, `reactjs`, `react.js`
- **Docs**: https://react.dev/
- **Context7**: `use context7 for react`
- **Common topics**: hooks, components, state, effects, context
#### TanStack Query
- **Aliases**: `tanstack query`, `react query`, `@tanstack/react-query`
- **Docs**: https://tanstack.com/query/latest
- **Context7**: `use context7 for tanstack query`
- **Common topics**: useQuery, useMutation, prefetching, caching, Server Components
#### TanStack Router
- **Aliases**: `tanstack router`, `@tanstack/react-router`
- **Docs**: https://tanstack.com/router/latest
- **Context7**: `use context7 for tanstack router`
- **Common topics**: routing, type-safe routes, loaders, navigation
#### TanStack Start
- **Aliases**: `tanstack start`, `@tanstack/start`
- **Docs**: https://tanstack.com/start/latest
- **Context7**: `use context7 for tanstack start`
- **Common topics**: full-stack setup, server functions, file routing
---
### Infrastructure & Deployment
#### Cloudflare Workers
- **Aliases**: `cloudflare workers`, `cloudflare`, `workers`, `cf workers`
- **Docs**: https://developers.cloudflare.com/workers
- **Context7**: `use context7 for cloudflare workers`
- **Common topics**: routing, KV storage, Durable Objects, bindings, middleware
#### AWS Lambda
- **Aliases**: `aws lambda`, `lambda`, `aws λ`
- **Docs**: https://docs.aws.amazon.com/lambda
- **Context7**: `use context7 for aws lambda`
- **Common topics**: handlers, layers, environment variables, triggers, TypeScript
#### Vercel
- **Aliases**: `vercel`
- **Docs**: https://vercel.com/docs
- **Context7**: `use context7 for vercel`
- **Common topics**: deployment, environment variables, edge functions, serverless
---
### UI Libraries & Styling
#### Shadcn/ui
- **Aliases**: `shadcn`, `shadcn/ui`, `shadcn-ui`
- **Docs**: https://ui.shadcn.com/
- **Context7**: `use context7 for shadcn`
- **Common topics**: components, installation, theming, customization
#### Radix UI
- **Aliases**: `radix`, `radix ui`, `radix-ui`, `@radix-ui`
- **Docs**: https://www.radix-ui.com/
- **Context7**: `use context7 for radix`
- **Common topics**: primitives, accessibility, composition
#### Tailwind CSS
- **Aliases**: `tailwind`, `tailwindcss`, `tailwind css`
- **Docs**: https://tailwindcss.com/docs
- **Context7**: `use context7 for tailwind`
- **Common topics**: configuration, utilities, responsive design, dark mode
---
### State Management
#### Zustand
- **Aliases**: `zustand`
- **Docs**: https://zustand-demo.pmnd.rs/
- **Context7**: `use context7 for zustand`
- **Common topics**: store creation, selectors, middleware, TypeScript
#### Jotai
- **Aliases**: `jotai`
- **Docs**: https://jotai.org/
- **Context7**: `use context7 for jotai`
- **Common topics**: atoms, async atoms, utilities
---
### Validation & Forms
#### Zod
- **Aliases**: `zod`
- **Docs**: https://zod.dev/
- **Context7**: `use context7 for zod`
- **Common topics**: schema validation, TypeScript inference, parsing, refinements
#### React Hook Form
- **Aliases**: `react hook form`, `react-hook-form`, `rhf`
- **Docs**: https://react-hook-form.com/
- **Context7**: `use context7 for react hook form`
- **Common topics**: register, validation, errors, TypeScript
---
### Testing
#### Vitest
- **Aliases**: `vitest`
- **Docs**: https://vitest.dev/
- **Context7**: `use context7 for vitest`
- **Common topics**: configuration, testing, mocking, coverage
#### Playwright
- **Aliases**: `playwright`
- **Docs**: https://playwright.dev/
- **Context7**: `use context7 for playwright`
- **Common topics**: browser automation, testing, selectors, assertions
---
## Detection Patterns
ContextScout and ExternalScout should match queries containing:
- Library name (case-insensitive)
- Common variations (e.g., "next.js" vs "nextjs")
- Package names (e.g., "@tanstack/react-query")
**Examples**:
- "How do I use **Drizzle** with PostgreSQL?" → Match: Drizzle ORM
- "Show me **Next.js** App Router setup" → Match: Next.js
- "**TanStack Query** with Server Components" → Match: TanStack Query
- "**Better Auth** integration" → Match: Better Auth
---
## Query Optimization Patterns
### Drizzle ORM
| User Intent | Optimized Query |
|-------------|-----------------|
| Setup/Installation | `PostgreSQL+setup+configuration+TypeScript+installation` |
| Modular schemas | `modular+schema+organization+domain+driven+design` |
| Relations | `relational+queries+one+to+many+joins+with+relations` |
| Migrations | `drizzle-kit+migrations+generate+push+PostgreSQL` |
| Transactions | `database+transactions+patterns+TypeScript` |
| Type safety | `TypeScript+type+inference+schema+types+inferInsert` |
### Better Auth
| User Intent | Optimized Query |
|-------------|-----------------|
| Setup | `setup+configuration+Next.js+TypeScript+installation` |
| Next.js integration | `Next.js+App+Router+integration+setup+configuration` |
| Drizzle adapter | `Drizzle+adapter+PostgreSQL+schema+generation+configuration` |
| Social providers | `social+providers+OAuth+GitHub+Google+setup` |
| Email/password | `email+password+authentication+signup+signin` |
| Session management | `session+management+cookies+JWT+middleware` |
### Next.js
| User Intent | Optimized Query |
|-------------|-----------------|
| App Router | `App+Router+file+conventions+layouts+pages+routing` |
| Server Actions | `Server+Actions+form+mutations+revalidation+TypeScript` |
| Server Components | `React+Server+Components+async+data+fetching+patterns` |
| Dynamic routes | `dynamic+routes+params+TypeScript+generateStaticParams` |
| Middleware | `middleware+authentication+redirects+headers+cookies` |
| API routes | `API+routes+route+handlers+TypeScript+POST+GET` |
### TanStack Query
| User Intent | Optimized Query |
|-------------|-----------------|
| Setup | `setup+QueryClient+provider+Next.js+TypeScript` |
| Data fetching | `useQuery+data+fetching+TypeScript+patterns+async` |
| Mutations | `useMutation+optimistic+updates+invalidation+TypeScript` |
| Prefetching | `prefetchQuery+Server+Components+hydration+Next.js` |
| Caching | `cache+configuration+staleTime+gcTime+invalidation` |
### Cloudflare Workers
| User Intent | Optimized Query |
|-------------|-----------------|
| Setup | `getting+started+setup+TypeScript+wrangler+configuration` |
| Routing | `routing+itty-router+hono+request+handling` |
| KV storage | `KV+storage+key+value+bindings+TypeScript` |
| Durable Objects | `Durable+Objects+state+WebSockets+coordination` |
### AWS Lambda
| User Intent | Optimized Query |
|-------------|-----------------|
| Setup | `getting+started+setup+TypeScript+handler+configuration` |
| Handlers | `handler+function+event+context+TypeScript+patterns` |
| Layers | `layers+dependencies+shared+code+deployment` |
| Environment variables | `environment+variables+secrets+configuration+SSM` |
---
## Adding New Libraries
To add a new library:
1. Add entry under appropriate category
2. Include: Name, aliases, docs link, Context7 command, common topics
3. (Optional) Add query optimization patterns
4. Update ExternalScout if needed (usually automatic)
**Template**:
```markdown
#### Library Name
- **Aliases**: `alias1`, `alias2`, `package-name`
- **Docs**: https://example.com/docs
- **Context7**: `use context7 for library-name`
- **Common topics**: topic1, topic2, topic3
```
---
## Usage by ExternalScout
ExternalScout uses this file to:
1. **Detect** which library the user is asking about
2. **Load** query optimization patterns for that library
3. **Build** optimized Context7 queries
4. **Fetch** live documentation
5. **Return** filtered, relevant results

View File

@@ -0,0 +1,51 @@
# Context7 Skill Navigation
**Purpose**: Live documentation fetching for external libraries via Context7 API
---
## Structure
```
context7/
├── navigation.md # This file
├── README.md # Quick start and workflow
├── SKILL.md # Context7 API documentation
└── library-registry.md # Supported libraries and query patterns
```
---
## Quick Routes
| Task | Path |
|------|------|
| **Quick start** | `README.md` |
| **API reference** | `SKILL.md` |
| **Supported libraries** | `library-registry.md` (lines 18-181) |
| **Query patterns** | `library-registry.md` (lines 199-261) |
| **Add new library** | `library-registry.md` (lines 264-279) |
| **ExternalScout integration** | `README.md` (lines 9-26) |
---
## By Purpose
**Using Context7**:
- Quick start → `README.md`
- API details → `SKILL.md`
**Adding Libraries**:
- Template → `library-registry.md` (lines 272-279)
- Supported list → `library-registry.md` (lines 18-181)
**Integration**:
- ContextScout workflow → `README.md` (lines 54-73)
- ExternalScout subagent → `.opencode/agent/subagents/core/externalscout.md`
---
## Related
- **ExternalScout**: `.opencode/agent/subagents/core/externalscout.md`
- **ContextScout**: `.opencode/agent/subagents/core/contextscout.md`

View File

@@ -0,0 +1,399 @@
---
name: task-management
description: Task management CLI for tracking and managing feature subtasks with status, dependencies, and validation
version: 1.0.0
author: opencode
type: skill
category: development
tags:
- tasks
- management
- tracking
- dependencies
- cli
---
# Task Management Skill
> **Purpose**: Track, manage, and validate feature implementations with atomic task breakdowns, dependency resolution, and progress monitoring.
---
## What I Do
I provide a command-line interface for managing task breakdowns created by the TaskManager subagent. I help you:
- **Track progress** - See status of all features and their subtasks
- **Find next tasks** - Show eligible tasks (dependencies satisfied)
- **Identify blocked tasks** - See what's blocked and why
- **Manage completion** - Mark subtasks as complete with summaries
- **Validate integrity** - Check JSON files and dependency trees
---
## How to Use Me
### Quick Start
```bash
# Show all task statuses
bash .opencode/skills/task-management/router.sh status
# Show next eligible tasks
bash .opencode/skills/task-management/router.sh next
# Show blocked tasks
bash .opencode/skills/task-management/router.sh blocked
# Mark a task complete
bash .opencode/skills/task-management/router.sh complete <feature> <seq> "summary"
# Validate all tasks
bash .opencode/skills/task-management/router.sh validate
```
### Command Reference
| Command | Description |
|---------|-------------|
| `status [feature]` | Show task status summary for all features or specific one |
| `next [feature]` | Show next eligible tasks (dependencies satisfied) |
| `parallel [feature]` | Show parallelizable tasks ready to run |
| `deps <feature> <seq>` | Show dependency tree for a specific subtask |
| `blocked [feature]` | Show blocked tasks and why |
| `complete <feature> <seq> "summary"` | Mark subtask complete with summary |
| `validate [feature]` | Validate JSON files and dependencies |
| `help` | Show help message |
---
## Examples
### Check Overall Progress
```bash
$ bash .opencode/skills/task-management/router.sh status
[my-feature] My Feature Implementation
Status: active | Progress: 45% (5/11)
Pending: 3 | In Progress: 2 | Completed: 5 | Blocked: 1
```
### Find What's Next
```bash
$ bash .opencode/skills/task-management/router.sh next
=== Ready Tasks (deps satisfied) ===
[my-feature]
06 - Implement API endpoint [sequential]
08 - Write unit tests [parallel]
```
### Mark Complete
```bash
$ bash .opencode/skills/task-management/router.sh complete my-feature 05 "Implemented authentication module"
✓ Marked my-feature/05 as completed
Summary: Implemented authentication module
Progress: 6/11
```
### Check Dependencies
```bash
$ bash .opencode/skills/task-management/router.sh deps my-feature 07
=== Dependency Tree: my-feature/07 ===
07 - Write integration tests [pending]
├── ✓ 05 - Implement authentication module [completed]
└── ○ 06 - Implement API endpoint [in_progress]
```
### Validate Everything
```bash
$ bash .opencode/skills/task-management/router.sh validate
=== Validation Results ===
[my-feature]
✓ All checks passed
```
---
## Architecture
```
.opencode/skills/task-management/
├── SKILL.md # This file
├── router.sh # CLI router (entry point)
└── scripts/
└── task-cli.ts # Task management CLI implementation
```
---
## Task File Structure
Tasks are stored in `.tmp/tasks/` at the project root:
```
.tmp/tasks/
├── {feature-slug}/
│ ├── task.json # Feature-level metadata
│ ├── subtask_01.json # Subtask definitions
│ ├── subtask_02.json
│ └── ...
└── completed/
└── {feature-slug}/ # Completed tasks
```
### task.json Schema
```json
{
"id": "my-feature",
"name": "My Feature",
"status": "active",
"objective": "Implement X",
"context_files": ["docs/spec.md"],
"reference_files": ["src/existing.ts"],
"exit_criteria": ["Tests pass", "Code reviewed"],
"subtask_count": 5,
"completed_count": 2,
"created_at": "2026-01-11T10:00:00Z",
"completed_at": null
}
```
### subtask_##.json Schema
```json
{
"id": "my-feature-05",
"seq": "05",
"title": "Implement authentication",
"status": "pending",
"depends_on": ["03", "04"],
"parallel": false,
"suggested_agent": "coder-agent",
"context_files": ["docs/auth.md"],
"reference_files": ["src/auth-old.ts"],
"acceptance_criteria": ["Login works", "JWT tokens valid"],
"deliverables": ["auth.ts", "auth.test.ts"],
"started_at": null,
"completed_at": null,
"completion_summary": null
}
```
---
## Integration with TaskManager
The TaskManager subagent creates task files using this format. When you delegate to TaskManager:
```javascript
task(
subagent_type="TaskManager",
description="Implement feature X",
prompt="Break down this feature into atomic subtasks..."
)
```
TaskManager creates:
1. `.tmp/tasks/{feature}/task.json` - Feature metadata
2. `.tmp/tasks/{feature}/subtask_XX.json` - Individual subtasks
You can then use this skill to track and manage progress.
---
## Key Concepts
### 1. Dependency Resolution
Subtasks can depend on other subtasks. A task is "ready" only when all its dependencies are complete.
### 2. Parallel Execution
Set `parallel: true` to indicate a subtask can run alongside other parallel tasks with satisfied dependencies.
### 3. Status Tracking
- **pending** - Not started, waiting for dependencies
- **in_progress** - Currently being worked on
- **completed** - Finished with summary
- **blocked** - Explicitly blocked (not waiting for deps)
### 4. Exit Criteria
Each feature has exit_criteria that must be met before marking the feature complete.
### 5. Validation Rules
The `validate` command performs comprehensive checks on task files:
**Task-Level Validation:**
- ✅ task.json file exists for the feature
- ✅ Task ID matches feature slug
- ✅ Subtask count in task.json matches actual subtask files
- ✅ All required fields are present
**Subtask-Level Validation:**
- ✅ All subtask IDs start with feature name (e.g., "my-feature-01")
- ✅ Sequence numbers are unique and properly formatted (01, 02, etc.)
- ✅ All dependencies reference existing subtasks
- ✅ No circular dependencies exist
- ✅ Each subtask has acceptance criteria defined
- ✅ Each subtask has deliverables specified
- ✅ Status values are valid (pending, in_progress, completed, blocked)
**Dependency Validation:**
- ✅ All depends_on references point to existing subtasks
- ✅ No task depends on itself
- ✅ No circular dependency chains
- ✅ Dependency graph is acyclic
Run `validate` regularly to catch issues early:
```bash
bash .opencode/skills/task-management/router.sh validate my-feature
```
### 6. Context and Reference Files
- **context_files** - Standards, conventions, and guidelines to follow
- **reference_files** - Existing project files to look at or build upon
---
## Workflow Integration
### With TaskManager Subagent
1. **TaskManager creates tasks** → Generates `.tmp/tasks/{feature}/` structure
2. **You use this skill to track** → Monitor progress with `status`, `next`, `blocked`
3. **You mark tasks complete** → Use `complete` command with summaries
4. **Skill validates integrity** → Use `validate` to check consistency
### With Other Subagents
Working agents (CoderAgent, TestEngineer, etc.) execute subtasks and report completion. Use this skill to:
- Find next available tasks with `next`
- Check what's blocking progress with `blocked`
- Validate task definitions with `validate`
---
## Common Workflows
### Starting a New Feature
```bash
# 1. TaskManager creates the task structure
task(subagent_type="TaskManager", description="Implement feature X", ...)
# 2. Check what's ready
bash .opencode/skills/task-management/router.sh next
# 3. Delegate first task to working agent
task(subagent_type="CoderAgent", description="Implement subtask 01", ...)
```
### Tracking Progress
```bash
# Check overall status
bash .opencode/skills/task-management/router.sh status my-feature
# See what's next
bash .opencode/skills/task-management/router.sh next my-feature
# Check what's blocked
bash .opencode/skills/task-management/router.sh blocked my-feature
```
### Completing Tasks
```bash
# After working agent finishes
bash .opencode/skills/task-management/router.sh complete my-feature 05 "Implemented auth module with JWT support"
# Check progress
bash .opencode/skills/task-management/router.sh status my-feature
# Find next task
bash .opencode/skills/task-management/router.sh next my-feature
```
### Validating Everything
```bash
# Validate all tasks
bash .opencode/skills/task-management/router.sh validate
# Validate specific feature
bash .opencode/skills/task-management/router.sh validate my-feature
```
---
## Tips & Best Practices
### 1. Use Meaningful Summaries
When marking tasks complete, provide clear summaries:
```bash
# Good
complete my-feature 05 "Implemented JWT authentication with refresh tokens and error handling"
# Avoid
complete my-feature 05 "Done"
```
### 2. Check Dependencies Before Starting
```bash
# See what a task depends on
bash .opencode/skills/task-management/router.sh deps my-feature 07
```
### 3. Identify Parallelizable Work
```bash
# Find tasks that can run in parallel
bash .opencode/skills/task-management/router.sh parallel my-feature
```
### 4. Regular Validation
```bash
# Validate regularly to catch issues early
bash .opencode/skills/task-management/router.sh validate
```
---
## Troubleshooting
### "task-cli.ts not found"
Make sure you're running from the project root or the router.sh can find it.
### "No tasks found"
Run `status` to see if any tasks have been created yet. Use TaskManager to create tasks first.
### "Dependency not satisfied"
Check the dependency tree with `deps` to see what's blocking the task.
### "Validation failed"
Run `validate` to see specific issues, then check the JSON files in `.tmp/tasks/`.
---
## File Locations
- **Skill**: `.opencode/skills/task-management/`
- **Router**: `.opencode/skills/task-management/router.sh`
- **CLI**: `.opencode/skills/task-management/scripts/task-cli.ts`
- **Tasks**: `.tmp/tasks/` (created by TaskManager)
- **Documentation**: `.opencode/skills/task-management/SKILL.md` (this file)
---
**Task Management Skill** - Track, manage, and validate your feature implementations!

View File

@@ -0,0 +1,108 @@
#!/usr/bin/env bash
#############################################################################
# Task Management Skill Router
# Routes to task-cli.ts with proper path resolution and command handling
#############################################################################
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLI_SCRIPT="$SCRIPT_DIR/scripts/task-cli.ts"
MIGRATE_SCRIPT="$SCRIPT_DIR/scripts/migrate-schema.ts"
# Show help
show_help() {
cat << 'HELP'
📋 Task Management Skill
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Usage: router.sh [COMMAND] [OPTIONS]
COMMANDS:
status [feature] Show task status summary
next [feature] Show next eligible tasks
parallel [feature] Show parallelizable tasks
deps <feature> <seq> Show dependency tree
blocked [feature] Show blocked tasks
complete <feature> <seq> "msg" Mark subtask complete
validate [feature] Validate JSON files
context <feature> Show bounded context breakdown
contracts <feature> Show contract dependencies
migrate <feature> [options] Migrate to enhanced schema
help Show this help message
MIGRATION OPTIONS:
--dry-run Preview migration changes
--lines-only Add only line-number precision
EXAMPLES:
./router.sh status
./router.sh status my-feature
./router.sh next
./router.sh deps my-feature 05
./router.sh complete my-feature 05 "Implemented auth module"
./router.sh validate
./router.sh context my-feature
./router.sh contracts my-feature
./router.sh migrate my-feature
./router.sh migrate my-feature --dry-run
FEATURES:
✓ Track progress across all features
✓ Find next eligible tasks (dependencies satisfied)
✓ Identify blocked tasks
✓ Mark subtasks complete with summaries
✓ Validate task integrity
✓ Show bounded context breakdown
✓ Show contract dependencies
✓ Migrate to enhanced schema
For more info, see: .opencode/skills/task-management/SKILL.md
HELP
}
# Check if CLI script exists
if [ ! -f "$CLI_SCRIPT" ]; then
echo "❌ Error: task-cli.ts not found at $CLI_SCRIPT"
exit 1
fi
# Find project root
find_project_root() {
local dir
dir="$(pwd)"
while [ "$dir" != "/" ]; do
if [ -d "$dir/.git" ] || [ -f "$dir/package.json" ]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
pwd
return 1
}
# Handle help
if [ "$1" = "help" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
show_help
exit 0
fi
# If no arguments, show help
if [ $# -eq 0 ]; then
show_help
exit 0
fi
PROJECT_ROOT="$(find_project_root)"
# Route commands
case "$1" in
migrate)
cd "$PROJECT_ROOT" && bunx --bun ts-node "$MIGRATE_SCRIPT" "$@"
;;
*)
# Run the task CLI with all arguments
cd "$PROJECT_ROOT" && bunx --bun ts-node "$CLI_SCRIPT" "$@"
;;
esac

View File

@@ -0,0 +1,553 @@
#!/usr/bin/env bunx --bun ts-node
/**
* Task Management CLI
*
* Usage: bunx --bun ts-node task-cli.ts <command> [feature] [args...]
*
* Commands:
* status [feature] - Show task status summary
* next [feature] - Show next eligible tasks
* parallel [feature] - Show parallelizable tasks ready to run
* deps <feature> <seq> - Show dependency tree for a task
* blocked [feature] - Show blocked tasks and why
* complete <feature> <seq> "summary" - Mark task completed
* validate [feature] - Validate JSON files and dependencies
*
* Task files are stored in .tmp/tasks/ at the project root:
* .tmp/tasks/{feature-slug}/task.json
* .tmp/tasks/{feature-slug}/subtask_01.json
* .tmp/tasks/completed/{feature-slug}/
*/
const fs = require('fs');
const path = require('path');
// Find project root (look for .git or package.json)
function findProjectRoot(): string {
let dir = process.cwd();
while (dir !== path.dirname(dir)) {
if (fs.existsSync(path.join(dir, '.git')) || fs.existsSync(path.join(dir, 'package.json'))) {
return dir;
}
dir = path.dirname(dir);
}
return process.cwd();
}
const PROJECT_ROOT = findProjectRoot();
const TASKS_DIR = path.join(PROJECT_ROOT, '.tmp', 'tasks');
const COMPLETED_DIR = path.join(TASKS_DIR, 'completed');
interface Task {
id: string;
name: string;
status: 'active' | 'completed' | 'blocked' | 'archived';
objective: string;
context_files: string[];
reference_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[];
reference_files?: string[];
acceptance_criteria: string[];
deliverables: string[];
agent_id: string | null;
suggested_agent?: string;
started_at: string | null;
completed_at: string | null;
completion_summary: string | null;
}
// Helpers
function getFeatureDirs(): string[] {
if (!fs.existsSync(TASKS_DIR)) return [];
return fs.readdirSync(TASKS_DIR).filter((f: string) => {
const fullPath = path.join(TASKS_DIR, f);
return fs.statSync(fullPath).isDirectory() && f !== 'completed';
});
}
function loadTask(feature: string): Task | null {
const taskPath = path.join(TASKS_DIR, feature, 'task.json');
if (!fs.existsSync(taskPath)) return null;
return JSON.parse(fs.readFileSync(taskPath, 'utf-8'));
}
function loadSubtasks(feature: string): Subtask[] {
const featureDir = path.join(TASKS_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')));
}
function saveSubtask(feature: string, subtask: Subtask): void {
const subtaskPath = path.join(TASKS_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(TASKS_DIR, feature, 'task.json');
fs.writeFileSync(taskPath, JSON.stringify(task, null, 2));
}
// Commands
function cmdStatus(feature?: string): void {
const features = feature ? [feature] : getFeatureDirs();
if (features.length === 0) {
console.log('No active features found.');
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 => s.status === 'pending').length,
in_progress: subtasks.filter(s => s.status === 'in_progress').length,
completed: subtasks.filter(s => s.status === 'completed').length,
blocked: subtasks.filter(s => 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 => s.status === 'completed').map(s => s.seq));
const ready = subtasks.filter(s => {
if (s.status !== 'pending') return false;
return s.depends_on.every(dep => 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=== Parallelizable Tasks Ready Now ===\n');
for (const f of features) {
const subtasks = loadSubtasks(f);
const completedSeqs = new Set(subtasks.filter(s => s.status === 'completed').map(s => s.seq));
const parallel = subtasks.filter(s => {
if (s.status !== 'pending') return false;
if (!s.parallel) return false;
return s.depends_on.every(dep => 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 => 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 => 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 => s.status === 'completed').map(s => s.seq));
const blocked = subtasks.filter(s => {
if (s.status === 'blocked') return true;
if (s.status !== 'pending') return false;
return !s.depends_on.every(dep => completedSeqs.has(dep));
});
if (blocked.length > 0) {
console.log(`[${f}]`);
for (const s of blocked) {
const waitingFor = s.depends_on.filter(dep => !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 => 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 => 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;
const validTaskStatuses = new Set(['active', 'completed', 'blocked', 'archived']);
const validSubtaskStatuses = new Set(['pending', 'in_progress', 'completed', 'blocked']);
const requiredTaskFields = [
'id',
'name',
'status',
'objective',
'context_files',
'exit_criteria',
'subtask_count',
'completed_count',
'created_at',
'completed_at',
];
const requiredSubtaskFields = [
'id',
'seq',
'title',
'status',
'depends_on',
'parallel',
'context_files',
'acceptance_criteria',
'deliverables',
'agent_id',
'started_at',
'completed_at',
'completion_summary',
];
const hasField = (obj: any, field: string): boolean => Object.prototype.hasOwnProperty.call(obj, field);
const isStringArray = (value: any): boolean => Array.isArray(value) && value.every(v => typeof v === 'string');
console.log('\n=== Validation Results ===\n');
for (const f of features) {
const errors: 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 seqCounts = new Map<string, number>();
for (const s of subtasks) {
const seq = typeof s.seq === 'string' ? s.seq : '';
seqCounts.set(seq, (seqCounts.get(seq) || 0) + 1);
}
const seqs = new Set(subtasks.map(s => s.seq));
if (task) {
// Required fields in task.json
for (const field of requiredTaskFields) {
if (!hasField(task, field)) {
errors.push(`task.json: missing required field '${field}'`);
}
}
// Task ID should match feature slug
if (task.id !== f) {
errors.push(`task.json id ('${task.id}') should match feature slug ('${f}')`);
}
// Task status should be valid
if (!validTaskStatuses.has(task.status)) {
errors.push(`task.json: invalid status '${task.status}'`);
}
// Basic type checks for key task fields
if (!isStringArray(task.context_files)) {
errors.push('task.json: context_files must be string[]');
}
if (hasField(task, 'reference_files') && task.reference_files !== undefined && !isStringArray(task.reference_files)) {
errors.push('task.json: reference_files must be string[] when present');
}
if (!isStringArray(task.exit_criteria)) {
errors.push('task.json: exit_criteria must be string[]');
}
if (typeof task.subtask_count !== 'number') {
errors.push('task.json: subtask_count must be number');
}
if (typeof task.completed_count !== 'number') {
errors.push('task.json: completed_count must be number');
}
}
for (const s of subtasks) {
// Required fields in subtask files
for (const field of requiredSubtaskFields) {
if (!hasField(s, field)) {
errors.push(`${s.seq || '??'}: missing required field '${field}'`);
}
}
// Sequence format and uniqueness
if (!/^\d{2}$/.test(s.seq)) {
errors.push(`${s.seq}: sequence must be 2 digits (e.g., 01, 02)`);
}
if ((seqCounts.get(s.seq) || 0) > 1) {
errors.push(`${s.seq}: duplicate sequence number`);
}
// Check ID format
if (!s.id.startsWith(f)) {
errors.push(`${s.seq}: ID should start with feature name`);
}
// Status should be valid
if (!validSubtaskStatuses.has(s.status)) {
errors.push(`${s.seq}: invalid status '${s.status}'`);
}
// Type checks
if (!isStringArray(s.depends_on)) {
errors.push(`${s.seq}: depends_on must be string[]`);
}
if (typeof s.parallel !== 'boolean') {
errors.push(`${s.seq}: parallel must be boolean`);
}
if (!isStringArray(s.context_files)) {
errors.push(`${s.seq}: context_files must be string[]`);
}
if (hasField(s, 'reference_files') && s.reference_files !== undefined && !isStringArray(s.reference_files)) {
errors.push(`${s.seq}: reference_files must be string[] when present`);
}
if (!isStringArray(s.acceptance_criteria)) {
errors.push(`${s.seq}: acceptance_criteria must be string[]`);
} else if (s.acceptance_criteria.length === 0) {
errors.push(`${s.seq}: No acceptance criteria defined`);
}
if (!isStringArray(s.deliverables)) {
errors.push(`${s.seq}: deliverables must be string[]`);
} else if (s.deliverables.length === 0) {
errors.push(`${s.seq}: No deliverables defined`);
}
// Self dependency is invalid
if (Array.isArray(s.depends_on) && s.depends_on.includes(s.seq)) {
errors.push(`${s.seq}: task cannot depend on itself`);
}
// Check for missing dependencies
for (const dep of (Array.isArray(s.depends_on) ? 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<string>();
const checkCircular = (seq: string, path: string[]): boolean => {
if (path.includes(seq)) {
errors.push(`${s.seq}: circular dependency detected: ${[...path, seq].join(' -> ')}`);
return true;
}
if (visited.has(seq)) return false;
visited.add(seq);
const task = subtasks.find(t => t.seq === seq);
if (task) {
for (const dep of task.depends_on) {
if (checkCircular(dep, [...path, seq])) return true;
}
}
return false;
};
checkCircular(s.seq, []);
}
// 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) {
console.log(' ✓ All checks passed');
} else {
for (const e of errors) {
console.log(` ✗ ERROR: ${e}`);
hasErrors = true;
}
}
console.log();
}
process.exit(hasErrors ? 1 : 0);
}
// Main
const [,, command, ...args] = process.argv;
switch (command) {
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 <feature> <seq>');
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 <feature> <seq> "summary"');
process.exit(1);
}
cmdComplete(args[0], args[1], args.slice(2).join(' '));
break;
case 'validate':
cmdValidate(args[0]);
break;
default:
console.log(`
Task Management CLI
Usage: bunx --bun ts-node task-cli.ts <command> [feature] [args...]
Task files are stored in: .tmp/tasks/{feature-slug}/
Commands:
status [feature] Show task status summary
next [feature] Show next eligible tasks (deps satisfied)
parallel [feature] Show parallelizable tasks ready to run
deps <feature> <seq> Show dependency tree for a task
blocked [feature] Show blocked tasks and why
complete <feature> <seq> "summary" Mark task completed with summary
validate [feature] Validate JSON files and dependencies
Examples:
bunx --bun ts-node task-cli.ts status
bunx --bun ts-node task-cli.ts next my-feature
bunx --bun ts-node task-cli.ts complete my-feature 02 "Implemented auth module"
`);
}