diff --git a/docs/API_CLI_PASSTHROUGH_PATTERN.md b/docs/API_CLI_PASSTHROUGH_PATTERN.md new file mode 100644 index 0000000..6346047 --- /dev/null +++ b/docs/API_CLI_PASSTHROUGH_PATTERN.md @@ -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. diff --git a/scripts/README.md b/scripts/README.md index d6e04c7..a5c4067 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -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 ``` -## 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 ] [--priority ] [--project ] [--assignee ] [--type ] [--limit ] [--json] ./task.sh get -``` - -### Update Task - -```bash -# Update status -./task.sh update --status in-progress - -# Update multiple fields -./task.sh update \ - --status done \ - --priority low \ - --add-comment "Task completed" - -# Update assignee -./task.sh update --assignee "Matt" - -# Clear tags -./task.sh update --clear-tags - -# Update tags -./task.sh update --tags "new-tag,another-tag" -``` - -### Delete Task - -```bash +./task.sh current-sprint [--project ] +./task.sh create --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)