From 4fd0befee16a4515788d4b0c668b395623eec085 Mon Sep 17 00:00:00 2001 From: typebasedio Date: Thu, 14 May 2026 19:59:22 -0400 Subject: [PATCH] docs(v0.5.4): rewrite commands.md as a structured reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring docs/commands.md up to current state. The document was last substantively updated in v0.5; v0.5.2 (restore/notes/path/list --names/ completion + archived-inclusive lookup), v0.5.3 (attach + --direct + persistent session mode + doctor tmux check), and v0.5.4 (info Session block + list SESSION column) were all undocumented. Format follows the spec section §3: each command has Purpose, Usage, Scenarios, Examples, Flags, Notes, Related. Sections are kept short (roughly one screen each) — if a command needed more, the command is doing too much, not the docs. New non-command sections: workspace layout (task and project), a consolidated environment-variables table (previously scattered), explicit query-resolution rules including archive-inclusive lookup behavior, session modes (direct vs persistent), and a shell-completion how-to. Examples use the canonical "ctask" command name per spec §3 — docs describe the product, not the user's local binary path. --- docs/commands.md | 1096 +++++++++++++++++++++++++++------------------- 1 file changed, 642 insertions(+), 454 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index d9ec3d2..800df45 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,576 +1,764 @@ -# Commands +# ctask Commands Reference -## ctask new +This document describes every ctask command, its flags, the workspace layout +ctask creates, the environment variables that influence its behavior, how +queries resolve to workspaces, the two session modes, and how to install +shell completion. -Create a new task or project workspace and launch the agent. +It is a practical reference, not a tutorial. Examples use the canonical +`ctask` command name; the binary may surface as `ctask.exe` or +`./ctask` in your shell, depending on how it was invoked. -``` -ctask new [title] [flags] -``` +## Table of contents -If title is omitted, generates `task-HHMMSS`. +- [Workspace layout](#workspace-layout) +- [Workspace creation and entry](#workspace-creation-and-entry) + - [`ctask new`](#ctask-new) + - [`ctask resume`](#ctask-resume) + - [`ctask last`](#ctask-last) + - [`ctask open`](#ctask-open) + - [`ctask attach`](#ctask-attach) +- [Inspection](#inspection) + - [`ctask info`](#ctask-info) + - [`ctask list`](#ctask-list) + - [`ctask notes`](#ctask-notes) + - [`ctask path`](#ctask-path) +- [Lifecycle](#lifecycle) + - [`ctask archive`](#ctask-archive) + - [`ctask restore`](#ctask-restore) + - [`ctask delete`](#ctask-delete) +- [Environment](#environment) + - [`ctask doctor`](#ctask-doctor) + - [`ctask completion`](#ctask-completion) +- [Environment variables](#environment-variables) +- [Query resolution](#query-resolution) +- [Session modes](#session-modes) +- [Shell completion](#shell-completion) +- [Exit codes](#exit-codes) + +--- + +## Workspace layout + +A task workspace: + + workspace-root/ + ├── task.yaml # workspace metadata (managed by ctask) + ├── CLAUDE.md # agent instructions (seeded by ctask, edited by agent) + ├── notes.md # working context (maintained by agent) + ├── logs/ + │ └── sessions.log # append-only session history + └── .ctask/ # ctask internals (do not edit) + ├── session.json # active session lease (v0.4+) + ├── write.lock # metadata write lock (v0.4+) + ├── manifest-start.json # session start snapshot (v0.2+) + └── last-session-summary.json # session summary (v0.4+) + +A project workspace adds a project subdirectory and (optionally) a git repo: + + workspace-root/ + ├── task.yaml # includes launch_dir: "" + ├── CLAUDE.md + ├── notes.md + ├── .git/ # single git repo at workspace root + ├── .gitignore # excludes .ctask/ and logs/sessions.log + ├── / # project subdirectory; agent launches here + │ └── ... # user's source code + ├── logs/ + │ └── sessions.log + └── .ctask/ + └── ... + +Files inside `.ctask/` are ctask state. Editing them by hand can confuse +session detection, archive guards, and the stale-workspace detector. + +--- + +## Workspace creation and entry + +### `ctask new` + +**Purpose:** Create a new task or project workspace and launch the agent. + +**Usage:** + + ctask new [title] [flags] + +If `title` is omitted, ctask generates `task-HHMMSS`. + +**Scenarios:** +- Starting a new piece of work from scratch. +- Standing up a longer-lived project workspace with `--project`. + +**Examples:** + + $ ctask new "fix auth bug" + [ctask] created general/2026-05-14_fix-auth-bug + + $ ctask new --project "billing service" + [ctask] created projects/2026-05-14_billing-service + + $ ctask new --no-launch "json cleanup" + [ctask] created general/2026-05-14_json-cleanup **Flags:** +- `--category`, `-c` — workspace category subdirectory. Default: `general` + for tasks, `projects` for projects. +- `--project` — create a project workspace. Uses `CTASK_PROJECT_ROOT` if + set, runs `git init`, seeds the project CLAUDE.md template. +- `--shell` — open an interactive shell instead of the agent. +- `--agent`, `-a` — override the agent command (default: `claude` or + `CTASK_AGENT`). +- `--no-launch` — create the workspace only; do not launch a session. +- `--direct` — bypass persistent session mode for this invocation. +- `--container` — deferred to a future release. -| Flag | Short | Default | Description | -|------|-------|---------|-------------| -| `--category` | `-c` | `general` (task) / `projects` (project) | Workspace category subdirectory | -| `--project` | | off | Create a long-lived project workspace (uses `CTASK_PROJECT_ROOT` if set, runs `git init`, project CLAUDE.md) | -| `--shell` | | off | Open interactive shell instead of agent | -| `--agent` | `-a` | `claude` | Command to exec as the agent | -| `--no-launch` | | off | Create workspace only, do not launch | -| `--container` | | off | Deferred to a future release | +**Notes:** +- `--project` runs `git init` if `git` is on PATH and seeds a minimal + `.gitignore` (only when no `.gitignore` was provided by a seed). +- For project workspaces, the agent launches inside the project + subdirectory (controlled by `launch_dir` in `task.yaml`). +- If the session ends without producing any file changes (e.g. the user + cancels at the agent prompt), the workspace is removed automatically — + the "provisional workspace" cleanup gate. Persistent mode disables + this gate. -**Examples:** - -```powershell -ctask new "fix auth bug" -ctask new -c scripts "backup helper" -ctask new --no-launch "json cleanup" -ctask new --shell "test env" -ctask new --agent aider "refactor api" -ctask new -ctask new --project "billing service" -ctask new --project -c backend "billing service" -``` - -When `--no-launch` is used, no session is started and no session log is written. - -### Project mode (`--project`) - -`--project` is a thin variation on the normal task workflow for longer-lived work. It changes: - -- `task.yaml` records `type: project` and `launch_dir: ` (v0.5+) -- Default category becomes `projects` -- Workspace root falls back to `CTASK_PROJECT_ROOT` if set; otherwise `CTASK_ROOT` -- Built-in CLAUDE.md is the project-oriented template (overridable via seed directories) -- Seed order: built-in defaults -> general seed (`CTASK_SEED_DIR`) -> project seed (`CTASK_SEED_PROJECT_DIR`) -- `git init` runs if `git` is on PATH; a minimal `.gitignore` (`.ctask/` + `logs/sessions.log`) is created **only if no `.gitignore` was already provided by a seed** -- If `git` is not available, ctask prints `[ctask] git not found; skipped repository initialization` and continues -- A project subdirectory named after the final suffixed slug is created inside the workspace. ctask does not seed any files inside it -- the user places their own CLAUDE.md, source code, and project structure there. - -**Workspace layout (v0.5):** - -``` -2026-04-22_litlink-v2/ -├── .ctask/ ctask state (lease, manifest, summary, lock) -├── .git/ single git repo at workspace root -├── .gitignore -├── CLAUDE.md ctask workspace rules (managed by ctask + seed) -├── notes.md -├── task.yaml includes launch_dir: "litlink-v2" -├── context/ reference material, imported specs -├── output/ ctask deliverables -├── logs/ session logs -└── litlink-v2/ project subdirectory (user's codebase) -``` - -When `ctask resume litlink-v2` runs, the agent is launched inside `litlink-v2/`. Both the workspace CLAUDE.md (root) and any project CLAUDE.md the user places inside `litlink-v2/` apply to the session -- Claude Code reads CLAUDE.md hierarchically. - -**Project root semantics:** - -- `CTASK_PROJECT_ROOT` not set: workspace goes under `$CTASK_ROOT/projects/_` (default category `projects` is appended) -- `CTASK_PROJECT_ROOT` set, no `-c`: workspace goes directly under `$CTASK_PROJECT_ROOT/_` (no `projects/` subdirectory is appended) -- `CTASK_PROJECT_ROOT` set, explicit `-c `: workspace goes under `$CTASK_PROJECT_ROOT//_` - -**Single git repo rule:** - -Project workspaces use a single git repository initialized at the workspace root. -Do not create nested git repositories inside the workspace -- including inside -the project subdirectory. If your project code lives in a subdirectory, it is -tracked by the root repo. - -**Changing the launch directory:** - -The `launch_dir` field in `task.yaml` controls which subdirectory the agent is launched into on `ctask resume`, `ctask last`, and `ctask open`. By default it is set to the project slug. To launch from the workspace root instead, edit `task.yaml` and set `launch_dir: ""`. To launch from a deeper path (e.g., `backend/api`), set `launch_dir: "backend/api"`. No CLI command is provided -- manual edit is the only supported way to change it. - -If `launch_dir` points to a directory that does not exist (deleted, renamed) or is not a directory, ctask prints a warning and falls back to the workspace root. Absolute paths and paths that escape the workspace via `..` are errors and abort the session. - -### Seed directories - -On `ctask new`, after writing the built-in defaults, ctask copies the contents of an optional user seed directory into the workspace. Files in the seed directory overwrite the built-in defaults; subdirectories are preserved recursively. `task.yaml` and `.ctask/` at the seed root are always skipped. - -| Variable | Default (Unix) | Default (Windows) | -|----------|---------------|-------------------| -| `CTASK_SEED_DIR` | `~/.config/ctask/seed/` | `%APPDATA%\ctask\seed\` | -| `CTASK_SEED_PROJECT_DIR` | `~/.config/ctask/seed-project/` | `%APPDATA%\ctask\seed-project\` | - -The general seed is applied to every workspace. The project seed is applied **only** when `--project` is set, on top of the general seed (project seed wins). Both directories are optional; missing directories are silently ignored. +**Related:** `ctask resume`, `ctask attach`, `ctask doctor`. --- -## ctask list +### `ctask resume` -List workspaces in reverse-chronological order. +**Purpose:** Reopen an existing workspace and launch the agent. -``` -ctask list [flags] -``` +**Usage:** -By default, `ctask list` shows **all active workspaces** -- both tasks and projects. Use `--task` or `--projects` to narrow by type, and `--all` to include archived workspaces. + ctask resume [flags] + +**Scenarios:** +- Picking up where you left off in a previous session. +- Re-entering a long-lived project workspace. + +**Examples:** + + $ ctask resume auth-bug + [ctask] local :: fix-auth-bug + [ctask] ~/ai-workspaces/general/2026-05-14_fix-auth-bug + [ctask] Last session: 2026-05-13 14:30-15:45 (warren-desktop, claude) **Flags:** +- `--shell` — open an interactive shell instead of the agent. +- `--agent`, `-a` — override the agent command for this invocation. +- `--force` — skip both the active-session and stale-workspace warnings. + Use only when the human has already decided to proceed. +- `--direct` — bypass persistent session mode for this invocation. +- `--container` — deferred. -| Flag | Short | Default | Description | -|------|-------|---------|-------------| -| `--all` | `-a` | off | Include archived workspaces | -| `--task` | | off | Show task workspaces only | -| `--projects` | | off | Show project workspaces only | -| `--category` | `-c` | all | Filter by category | -| `--limit` | `-n` | 20 | Maximum entries to show | +**Notes:** +- Resolves archived workspaces too, but refuses to launch them and + prints a `restore` hint instead. +- If multiple workspaces match the query, ctask prints all matches and + exits with an error. See [Query resolution](#query-resolution). +- Updates `updated_at` in `task.yaml` so `ctask last` recognises this + workspace as the most recent. -`--task` and `--projects` are mutually exclusive; passing both returns a usage error. - -**Examples:** - -```powershell -ctask list # active tasks AND projects -ctask list --all # everything (incl. archived) -ctask list --task # active tasks only -ctask list --task --all # all tasks (incl. archived) -ctask list --projects # active projects only -ctask list --projects --all # all projects (incl. archived) -ctask list -c scripts -n 5 -``` - -Output columns: status, type, mode, category, date, slug. - -Workspaces created before v0.3 (which have no `type` field in `task.yaml`) are treated as tasks. +**Related:** `ctask last`, `ctask attach`, `ctask restore`, `ctask info`. --- -## ctask resume +### `ctask last` -Reopen an existing workspace and launch the agent. +**Purpose:** Resume the most recently updated workspace, across both tasks +and projects. -``` -ctask resume [flags] -``` +**Usage:** -Resolves the workspace by query (exact directory name, exact slug, or case-insensitive substring). Archived workspaces are excluded by default. + ctask last [flags] + +**Scenarios:** +- Returning to whatever you were last working on without typing a name. + +**Examples:** + + $ ctask last + [ctask] local :: fix-auth-bug + [ctask] ... **Flags:** +- `--shell` — open an interactive shell instead of the agent. +- `--agent`, `-a` — override the agent command. +- `--force` — skip active-session and stale-workspace warnings. +- `--direct` — bypass persistent session mode for this invocation. -| Flag | Short | Default | Description | -|------|-------|---------|-------------| -| `--shell` | | off | Open shell instead of agent | -| `--agent` | `-a` | from task.yaml | Override agent command | -| `--force` | | off | Skip active-session and stale-workspace warnings | -| `--container` | | off | Deferred to a future release | +**Notes:** +- Equivalent to `ctask resume` on whichever active workspace has the + latest `updated_at`. Archived workspaces are excluded. +- Exits non-zero with `No active workspaces found.` when there are no + active workspaces. -**Examples:** - -```powershell -ctask resume auth-bug -ctask resume backup -ctask resume --shell auth-bug -ctask resume --agent aider auth-bug -ctask resume --force auth-bug -``` - -If multiple workspaces match, prints all matches and exits. If none match, prints an error. - -Session logging runs automatically: file changes during the session are recorded in `logs/sessions.log`. +**Related:** `ctask resume`. --- -## ctask open +### `ctask open` -Open a workspace directory in an interactive shell without launching the agent. +**Purpose:** Open a workspace directory in an interactive shell without +launching the agent. -``` -ctask open [flags] -``` +**Usage:** -Spawns a new subshell in the workspace directory. Does not modify the caller's shell session. + ctask open [flags] + +**Scenarios:** +- Browsing the workspace, running git commands, editing files manually. + +**Examples:** + + $ ctask open auth-bug + # subshell rooted at the workspace; exit returns to your prior shell. **Flags:** +- `--all`, `-a` — include archived workspaces in query resolution. +- `--force` — skip active-session and stale-workspace warnings. +- `--direct` — bypass persistent session mode for this invocation. -| Flag | Short | Default | Description | -|------|-------|---------|-------------| -| `--all` | `-a` | off | Include archived workspaces in query resolution | -| `--force` | | off | Skip active-session and stale-workspace warnings | +**Notes:** +- Spawns a subshell — the parent shell is unaffected. +- Same lease/manifest/summary lifecycle as `resume`, just running + `$SHELL` (or PowerShell on Windows) instead of the agent. -**Examples:** - -```powershell -ctask open auth-bug -``` +**Related:** `ctask resume`, `ctask path`. --- -## ctask info +### `ctask attach` -Display metadata and path for a workspace without entering it. +**Purpose:** Attach to a workspace via tmux. Always uses persistent +session mode regardless of `CTASK_SESSION_MODE`. -``` -ctask info [flags] -``` +**Usage:** + + ctask attach [flags] + +**Scenarios:** +- You have not enabled persistent mode globally but want tmux for one + workspace. +- A persistent tmux session already exists; you want to reattach + instead of starting a second direct-mode session. +- Scripts that need unambiguous "tmux always" behavior. + +**Examples:** + + $ ctask attach promptvolley-v3 + # owner-create OR passive reattach OR adopted reattach, + # depending on the workspace's current lease/tmux state. **Flags:** +- `--agent`, `-a` — override the agent command (used only on + owner-create, when ctask spawns the agent inside the new tmux session). +- `--force` — skip active-session and stale-workspace warnings. + Owner-create path only. -| Flag | Short | Default | Description | -|------|-------|---------|-------------| -| `--all` | `-a` | off | Include archived workspaces in query resolution | +**Notes:** +- Active-only resolution: archived workspaces are not candidates. +- See [Session modes](#session-modes) for the three entry paths + (owner-create, passive reattach, adopted reattach). +- Native Windows is not supported. Run ctask under WSL. -**Examples:** - -```powershell -ctask info auth-bug -ctask info backup -``` - -Shows: slug, title, category, status, mode, agent, created/updated timestamps, path, and directory contents. For v0.5 project workspaces, also shows `Launch dir`, `Launch path`, and `Dir exists`. +**Related:** `ctask resume --direct`, `ctask doctor` (tmux check). --- -## ctask archive +## Inspection -Mark a workspace as archived. The workspace stays in place but is hidden from default listings and query resolution. +### `ctask info` -``` -ctask archive -``` +**Purpose:** Display metadata, path, and current session state for a +workspace without entering it. + +**Usage:** + + ctask info + +**Scenarios:** +- Diagnosing whether a workspace is currently in use. +- Checking the launch directory for a project workspace. **Examples:** -```powershell -ctask archive auth-bug -``` + $ ctask info promptvolley-v3 + Task: promptvolley-v3 + Title: Prompt Volley v3 + Category: projects + Status: active + Mode: local + Agent: claude + Created: 2026-04-24 09:12:03 + Updated: 2026-05-14 18:05:21 + Path: /home/warren/ai-workspaces/projects/2026-04-24_promptvolley-v3 -To see archived workspaces, use `ctask list --all`. To resolve archived workspaces in other commands, use the `--all` flag where available. + Session: active + Mode: persistent + Owner: pid 48291 + Attach: ctask attach promptvolley-v3 + + Launch dir: promptvolley-v3/ + Launch path: /home/warren/.../2026-04-24_promptvolley-v3/promptvolley-v3 + Dir exists: yes + + Contents: + .ctask/ + .git/ + CLAUDE.md + ... + +**Flags:** (none) + +**Notes:** +- Resolves archived workspaces by default — the `Status:` line shows + whether the workspace is active or archived. No `--all` flag needed. +- The `Session:` block is derived from the lease file (no tmux invocation, + no PID liveness checks). Possible states: `none`, `active`, `stale`. + Hostname is omitted when it matches the local machine. +- `Attach:` hint appears only for active+persistent sessions. +- For an archived workspace with a stranded lease (rare; theoretically + possible after a crash), `info` will still surface the raw session + state because `info` is the diagnostic command. `list` collapses + archived workspaces to a dash for simplicity. + +**Related:** `ctask list`, `ctask path`, `ctask notes`. --- -## ctask last +### `ctask list` -Resume the most recently updated workspace, considering **both tasks and projects**. Equivalent to `ctask resume` on whichever active workspace has the latest `updated_at` timestamp. Archived workspaces are excluded. +**Purpose:** List workspaces in reverse-chronological order. -``` -ctask last [flags] -``` +**Usage:** + + ctask list [flags] + +**Scenarios:** +- Surveying active work. +- Finding the basename to feed into another command. +- Driving shell scripting via `--names`. + +**Examples:** + + $ ctask list + active persistent project local projects 2026-04-24 promptvolley-v3 + active direct task local general 2026-05-14 fix-auth-bug + active — task local general 2026-05-12 json-cleanup + + $ ctask list --names + 2026-04-24_promptvolley-v3 + 2026-05-14_fix-auth-bug + 2026-05-12_json-cleanup **Flags:** +- `--all`, `-a` — include archived workspaces. +- `--task` — show task workspaces only. +- `--projects` — show project workspaces only. Mutually exclusive with + `--task`. +- `--category`, `-c` — filter by category. +- `--limit`, `-n` — maximum entries to show. Default: 20. +- `--names` — emit one workspace directory basename per line, no header. + Used by shell completion and scripting. -| Flag | Short | Default | Description | -|------|-------|---------|-------------| -| `--shell` | | off | Open shell instead of agent | -| `--agent` | `-a` | from task.yaml | Override agent command | -| `--force` | | off | Skip active-session and stale-workspace warnings | +**Notes:** +- Output columns (default mode): status, session, type, mode, category, + date, slug. The `session` column shows `direct`, `persistent`, `stale`, + or em dash for no session. Archived workspaces always show em dash in + the session column (a display simplification — see `info` for the raw + state). +- `--names` output is intentionally minimal: bare basenames, no header, + empty stdout when nothing matches. Every emitted line is guaranteed + to resolve to exactly one workspace under the same archive policy. +- Workspaces created before v0.3 (no `type` field) are treated as tasks. + +**Related:** `ctask info`, `ctask path`, `ctask completion`. + +--- + +### `ctask notes` + +**Purpose:** Print a workspace's `notes.md` to stdout. + +**Usage:** + + ctask notes + +**Scenarios:** +- Quick read of working context without `cd`-ing into the workspace. +- Piping notes into pagers, search tools, or other agents. **Examples:** -```powershell -ctask last -ctask last --shell -``` + $ ctask notes auth-bug | less + $ ctask notes promptvolley-v3 | grep -i "decision" -If no active workspaces exist, prints an error and exits. +**Flags:** (none) + +**Notes:** +- Resolves archived workspaces too, mirroring `info`'s archive-inclusive + lookup. +- If `notes.md` is missing, prints `[ctask] no notes.md found in + workspace ""` to stderr and exits non-zero. + +**Related:** `ctask info`, `ctask path`. --- -## ctask doctor +### `ctask path` -Verify that ctask is correctly set up. Read-only -- never modifies anything. +**Purpose:** Print the absolute filesystem path of a workspace, with a +trailing newline. Designed for shell pipelines. -``` -ctask doctor -``` +**Usage:** -Checks: -1. Workspace root exists and is writable -2. Default agent command is found on PATH -3. Status-line helper script exists at the expected location -4. Claude Code `statusLine` is configured in `~/.claude/settings.json` -5. At least one workspace exists + ctask path -Exits 0 if all checks pass, 1 if any fail. Each failure includes a concrete fix instruction. +**Scenarios:** +- `cd "$(ctask path auth-bug)"` +- Feeding the path to other tools (rg, fd, editors). -`ctask doctor` also reports seed directory status: `[INFO]` if the `CTASK_SEED_DIR` / `CTASK_SEED_PROJECT_DIR` variable is unset (built-in defaults will be used), `[PASS]` if the variable is set and the path exists, `[FAIL]` if the variable is set but the path is missing. Only the configured-but-missing state counts as a failure. +**Examples:** -**Example output:** + $ ctask path auth-bug + /home/warren/ai-workspaces/general/2026-05-14_fix-auth-bug -``` - [PASS] Workspace root exists: C:\Users\Warren\ai-workspaces - [PASS] Default agent found: claude - [PASS] Status line helper found: C:\Users\Warren\AppData\Local\ctask\bin\ctask-statusline.sh - [PASS] Claude Code status line configured - [PASS] Workspaces found: 5 tasks (2 archived) - [INFO] General seed directory: not configured (using built-in defaults) - [INFO] Project seed directory: not configured (using built-in defaults) - [INFO] CTASK_PROJECT_ROOT: not set (projects discovered under C:\Users\Warren\ai-workspaces\projects) +**Flags:** (none) -5 checks passed, 0 failed -``` +**Notes:** +- Resolves archived workspaces too. +- Output uses native path separators (backslashes on Windows). + +**Related:** `ctask open`, `ctask info`. --- -## ctask delete +## Lifecycle -Permanently remove a workspace directory. +### `ctask archive` -``` -ctask delete [flags] -``` +**Purpose:** Mark a workspace as archived. The workspace stays on disk +but is hidden from default listings and most query resolution. + +**Usage:** + + ctask archive + +**Scenarios:** +- Putting a workspace away when the task is complete. +- Reducing default-listing noise. + +**Examples:** + + $ ctask archive auth-bug + [ctask] archived: general/2026-05-14_fix-auth-bug + +**Flags:** (none) + +**Notes:** +- Refuses to archive a workspace with an active session (fresh lease) + on non-TTY stdin. On a TTY, prompts for confirmation. +- After archiving, use `ctask list --all` to see archived workspaces and + `ctask restore` to bring one back. + +**Related:** `ctask restore`, `ctask list --all`, `ctask delete`. + +--- + +### `ctask restore` + +**Purpose:** Un-archive a workspace (set `status` back to `active`). + +**Usage:** + + ctask restore + +**Scenarios:** +- Resuming work on a previously archived task or project. + +**Examples:** + + $ ctask restore old-task + [ctask] restored: general/2026-04-22_old-task + [ctask] status: active + +**Flags:** (none) + +**Notes:** +- Errors if the target workspace is already active. +- Mirrors `archive`'s active-session lease guard: refuses on non-TTY, + prompts on TTY. +- Tab completion for `restore` surfaces archived workspaces only. + +**Related:** `ctask archive`, `ctask list --all`. + +--- + +### `ctask delete` + +**Purpose:** Permanently remove a workspace directory. + +**Usage:** + + ctask delete [flags] + +**Scenarios:** +- Reclaiming space from workspaces that are no longer needed. +- Removing accidentally created or trivial workspaces. + +**Examples:** + + $ ctask delete --force throwaway **Flags:** +- `--force`, `-f` — skip the confirmation prompt. +- `--all`, `-a` — include archived workspaces in query resolution. -| Flag | Short | Default | Description | -|------|-------|---------|-------------| -| `--force` | `-f` | off | Skip confirmation prompt | -| `--all` | `-a` | off | Include archived workspaces in query resolution | +**Notes:** +- Confirmation is required by default. `--force` skips it but does NOT + override the active-session guard. +- Refuses to delete the workspace currently exported via + `CTASK_WORKSPACE` (catches same-session attempts) and refuses any + workspace with `.ctask/manifest-start.json` present (catches + cross-terminal attempts). Exit the session first. +- Prints a note before confirmation when deleting the most recently + updated workspace. + +**Related:** `ctask archive` (the safer alternative for completed work). + +--- + +## Environment + +### `ctask doctor` + +**Purpose:** Verify ctask is correctly set up. Read-only. + +**Usage:** + + ctask doctor + +**Scenarios:** +- First-run sanity check after install. +- Diagnosing a misconfigured agent, missing seed dir, or tmux problems. **Examples:** -```powershell -ctask delete old-task -ctask delete --force old-task -ctask delete --all --force archived-task -``` + $ ctask doctor + [PASS] Workspace root exists: /home/warren/ai-workspaces + [PASS] Default agent found: claude + [PASS] Status line helper found: /home/warren/.local/bin/ctask-statusline.sh + [PASS] Claude Code status line configured + [PASS] Workspaces found: 7 tasks (2 archived) + [INFO] General seed directory: not configured (using built-in defaults) + [INFO] Project seed directory: not configured (using built-in defaults) + [INFO] CTASK_PROJECT_ROOT: not set (projects discovered under /home/warren/ai-workspaces/projects) + [INFO] Session mode: persistent + [INFO] tmux found: tmux 3.4 (/usr/bin/tmux) -**Safety:** -- Confirmation is required by default. `--force` skips it. -- If the workspace has an active session (running in another terminal), deletion is refused even with `--force`. Exit the session first. -- If the workspace is the most recently updated one, a note is printed before confirmation. + 10 checks passed, 0 failed + +**Flags:** (none) + +**Notes:** +- Exits 0 on all-pass, 1 if any check fails. +- Each `[FAIL]` row includes a concrete fix line. +- Persistent-mode hosts also see two tmux rows; direct-mode hosts see a + single `[INFO] Session mode: direct (tmux not required)` row. + +**Related:** [`ctask completion`](#ctask-completion), [Session modes](#session-modes). --- -## Query Resolution +### `ctask completion` -Commands that take a `` argument (`resume`, `open`, `info`, `archive`, `delete`) resolve workspaces in this order: +**Purpose:** Emit shell-completion scripts for bash, zsh, fish, or +PowerShell. -1. Exact directory name match (e.g. `2026-04-06_auth-bug`) -2. Exact slug match (e.g. `auth-bug`) -3. Case-insensitive substring match (e.g. `auth`) +**Usage:** -If multiple workspaces match, all matches are printed and the command exits. If none match, an error is printed. + ctask completion {bash | zsh | fish | powershell} -Archived workspaces are excluded from matching by default. Use `--all` where supported to include them. +**Scenarios:** +- Setting up tab completion in your shell so workspace names complete + automatically. -By default, queries search `$CTASK_ROOT` (including its `projects/` subdirectory) plus `$CTASK_PROJECT_ROOT` when that variable is set. Projects created without `CTASK_PROJECT_ROOT` are therefore discoverable from any shell without additional configuration. Custom `CTASK_PROJECT_ROOT` paths must be set (recommended: at user scope) in any shell where you want those projects findable. +**Examples:** + + # bash, current shell only + $ source <(ctask completion bash) + + # zsh, persistent + $ ctask completion zsh > "${fpath[1]}/_ctask" + + # PowerShell, persistent + PS> ctask completion powershell | Out-String | Invoke-Expression + +**Flags:** (none — the shell is a positional argument.) + +**Notes:** +- See [Shell completion](#shell-completion) for installation patterns + per shell. +- Workspace name completion is provided via Cobra `ValidArgsFunction` + hooks: `resume`/`open`/`attach` complete on active workspaces, + `restore` completes on archived workspaces, `info`/`notes`/`path` + complete on any workspace. + +**Related:** [`ctask list --names`](#ctask-list). --- -## Environment Variables +## Environment variables + +ctask reads these to configure behavior: + +| Variable | Default | Description | +|----------|---------|-------------| +| `CTASK_ROOT` | `~/ai-workspaces` (Unix) / `%USERPROFILE%\ai-workspaces` (Windows) | Workspace root directory. | +| `CTASK_PROJECT_ROOT` | (none) | Workspace root for project workspaces. When set, projects go directly under this path with no `projects/` segment unless `-c` is also passed. | +| `CTASK_AGENT` | `claude` | Default agent command. | +| `CTASK_SEED_DIR` | `~/.config/ctask/seed/` (Unix) / `%APPDATA%\ctask\seed\` (Windows) | General user seed directory copied into every new workspace. | +| `CTASK_SEED_PROJECT_DIR` | `~/.config/ctask/seed-project/` (Unix) / `%APPDATA%\ctask\seed-project\` (Windows) | Project seed directory copied only for `--project` workspaces (overlay on top of the general seed). | +| `CTASK_SESSION_MODE` | `direct` | `direct` or `persistent`. See [Session modes](#session-modes). | ctask exports these into every child session: | Variable | Description | |----------|-------------| -| `CTASK_TASK` | Task slug | -| `CTASK_MODE` | Execution mode (`local`) | -| `CTASK_ROOT` | Resolved workspace root path | -| `CTASK_WORKSPACE` | Full workspace path | -| `CTASK_CATEGORY` | Category name | -| `CTASK_TYPE` | `task` or `project` | -| `CTASK_LAUNCH_DIR` | Project subdirectory (v0.5); empty for tasks and pre-v0.5 projects | - -Configure ctask behavior with: - -| Variable | Default | Description | -|----------|---------|-------------| -| `CTASK_ROOT` | `%USERPROFILE%\ai-workspaces` (Windows) / `~/ai-workspaces` (Unix) | Workspace root directory | -| `CTASK_AGENT` | `claude` | Default agent command | -| `CTASK_PROJECT_ROOT` | (none) | Workspace root for projects. When set, project workspaces are created directly under this path (no doubled `projects/` segment unless `-c` is passed). | -| `CTASK_SEED_DIR` | `%APPDATA%\ctask\seed\` (Windows) / `~/.config/ctask/seed/` (Unix) | General user seed directory copied into every new workspace. | -| `CTASK_SEED_PROJECT_DIR` | `%APPDATA%\ctask\seed-project\` (Windows) / `~/.config/ctask/seed-project/` (Unix) | Project seed directory copied only for `--project` workspaces (overlay on top of the general seed). | +| `CTASK_TASK` | Workspace slug. | +| `CTASK_MODE` | Execution mode (currently always `local`). | +| `CTASK_ROOT` | Resolved workspace root path. | +| `CTASK_WORKSPACE` | Full workspace path. | +| `CTASK_CATEGORY` | Category name. | +| `CTASK_TYPE` | `task` or `project`. | +| `CTASK_LAUNCH_DIR` | Project subdirectory; empty for tasks and pre-v0.5 projects. | --- -## Concurrency and safety +## Query resolution -ctask v0.4 protects workspaces from conflicts when multiple sessions (or manual file edits) touch the same workspace. +Commands that take a `` argument resolve workspaces in this order: -### Session lease +1. Exact directory basename match (e.g. `2026-05-14_fix-auth-bug`). +2. Exact slug match (e.g. `fix-auth-bug`). +3. Case-insensitive substring match against the slug (e.g. `auth`). -Each active `ctask resume`, `open`, `last`, or `new` writes a lease file at `/.ctask/session.json` identifying the ctask process, hostname, user, agent, mode, and a heartbeat timestamp. A background goroutine updates the heartbeat every 30 seconds. +If multiple workspaces match, ctask prints all matches and exits with +an error. If none match, ctask prints `No workspace matches "".` +and exits with an error. -On session start, if a fresh lease already exists (heartbeat within 60 seconds), ctask warns: +**Archive-inclusive lookup:** Some commands resolve archived workspaces +by default — `info`, `notes`, `path`, `restore`, and `resume`. Of those, +`resume` refuses to launch the archived workspace and prints a +`restore` hint. The other archive-inclusive commands operate on the +archived workspace as you would expect. -``` -[ctask] This workspace has an active session: - Session: - Host: - Agent: - Started: ( ago) - Last seen: ago +`open`, `delete`, and a few others gate archive inclusion behind +`--all`. `archive` itself is active-only by definition. - Opening a second session may cause conflicts. - Continue anyway? [y/N] -``` - -If the user answers `y`, the second session proceeds **without writing its own lease** (see "Known limitation: coexisting sessions" below). If the lease is older than 60 seconds (crash, lost connection), ctask cleans it up silently and proceeds. - -### Metadata write lock - -All ctask-owned file writes (`task.yaml`, `logs/sessions.log`, `.ctask/session.json`, `.ctask/manifest-start.json`, `.ctask/last-session-summary.json`) are serialized through `/.ctask/write.lock`. The lock is held for the duration of one write only. If the lock cannot be acquired within 2 seconds, the write is skipped with a warning rather than blocking. - -### Stale-workspace detection - -On session start, ctask compares the current workspace state against the end-state recorded by the previous session's summary. If anything changed outside a ctask session (another machine, manual edits), ctask warns: - -``` -[ctask] Workspace modified since last session ended: - - Last session: (, ) - - Modified since then: - notes.md (modified) - output/report.md (new file) - - These changes were not made during a ctask session. - Review before continuing? [Y/n] -``` - -Press Enter (or `y`) to proceed. Press `n` to exit without launching. - -This check is skipped silently for workspaces that have never completed a v0.4 session (no `last-session-summary.json`). - -### Session handoff summary - -At end of session, ctask writes `/.ctask/last-session-summary.json` containing: - -- `session_id`, `hostname`, `agent`, `mode` -- `started_at`, `ended_at`, `duration_seconds` -- `files_added`, `files_modified`, `files_deleted` -- `notes_updated` -- `end_manifest` (snapshot of workspace file list at session end -- used by the stale-workspace detector) - -The next session prints a short orientation banner from this file: - -``` -[ctask] local :: api-cleanup -[ctask] ~/ai-workspaces/general/2026-04-21_api-cleanup -[ctask] Last session: 2026-04-21 14:30-15:45 (warren-desktop, claude) -[ctask] Changed: notes.md, output/plan.md -``` - -### `--force` - -`--force` on `resume`, `open`, and `last` suppresses both the active-session warning and the stale-workspace warning. It does **not** disable the metadata write lock or the session summary -- those are always active. - -Use `--force` only for automation where the human has already decided to proceed. - -### Known limitation: coexisting sessions - -When the user confirms "Continue anyway?" on an active-session warning (or passes `--force`), the second session runs **without writing its own lease**. This keeps the lease model simple (one lease file per workspace), but has two consequences: - -1. A third session attempt will only see the original lease. The second (coexisting) session is invisible to lease-based detection. -2. If the original session exits and removes its lease before the coexisting session finishes, the coexisting session is unprotected for its remaining lifetime. - -The metadata write lock still serializes all ctask-owned file writes regardless of session count, so no state corruption can occur. If you need stronger guarantees, exit the existing session before starting another one. +**Search roots:** Queries always search `$CTASK_ROOT` plus +`$CTASK_PROJECT_ROOT` when set. When `CTASK_PROJECT_ROOT` is unset, +`$CTASK_ROOT/projects/` is searched as well, so projects created with +the default category are discoverable from any shell without +additional configuration. --- -## Exit Codes +## Session modes + +ctask supports two session modes, selected by `CTASK_SESSION_MODE`: + +- **`direct`** (default) — the agent or shell is the foreground process + of the ctask invocation. When the terminal closes, the agent exits. + This is the v0.4 behavior. +- **`persistent`** — workspace entry runs inside a deterministic + per-workspace tmux session named `ctask---`. + Multiple terminals (local + SSH) can attach to the same session, and + the agent survives terminal disconnection. + +Persistent mode requires: + +- tmux 3.0+ on PATH. +- An interactive terminal (over SSH, use `ssh -t`). +- Not running inside an existing tmux session (the `$TMUX` env var + must be unset). +- A Unix-like host or WSL — native Windows is not supported. + +When you run a persistent-mode entry command (`new`, `resume`, `last`, +`open`, or `attach`), ctask picks one of three paths: + +1. **Owner-create** — no tmux session exists for this workspace. ctask + launches the agent inside a new tmux session and starts a lease and + heartbeat. Equivalent to direct-mode entry plus a tmux wrapper. +2. **Passive reattach** — a tmux session exists and a fresh local lease + is heartbeating. ctask attaches the user's terminal to the existing + session and exits when the user detaches. No lease writes, no + manifest, no finalize — the original ctask owner is still managing + the workspace. +3. **Adopted reattach** — a tmux session exists but the lease is + missing, stale, or from another host (the original owner died). + ctask transfers ownership to itself, captures a fresh start + manifest, starts heartbeating, attaches the terminal, and runs + finalize when the session ends. + +`new`, `resume`, `last`, and `open` accept `--direct` to bypass +persistent mode for one invocation. When a persistent tmux session +exists for the workspace, ctask prompts the user to confirm. `--direct` +is a no-op under direct mode (allows scripts to use it defensively). + +`ctask attach` always uses persistent mode regardless of +`CTASK_SESSION_MODE`. + +`ctask doctor` validates session-mode prerequisites and reports tmux +availability. + +--- + +## Shell completion + +`ctask completion ` writes a Cobra-generated completion script +to stdout. Install it once per shell: + +**bash** (system-wide): + + ctask completion bash > /etc/bash_completion.d/ctask + +**bash** (per-user, current session): + + source <(ctask completion bash) + +**zsh** (assuming `compinit` and an `fpath` directory): + + ctask completion zsh > "${fpath[1]}/_ctask" + +**fish:** + + ctask completion fish > ~/.config/fish/completions/ctask.fish + +**PowerShell** (current session): + + ctask completion powershell | Out-String | Invoke-Expression + +**PowerShell** (persistent — append to your profile): + + ctask completion powershell >> $PROFILE + +Once installed, tab completion works on workspace-name arguments. The +completion source is `ctask list --names`, filtered per command: +`resume`/`open`/`attach` show active workspaces only, `restore` shows +archived workspaces only, and `info`/`notes`/`path` show all. + +--- + +## Exit codes | Code | Meaning | |------|---------| -| 0 | Success | -| 1 | General error (multiple matches, not found, invalid args, doctor failure) | -| 2 | Missing required argument | -| 127 | Agent command not found | - ---- - -## Persistent Session Mode (tmux) - -ctask v0.5.3+ supports an opt-in persistent session mode where workspace entry -runs inside a deterministic per-workspace tmux session. Multiple terminals -(local + SSH) attach to the same session, the agent and shell state survive -terminal disconnection, and the v0.4 lifecycle protections continue to apply -with one ctask process owning the workspace lifecycle while terminal -connections come and go. - -### Enabling persistent mode - -```bash -export CTASK_SESSION_MODE=persistent # in ~/.bashrc or equivalent -``` - -When unset or set to `direct`, ctask behaves as in v0.5.2 (no behavior change). - -Persistent mode requires: -- tmux 3.0+ on PATH (install via `apt install tmux`, `brew install tmux`, `pacman -S tmux`, or `dnf install tmux`) -- An interactive terminal (over SSH, use `ssh -t`) -- Not running inside an existing tmux session -- A Unix-like host or WSL — native Windows is not supported (use WSL) - -### Three entry paths - -When you run a persistent-mode entry command (`new`, `resume`, `last`, `open`, or `attach`), ctask picks one of three paths: - -1. **Owner-create** — no tmux session exists for this workspace yet. The command behaves like the direct path but launches the agent inside a new tmux session named `ctask---`. -2. **Passive reattach** — a tmux session exists and a fresh local lease is heartbeating. The command attaches the user's terminal to the existing session and exits when the user detaches. No lease writes, no manifest, no finalize — the original ctask owner is still managing the workspace. -3. **Adopted reattach** — a tmux session exists but the lease is missing, stale, or from another host (the original owner died). The command transfers ownership to itself, captures a fresh start manifest, starts heartbeating, attaches the terminal, and runs finalize when the session ends. - -### `ctask attach ` - -`ctask attach` always uses tmux regardless of `CTASK_SESSION_MODE`. Useful when you have not enabled persistent mode globally but want tmux for one workspace, or when shell scripts need unambiguous behavior. - -```bash -ctask attach promptvolley-v3 -``` - -The same three paths apply. - -### `--direct` bypass flag - -`new`, `resume`, `last`, and `open` accept `--direct` to bypass persistent mode for one invocation. When a persistent tmux session exists for the workspace, ctask prompts: - -``` -A persistent tmux session exists for this workspace: - ctask-projects-promptvolley-v3-a8f3c2 - -Opening a direct-mode shell may create conflicting workspace activity. -The recommended path is: - ctask attach promptvolley-v3 - -Continue with --direct anyway? [y/N] -``` - -`--direct` is a no-op under direct mode (allows scripts to use it defensively). - -### Doctor - -`ctask doctor` reports: - -``` -[INFO] Session mode: persistent -[INFO] tmux found: tmux 3.4 (/usr/bin/tmux) -``` - -or, on misconfiguration: - -``` -[INFO] Session mode: persistent -[FAIL] tmux not found on PATH - Fix: install tmux 3.0+ (apt/brew/pacman/dnf), or unset CTASK_SESSION_MODE -``` - -### Workflow examples - -**Local development** - -```bash -export CTASK_SESSION_MODE=persistent - -ctask new --project promptvolley-v3 -# -> workspace created, tmux session ctask-projects-promptvolley-v3-a8f3c2 started, attached. - -# Detach with Ctrl-B d. Terminal returns; tmux session keeps running. - -ctask resume promptvolley-v3 -# -> passive reattach. Same Claude Code session, scrollback intact. -``` - -**Remote access via SSH** - -```bash -ssh -t warren-desktop # -t is required -ctask resume promptvolley-v3 # -> passive reattach (concurrent with desktop) -``` - -### Native Windows note - -Persistent mode is not supported on native Windows (PowerShell). Run ctask under WSL and install tmux there. +| 0 | Success. | +| 1 | General error (multiple matches, not found, invalid args, doctor failure, refusal). | +| 2 | Missing required argument (Cobra default). | +| 127 | Agent command not found. |