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 ## Scripts
- `task.sh` - Full CRUD operations for tasks - `gantt.sh`: full API wrapper (task, project, sprint, auth, debug)
- `project.sh` - Project management - `task.sh`: task-focused CRUD helpers
- `sprint.sh` - Sprint management - `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`
```bash - `curl`
chmod +x task.sh project.sh sprint.sh - `jq`
``` - `uuidgen` (used by `task.sh create`)
Install `jq` on macOS:
2. Ensure dependencies are installed:
```bash ```bash
brew install jq brew install jq
``` ```
## Configuration ## Configuration
Scripts call the Gantt Board API and use these defaults: Environment variables:
- **API URL**: `http://localhost:3000/api` (override with `API_URL`)
- **Session cookie file**: `~/.config/gantt-board/cookies.txt` (override with `GANTT_COOKIE_FILE`) - `API_URL` (default: `http://localhost:3000/api`)
- **Default Project/Assignee**: optional via `DEFAULT_PROJECT_ID` / `DEFAULT_ASSIGNEE_ID` - `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 ```bash
./gantt.sh auth login <email> <password> ./gantt.sh auth login <email> <password>
``` ```
## Task Management (`task.sh`) ## Command Reference
### Create Task ## `task.sh`
```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
```bash ```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> ./task.sh get <task-id>
``` ./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 "..."]
### Update Task ./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"]
```bash ./task.sh update <task-id> <field> <value> # legacy form
# 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 delete <task-id> ./task.sh delete <task-id>
./task.sh bulk-create <tasks.json>
``` ```
### Bulk Create Notes:
```bash - `--interactive` is not supported in passthrough mode.
# From JSON file - `--auto-create` is not supported in passthrough mode.
./task.sh bulk-create tasks.json
# With auto-create for projects Bulk JSON format:
./task.sh bulk-create tasks.json --auto-create
```
**JSON Format:**
```json ```json
[ [
{ {
@ -155,224 +85,81 @@ Authenticate once before using data commands:
] ]
``` ```
## Project Management (`project.sh`) ## `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
```bash ```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> ./project.sh delete <project-id-or-name>
``` ```
## Sprint Management (`sprint.sh`) ## `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
```bash ```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 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 ```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 ## 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` - project names -> `projectId`
- **Sprints**: "Sprint 1" → `b2c3d4e5-0001-0000-0000-000000000001` - sprint names -> `sprintId`
- **Assignees**: - assignee names/emails -> `assigneeId`
- "Max" → `9c29cc99-81a1-4e75-8dff-cd7cc5ceb5aa` - sprint value `current` -> `/api/sprints/current` (optionally scoped by project)
- "Matt" → `0a3e400c-3932-48ae-9b65-f3f9c6f26fe9`
- **Special**: Use "current" for the most recent active sprint
## Task Types ## Testing
- `task` - Standard task Run refactor safety suite:
- `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
```bash ```bash
# Create a new task npm run test:refactor
./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"
``` ```
### Sprint Planning This runs:
```bash 1. TypeScript unit tests for sprint-selection logic.
# Create new sprint 2. Shell contract tests that mock API transport and validate CLI passthrough behavior.
./sprint.sh create --name "Sprint 5" --project "Gantt Board" --start-date 2026-03-01 --end-date 2026-03-14
# Create multiple tasks for the sprint The shell contract test also fails if direct Supabase/DB references appear in `scripts/`.
./task.sh bulk-create sprint-tasks.json
# Close previous sprint ## AI/Agent Guidance
./sprint.sh close "Sprint 4"
```
### Project Setup If you are extending CLI behavior:
```bash 1. Add/modify API behavior first (`src/app/api/*` and server modules).
# Create new project 2. Keep CLI as passthrough only.
./project.sh create --name "Mobile App" --description "iOS and Android app" --color "#8b5cf6" 3. Add or update tests (unit + mocked CLI contract tests).
4. Do not introduce direct database calls in `scripts/`.
# 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"
```
## Exit Codes ## Exit Codes
- `0` - Success - `0`: success
- `1` - Error (invalid input, API failure, not found) - `1`: error (invalid input, missing entity, API failure)
## 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.