Signed-off-by: Max <ai-agent@topdoglabs.com>
This commit is contained in:
parent
7687dfd8a6
commit
8a828e72c8
125
docs/API_CLI_PASSTHROUGH_PATTERN.md
Normal file
125
docs/API_CLI_PASSTHROUGH_PATTERN.md
Normal 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.
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user