Signed-off-by: Max <ai-agent@topdoglabs.com>

This commit is contained in:
Max 2026-02-24 17:53:20 -06:00
parent 7687dfd8a6
commit 8a828e72c8
2 changed files with 213 additions and 301 deletions

View File

@ -0,0 +1,125 @@
# API-Centric CLI Pattern (Skill Blueprint)
## Goal
Define one final implementation path for product logic:
- API = single source of truth for business logic
- CLI = passthrough client to API only
- Tests = mandatory, with mocks, for both API behavior and CLI contracts
Use this pattern when building or refactoring any web app that exposes both a UI and CLI.
## Non-Negotiable Rules
1. All business logic lives in API/server code.
2. CLI scripts must not implement decision logic, validation rules, or data model behavior.
3. CLI scripts only:
- parse args
- resolve user-friendly identifiers (if needed)
- call API endpoints
- format output/errors
4. If CLI needs behavior that API does not expose, add it to API first.
5. No direct DB calls from CLI.
6. Every change requires tests.
7. Every CLI test suite must run against a mocked API transport.
## Architecture Contract
## Source of truth
- Domain rules: API
- State transitions: API
- Date/sprint logic: API/shared server module
- Persistence: API + data layer
- Auth/session policy: API
## Client responsibilities
- Web UI: call API, render state
- CLI: call API, render terminal output
- Both are clients of the same backend contract
## Refactor Pattern (DB-calling CLI -> API passthrough CLI)
1. Inventory CLI commands and all direct DB access.
2. Create/confirm matching API endpoints.
3. Move duplicated logic into API modules.
4. Rewrite CLI commands to call API endpoints only.
5. Keep one shared API client helper in CLI (`api_call`).
6. Remove DB env vars/credentials from CLI.
7. Add regression tests before removing old paths.
8. Delete dead logic after tests pass.
## Testing Standard
## Required layers
1. API unit tests
- domain logic (date windows, status transitions, validation, selectors)
2. API route/integration tests
- request/response shape, status codes, error handling
3. CLI contract tests with mocked API transport
- assert commands hit correct endpoints/methods/payload paths
- assert cookie/session handling
- assert no direct DB references remain
## Mock requirements for CLI tests
- Replace network transport (for shell CLIs, mock `curl` in `PATH`).
- Log request triples: `METHOD URL PAYLOAD`.
- Return deterministic fixtures per endpoint.
- Fail test on unhandled endpoint calls.
- Assert expected calls occurred.
## Definition of done for a refactor
- All CLI commands route through API.
- No direct DB references in CLI codebase.
- Shared business logic exists in API/server modules only.
- Tests pass locally and in CI.
- API contract changes are documented.
## Anti-Patterns to Block
- CLI reads DB tables directly "for convenience"
- CLI re-implements sprint/task/business status logic
- API and CLI each have separate versions of the same selector/validator
- "Temporary" bypasses that never get removed
## Recommended Repository Layout
```text
src/
app/api/...
lib/server/domain/...
scripts/
lib/api_client.sh
task.sh
project.sh
sprint.sh
tests/
refactor-cli-api.sh
*.test.ts
```
## PR Checklist (copy/paste)
- [ ] No business logic added to CLI.
- [ ] Needed behavior implemented in API first.
- [ ] CLI commands call API endpoints only.
- [ ] Direct DB access removed from CLI.
- [ ] Unit tests added/updated for moved logic.
- [ ] CLI mock-contract tests added/updated.
- [ ] Unhandled endpoint calls fail tests.
- [ ] CI includes the refactor test suite.
## Skill Conversion Notes
If converting this into a reusable skill:
1. Skill name suggestion: `api-cli-passthrough`.
2. Put these rules in `SKILL.md`.
3. Include reusable test harness scripts under `scripts/`.
4. Include endpoint/contract references under `references/`.
5. Trigger when user asks to build/refactor web API + CLI with no logic duplication.

View File

@ -1,142 +1,72 @@
# Gantt Board CLI
# Gantt Board CLI (API Passthrough)
A comprehensive command-line interface for managing tasks, projects, and sprints in the Gantt Board system.
Shell CLI for Gantt Board task/project/sprint/auth workflows.
The CLI is intentionally thin:
- API is the only source of business logic.
- CLI parses args, resolves names to IDs, calls API endpoints, and formats output.
- CLI must never call the database directly.
## Scripts
- `task.sh` - Full CRUD operations for tasks
- `project.sh` - Project management
- `sprint.sh` - Sprint management
- `gantt.sh`: full API wrapper (task, project, sprint, auth, debug)
- `task.sh`: task-focused CRUD helpers
- `project.sh`: project-focused CRUD helpers
- `sprint.sh`: sprint-focused CRUD helpers
- `lib/api_client.sh`: shared HTTP client (`api_call`, cookie handling, URL encoding)
## Installation
## Requirements
1. Make scripts executable:
```bash
chmod +x task.sh project.sh sprint.sh
```
- `bash`
- `curl`
- `jq`
- `uuidgen` (used by `task.sh create`)
Install `jq` on macOS:
2. Ensure dependencies are installed:
```bash
brew install jq
```
## Configuration
Scripts call the Gantt Board API and use these defaults:
- **API URL**: `http://localhost:3000/api` (override with `API_URL`)
- **Session cookie file**: `~/.config/gantt-board/cookies.txt` (override with `GANTT_COOKIE_FILE`)
- **Default Project/Assignee**: optional via `DEFAULT_PROJECT_ID` / `DEFAULT_ASSIGNEE_ID`
Environment variables:
- `API_URL` (default: `http://localhost:3000/api`)
- `GANTT_COOKIE_FILE` (default: `~/.config/gantt-board/cookies.txt`)
- `DEFAULT_PROJECT_ID` (optional task default)
- `DEFAULT_ASSIGNEE_ID` (optional task default)
Authenticate once:
Authenticate once before using data commands:
```bash
./gantt.sh auth login <email> <password>
```
## Task Management (`task.sh`)
## Command Reference
### Create Task
```bash
# Minimal task
./task.sh create --title "Fix bug"
# Full task with all fields
./task.sh create \
--title "Implement OAuth" \
--description "Add OAuth2 login support" \
--type task \
--status todo \
--priority high \
--project "Gantt Board" \
--sprint "current" \
--assignee "Max" \
--due-date "2026-03-01" \
--tags "auth,security" \
--comments "Starting implementation"
# Create from JSON file
./task.sh create --file task.json
# Interactive mode
./task.sh create --interactive
# Auto-create project if not found
./task.sh create --title "New Feature" --project "New Project" --auto-create
```
### List Tasks
```bash
# List all tasks
./task.sh list
# Filter by status
./task.sh list --status todo
# Filter by priority
./task.sh list --priority high
# Filter by project (auto-resolves name)
./task.sh list --project "Gantt Board"
# Filter by assignee
./task.sh list --assignee "Max"
# Filter by type
./task.sh list --type bug
# JSON output
./task.sh list --json
# Limit results
./task.sh list --limit 10
```
### Get Task
## `task.sh`
```bash
./task.sh list [status] [--status <v>] [--priority <v>] [--project <name-or-id>] [--assignee <name-or-id>] [--type <v>] [--limit <n>] [--json]
./task.sh get <task-id>
```
### Update Task
```bash
# Update status
./task.sh update <task-id> --status in-progress
# Update multiple fields
./task.sh update <task-id> \
--status done \
--priority low \
--add-comment "Task completed"
# Update assignee
./task.sh update <task-id> --assignee "Matt"
# Clear tags
./task.sh update <task-id> --clear-tags
# Update tags
./task.sh update <task-id> --tags "new-tag,another-tag"
```
### Delete Task
```bash
./task.sh current-sprint [--project <name-or-id>]
./task.sh create --title "<title>" [--description "..."] [--type <task|bug|research|plan|idea>] [--status <status>] [--priority <priority>] [--project <name-or-id>] [--sprint <name-or-id|current>] [--assignee <name-or-id>] [--due-date YYYY-MM-DD] [--tags "a,b"] [--comments "..."]
./task.sh create --file <task.json>
./task.sh update <task-id> [--status <v>] [--priority <v>] [--title "..."] [--description "..."] [--type <v>] [--project <name-or-id>] [--assignee <name-or-id>] [--sprint <name-or-id|current>] [--due-date YYYY-MM-DD] [--add-comment "..."] [--clear-tags] [--tags "a,b"]
./task.sh update <task-id> <field> <value> # legacy form
./task.sh delete <task-id>
./task.sh bulk-create <tasks.json>
```
### Bulk Create
Notes:
```bash
# From JSON file
./task.sh bulk-create tasks.json
- `--interactive` is not supported in passthrough mode.
- `--auto-create` is not supported in passthrough mode.
# With auto-create for projects
./task.sh bulk-create tasks.json --auto-create
```
Bulk JSON format:
**JSON Format:**
```json
[
{
@ -155,224 +85,81 @@ Authenticate once before using data commands:
]
```
## Project Management (`project.sh`)
### Create Project
```bash
./project.sh create --name "New Project" --description "Project description" --color "#3b82f6"
```
### List Projects
```bash
# List all projects
./project.sh list
# JSON output
./project.sh list --json
```
### Get Project
```bash
# By ID
./project.sh get <project-id>
# By name (auto-resolves)
./project.sh get "Gantt Board"
```
### Update Project
```bash
./project.sh update <project-id-or-name> \
--name "Updated Name" \
--description "Updated description" \
--color "#ff0000"
```
### Delete Project
## `project.sh`
```bash
./project.sh list [--json]
./project.sh get <project-id-or-name>
./project.sh create --name "<name>" [--description "..."] [--color "#hex"]
./project.sh update <project-id-or-name> [--name "..."] [--description "..."] [--color "#hex"]
./project.sh delete <project-id-or-name>
```
## Sprint Management (`sprint.sh`)
### Create Sprint
```bash
./sprint.sh create \
--name "Sprint 3" \
--project "Gantt Board" \
--goal "Complete API integration" \
--start-date "2026-02-24" \
--end-date "2026-03-07"
```
### List Sprints
```bash
# List all sprints
./sprint.sh list
# List active sprints
./sprint.sh list --active
# Filter by project
./sprint.sh list --project "Gantt Board"
# JSON output
./sprint.sh list --json
```
### Get Sprint
```bash
# By ID
./sprint.sh get <sprint-id>
# By name
./sprint.sh get "Sprint 1"
```
### Update Sprint
```bash
./sprint.sh update <sprint-id-or-name> \
--name "Updated Sprint" \
--goal "Updated goal" \
--status active \
--start-date "2026-02-25" \
--end-date "2026-03-10"
```
### Close Sprint
## `sprint.sh`
```bash
./sprint.sh list [--status <planning|active|completed>] [--project <name-or-id>] [--active] [--json]
./sprint.sh get <sprint-id-or-name>
./sprint.sh create --name "<name>" --project <name-or-id> [--goal "..."] [--start-date YYYY-MM-DD] [--end-date YYYY-MM-DD] [--status <planning|active|completed>]
./sprint.sh update <sprint-id-or-name> [--name "..."] [--goal "..."] [--start-date YYYY-MM-DD] [--end-date YYYY-MM-DD] [--status <planning|active|completed>] [--project <name-or-id>]
./sprint.sh close <sprint-id-or-name>
./sprint.sh delete <sprint-id-or-name>
```
### Delete Sprint
## `gantt.sh`
High-level wrapper for full API surface:
- `task`: list/get/create/natural/update/delete/comment/attach
- `project`: list/get/create/update/delete
- `sprint`: list/get/create/update/close/delete
- `auth`: login/logout/session/register/forgot-password/reset-password/account/users
- `debug`
Examples:
```bash
./sprint.sh delete <sprint-id-or-name>
./gantt.sh task list open
./gantt.sh task natural "Fix login bug by Friday, high priority"
./gantt.sh project create "Mobile App" "iOS and Android app" "#3b82f6"
./gantt.sh sprint close <sprint-id>
./gantt.sh auth session
```
## Name Resolution
All scripts support automatic name-to-ID resolution:
The task/project/sprint scripts support name-to-ID resolution against API data:
- **Projects**: "Gantt Board" → `68d05855-a399-44a4-9c36-3ee78257c2c9`
- **Sprints**: "Sprint 1" → `b2c3d4e5-0001-0000-0000-000000000001`
- **Assignees**:
- "Max" → `9c29cc99-81a1-4e75-8dff-cd7cc5ceb5aa`
- "Matt" → `0a3e400c-3932-48ae-9b65-f3f9c6f26fe9`
- **Special**: Use "current" for the most recent active sprint
- project names -> `projectId`
- sprint names -> `sprintId`
- assignee names/emails -> `assigneeId`
- sprint value `current` -> `/api/sprints/current` (optionally scoped by project)
## Task Types
## Testing
- `task` - Standard task
- `bug` - Bug fix
- `research` - Research spike
- `plan` - Planning task
- `idea` - Idea/backlog item
## Task Statuses
- `open` - Newly created
- `todo` - Ready to start
- `blocked` - Blocked
- `in-progress` - Currently working
- `review` - Ready for review
- `validate` - Needs validation
- `done` - Completed
## Priorities
- `low`
- `medium`
- `high`
- `urgent`
## Examples
### Daily Workflow
Run refactor safety suite:
```bash
# Create a new task
./task.sh create --title "Fix login bug" --type bug --priority urgent --project "Gantt Board"
# List my high priority tasks
./task.sh list --assignee "Max" --priority high
# Update task status
./task.sh update <task-id> --status in-progress
# Add progress comment
./task.sh update <task-id> --add-comment "Working on reproduction steps"
# Mark as done
./task.sh update <task-id> --status done --add-comment "Fixed in commit abc123"
npm run test:refactor
```
### Sprint Planning
This runs:
```bash
# Create new sprint
./sprint.sh create --name "Sprint 5" --project "Gantt Board" --start-date 2026-03-01 --end-date 2026-03-14
1. TypeScript unit tests for sprint-selection logic.
2. Shell contract tests that mock API transport and validate CLI passthrough behavior.
# Create multiple tasks for the sprint
./task.sh bulk-create sprint-tasks.json
The shell contract test also fails if direct Supabase/DB references appear in `scripts/`.
# Close previous sprint
./sprint.sh close "Sprint 4"
```
## AI/Agent Guidance
### Project Setup
If you are extending CLI behavior:
```bash
# Create new project
./project.sh create --name "Mobile App" --description "iOS and Android app" --color "#8b5cf6"
# Create initial sprint
./sprint.sh create --name "Sprint 1" --project "Mobile App" --goal "MVP release"
# Create starter tasks
./task.sh create --title "Setup repo" --project "Mobile App" --sprint "Sprint 1"
./task.sh create --title "Design system" --project "Mobile App" --sprint "Sprint 1"
```
1. Add/modify API behavior first (`src/app/api/*` and server modules).
2. Keep CLI as passthrough only.
3. Add or update tests (unit + mocked CLI contract tests).
4. Do not introduce direct database calls in `scripts/`.
## Exit Codes
- `0` - Success
- `1` - Error (invalid input, API failure, not found)
## Environment Variables
Scripts are API-first and read runtime configuration from environment variables:
- `API_URL` - API base URL (default: `http://localhost:3000/api`)
- `GANTT_COOKIE_FILE` - Session cookie jar path (default: `~/.config/gantt-board/cookies.txt`)
- `DEFAULT_PROJECT_ID` - Optional default project for task creation
- `DEFAULT_ASSIGNEE_ID` - Optional default assignee for task creation
## Troubleshooting
### "Project not found"
- Use `--auto-create` flag to create project automatically
- Check project name spelling
- Use `./project.sh list` to see available projects
### "Sprint not found"
- Use "current" to reference the most recent active sprint
- Check sprint name with `./sprint.sh list`
### "jq parse error"
- Ensure `jq` is installed: `brew install jq`
- Check JSON file syntax
## License
Part of the OpenClaw Gantt Board project.
- `0`: success
- `1`: error (invalid input, missing entity, API failure)