diff --git a/.claude/settings.json b/.claude/settings.json index 0f0432cc..651ccc62 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -16,7 +16,10 @@ "hack": { "type": "stdio", "command": "hack", - "args": ["mcp", "serve"] + "args": [ + "mcp", + "serve" + ] } } } diff --git a/.claude/skills/hack-init/SKILL.md b/.claude/skills/hack-init/SKILL.md new file mode 100644 index 00000000..7e0f7aca --- /dev/null +++ b/.claude/skills/hack-init/SKILL.md @@ -0,0 +1,17 @@ +--- +name: hack-init +description: > + Stand up or adopt hack in a project (agent-assisted onboarding). + Trigger when asked to set a repo up with hack, migrate a project onto + hack, or finish/repair a partial hack setup. +--- + +# hack init (agent-assisted onboarding) + +The full onboarding prompt is generated by the hack CLI — do not improvise the steps. + +1. Fetch the canonical onboarding prompt: + - With shell access: run `hack agent onboard` (works for new and existing projects). + - Without shell access: fetch the `hack-init` MCP prompt from the hack MCP server. +2. Follow the printed prompt phase by phase (inventory, setup, platform nuances, running things, verification). +3. Finish only when `hack doctor` is clean and every routable service responds. diff --git a/.codex/skills/hack-cli/SKILL.md b/.codex/skills/hack-cli/SKILL.md index 57071c7f..d615acb5 100644 --- a/.codex/skills/hack-cli/SKILL.md +++ b/.codex/skills/hack-cli/SKILL.md @@ -1,7 +1,7 @@ --- name: hack-cli description: > - Use the hack CLI for local runtime orchestration (compose, DNS/TLS, logs, persistent project workspaces, tickets) and agent setup. + Use the hack CLI for local runtime orchestration (compose, DNS/TLS, logs, env, persistent project workspaces) and agent setup. Trigger when asked to run/start/stop services, inspect logs, manage lifecycle/workspace workflows, or update agent integrations. Prefer CLI over MCP when shell access is available. --- @@ -10,128 +10,116 @@ description: > Use `hack` as the primary interface for local-first development. -## Product Boundary +## Product boundary -- Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, daemon, and optional local tickets. +- Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. - Removed surfaces: hosted auth/account/org/team flows, web dashboard, built-in GitHub workflows, and built-in Linear sync. -- Remote/gateway/node/dispatch code is unsupported experimental. Do not put it on the critical path for local dev unless explicitly requested. +- Experimental and unsupported: remote/gateway/node/dispatch commands. They are hidden from default help (list with `hack help --all`) and warn on use; do not use them unless explicitly requested. -## Operating Rules +## Operating rules -- Prefer `hack` over raw `docker` / `docker compose`. -- Do not start/stop project services from Docker Desktop UI for `hack`-managed repos. -- Treat `.hack/.internal` and `.hack/.branch` as hack-managed artifacts; avoid hand-editing generated files. +- Prefer `hack` over raw `docker` / `docker compose` for project workflows. +- Do not start/stop services from Docker Desktop UI for `hack`-managed projects. +- Treat `.hack/.internal` and `.hack/.branch` as hack-managed artifacts; do not hand-edit generated files there. +- Use `--json` for machine-readable output when available; `hack up/down/restart/doctor --json` emit an `{ok, data | error: {code, message}}` envelope with stable E_* error codes. +- Scripted/agent runs: pass `--no-interactive` (or set `HACK_NO_INTERACTIVE=1`) so commands never block on prompts — they apply documented defaults or fail fast with E_INTERACTIVE_REQUIRED. - Use MCP only when shell access is unavailable. -- Run `hack doctor` (and `hack doctor --fix`) before manual runtime/network repair. +- If runtime state looks wrong, run `hack doctor`, then `hack doctor --fix` before manual repair. -## Config + Schema +## Core objects + +- Project: a repo with `.hack/` config + compose file. +- Service: a compose service (e.g. api, web, worker). +- Instance: a running project; branch instances are separate copies started with `--branch`. + +## Config + schema - Project config: `.hack/hack.config.json` - Global config: `~/.hack/hack.config.json` - Schema URL: `https://schemas.hack/hack.config.schema.json` -- Prefer CLI config edits via `hack config get/set`. +- Prefer CLI writes: `hack config get `, `hack config set `, `hack config set --global ` -## Hostname Routing +## Hostname routing + Caddy labels -- Primary host is `dev_host` (default: `.hack`). -- Subdomains use `.` (for example: `api.myapp.hack`). -- OAuth alias can add `.` and `..` (default: `gy`). -- Only HTTP services with Caddy labels and `hack-dev` network attachment are routable. -- Required labels: `caddy`, `caddy.reverse_proxy`, `caddy.tls=internal`. +- Primary host comes from `dev_host` (default: `.hack`). +- Subdomain pattern is `.` (for example: `api.myapp.hack`). +- OAuth alias (when enabled) also routes `.` and `..` (default tld: `gy`). +- Not every compose service is routable: only services with Caddy labels and on `hack-dev` are exposed. +- Required labels for HTTP services: `caddy`, `caddy.reverse_proxy`, `caddy.tls=internal`. - Quick checks: `hack open`, `hack open `, `hack open --json`. -## TLS + OAuth Host Rules +## TLS + valid-hostname constraints -- Caddy internal PKI provides HTTPS for routed hosts; trust CA with `hack global trust`. -- `.hack` is local-first and not a public suffix. -- Use alias hosts like `*.hack.gy` when provider callback validation rejects non-public-suffix hosts. -- Alias hosts are local routes unless you explicitly add remote ingress/tunnel plumbing. +- `hack` uses Caddy internal PKI for HTTPS on routed hosts; trust CA with `hack global trust`. +- `.hack` is local-first and great for dev, but it is not a public suffix. +- Use OAuth alias hosts (for example `*.hack.gy`) when providers require public-suffix-style callback domains. +- Alias hosts are still local-dev routes unless you add an external tunnel/remote ingress path. -## Managed Files +## Project files (managed vs generated) - Source-of-truth files: `.hack/docker-compose.yml`, `.hack/hack.config.json`, `.hack/hack.env.default.yaml`, and optional `.hack/hack.env..yaml`. -- Worktree-local override files: `.hack/hack.env.local.yaml` and `.hack/hack.env..local.yaml`. -- Local-only files: `.hack.secret.key`, optional `.hack/.env` compatibility output, `.hack/.env.state.json`, and `.hack/.internal/` (gitignored; machine-specific state). -- Generated by hack: `.hack/.internal/compose.override.yml`, `.hack/.internal/compose.env.override.yml`, `.hack/.branch/compose..override.yml`. -- Managed via CLI: `.hack/.internal/extra-hosts.json` using `hack internal extra-hosts ...` commands. +- Worktree-local env override files: `.hack/hack.env.local.yaml` and `.hack/hack.env..local.yaml`. +- Local-only files: `.hack.secret.key`, optional `.hack/.env` compatibility output, `.hack/.env.state.json`, and `.hack/.internal/` (runtime/local machine state; keep gitignored). +- Generated (do not hand-edit): `.hack/.internal/compose.override.yml`, `.hack/.internal/compose.env.override.yml`, `.hack/.branch/compose..override.yml`. +- Managed via CLI: `.hack/.internal/extra-hosts.json` (use `hack internal extra-hosts ...` commands). - Lifecycle runtime files: `.hack/.internal/lifecycle/state.json`, `.hack/.internal/lifecycle/*.log`. +- Ignore rules: hack owns a committed `.hack/.gitignore` (self-healing on init/up) covering machine-local generated files (`.internal/`, `.branch/`, `.env`, `.env.state.json`, `hack.env*.local.yaml`); keep it committed, and if generated files leaked into git, `hack doctor --fix` untracks them (files stay on disk). -## Advanced Networking - -- Static host mappings: set `internal.extra_hosts` in `.hack/hack.config.json`. -- Dynamic mappings for local proxies/tunnels: `hack internal extra-hosts set `. -- List/remove mappings: `hack internal extra-hosts list` / `hack internal extra-hosts unset `. -- Prefer `host-gateway` for host-local proxy targets when possible. -- Apply changes with `hack restart`; verify with `hack doctor`. - -## Quick Start - -- Bootstrap project config: `hack init` -- Start services: `hack up --detach` -- Alternate shorthand: `hack up -d` -- Restart services: `hack restart` -- Open app: `hack open --json` -- Tail logs (compose): `hack logs --pretty` -- Per-service logs: `hack logs ` -- Snapshot logs: `hack logs --json --no-follow` -- Loki history/query: `hack logs --loki --since 2h --pretty` -- Run commands: `hack run ` -- Stop services: `hack down` - -## Global Infra +## Linked git worktrees -- Install once: `hack global install` -- Start/stop/status: `hack global up`, `hack global down`, `hack global status` -- Global logs: `hack global logs --no-follow --tail 200` +- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments. +- `hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; opt out with config `worktree.auto_branch=false` or an explicit `--branch`. +- `hack doctor` flags divergent secret keys and dev_host collisions across checkouts. -## Unsupported Experimental Remote +## Advanced networking (extra_hosts + local proxies/tunnels) -These commands are source-available but outside the supported v3 product contract: +- Static host mappings: set `internal.extra_hosts` in `.hack/hack.config.json`. +- Dynamic host mappings: `hack internal extra-hosts set ` / `unset` / `list`. +- For host-local proxies/tunnels, prefer `host-gateway` as target when possible. +- After mapping changes or proxy IP churn: `hack restart` and then `hack doctor`. -- Pair/register a node: `hack node pair ...`, then verify via `hack node list` and `hack node status --watch`. -- Repair SSH access for remote Git/mutagen: `hack node ssh setup --node `. -- Inspect node workspace map on the node host: `ssh 'hack node workspace list --json'`. -- Inspect/repair controller route bridge: `hack node routes status`, `hack node routes repair`. -- Dispatch command to remote workspace: `hack dispatch run --project --node default --branch --runner generic -- "pwd"`. +## Standard workflow -## Lifecycle + Startup +- If `.hack/` is missing: `hack init` +- Start services: `hack up --detach` (or `hack up -d`) +- Check status: `hack ps` or `hack status` +- Open app URL: `hack open --json` +- Restart: `hack restart` +- Stop services: `hack down` -- Put host setup in `.hack/hack.config.json` under `startup` / `lifecycle`. -- Use lifecycle processes for long-running host tasks, not ad-hoc terminals. -- For fixed-port host helpers such as SSM tunnels or local proxies, set `singleton.ports` and usually `onConflict: "adopt"` so Hack reuses a healthy existing listener instead of starting duplicate tunnel stacks. -- `singleton` is a listener guard, not process ownership transfer; adopted external processes are left running on `hack down`. -- Inspect via `hack projects --details` and `hack logs `. +## Running things (decision guide) -## Verification Loops +- One-off command in a fresh service container (deps started as needed): `hack run `. +- Command inside an already-running service container: `hack exec -- `. +- Host script that needs hack-stored env: `hack host exec --env --scope -- ` — this is THE way to run repo scripts; never read .env files directly. +- Interactive host shell with injected env: `hack host shell --env --scope `. +- Call a service over HTTP (from the host or between containers): use its Caddy hostname `https://.`; discover routable URLs with `hack open --json`. -- For `hack run` / `hack exec` / env-resolution changes, verify the effective env transition matrix in - `tests/project-run-command.test.ts`. -- Cover omitted env, explicit overlay, explicit `base`, default-overlay resolution, cached runtime-state env, - target-service running/not-running, worktree-local overrides, and host-vs-compose target mode. -- For lifecycle or startup-process changes, verify `tests/project-lifecycle-processes.test.ts` and - `tests/project-lifecycle-singleton.test.ts`. -- Preserve `sh -c` semantics, process-group cleanup, stale pane-metadata reconciliation, singleton listener - behavior, and interactive stdin behavior. -- When semantics change, update `docs/env.md` or `docs/lifecycle.md` in the same patch so future agent work starts - from the current contract. +## Logs (default is compose) -## Branch Instances +- Fast tail: `hack logs --pretty` +- Per-service tail: `hack logs ` +- Machine snapshot: `hack logs --json --no-follow` +- Loki history/query: `hack logs --loki --since 2h --pretty` or `hack logs --loki --query '{project=""}'` +- Force compose backend: `hack logs --compose` +- Global infra logs: `hack global logs caddy --no-follow --tail 200` -Use branch instances to run parallel environments: +## Lifecycle + startup -- `hack up --branch --detach` -- `hack open --branch ` -- `hack logs --branch ` -- `hack down --branch ` +- Put host setup in `.hack/hack.config.json` under `startup`/`lifecycle` (not ad-hoc terminal tabs). +- Use `lifecycle.up.before` for pre-start hooks and `lifecycle.processes` for long-running host tasks. +- For fixed-port host helpers such as SSM tunnels or local proxies, set `singleton.ports` and usually `onConflict: "adopt"` so Hack reuses a healthy existing listener instead of starting duplicate tunnel stacks. +- `singleton` is a listener guard, not process ownership transfer; adopted external processes are left running on `hack down`. +- Inspect lifecycle status via `hack projects --details` and stream via `hack logs `. -## Workspaces +## Workspaces (mux-managed, tmux-first by default) - Picker: `hack session` for persistent project workspaces. - Reuse/create: `hack session start ` - Env-scoped workspace: `hack session start --env qa --service api --detach` -- Isolated agent workspace: `hack session start --new --name agent-1` (`--agent-1`). -- Exec in workspace: `hack session exec ""` -- Exec in workspace with injected env: `hack session exec --env qa --service api "bun db:migrate"` +- Force isolated agent workspace: `hack session start --new --name agent-1` (`--agent-1`). +- Execute in workspace: `hack session exec ""` +- Execute in workspace with injected env: `hack session exec --env qa --service api "bun db:migrate"` - Stop workspace: `hack session stop ` ## Host-side env helpers @@ -142,34 +130,63 @@ Use branch instances to run parallel environments: - Interactive host shell with injected env: `hack host shell --env qa --scope api` - Run inside an already-running service container: `hack exec api -- bun test` -## Tickets +## Branch instances (parallel envs) + +- Use a branch instance when you need two versions running at once (PR review, experiments, migrations) or want to keep a stable environment while testing another branch. +- Target one with `--branch ` on up/open/logs/down (for example: `hack up --branch --detach`). +- Linked worktrees pick a branch instance automatically (see Linked git worktrees). + +## Run commands inside services + +- One-off: `hack run ` (uses `docker compose run --rm`) +- Example: `hack run api bun test` +- Use `--workdir ` to change working dir inside the container. +- Use `hack ps --json` to list services and status. + +## Project targeting + +- From repo root, commands use that project automatically. +- Else use `--project ` (registry) or `--path `. +- List projects: `hack projects --json` -- Create: `hack tickets create --title "..." --body-stdin` -- List/show: `hack tickets list`, `hack tickets show T-AB12CD34EF` -- Status/sync: `hack tickets status T-AB12CD34EF in_progress`, `hack tickets sync` +## Global infra -## Project Targeting +- Bootstrap once: `hack global install` +- Start/stop/status: `hack global up`, `hack global down`, `hack global status` +- Use `hack global up` before Loki/Grafana queries if global logging is offline. + +## Daemon (optional) + +- Start for faster JSON status/ps: `hack daemon start` +- Check status: `hack daemon status` -- Run from repo root when possible. -- Otherwise use `--project ` or `--path `. -- List projects: `hack projects --json`. +## Docker compose notes -## Agent Maintenance +- Prefer `hack` commands; they include the right files/networks. +- Use `docker compose -f .hack/docker-compose.yml exec ` only if you need exec into a running container. -- Project-level hack commands auto-check integration drift and attempt auto-sync. -- Set `HACK_SETUP_SYNC_MODE=warn` to warn-only, or `HACK_SETUP_SYNC_MODE=off` to disable. -- Refresh project + global integrations: `hack setup sync --all-scopes` -- Check generated integrations: `hack setup sync --all-scopes --check` -- Remove generated integrations: `hack setup sync --all-scopes --remove` -- After self-update: `hack update` then `hack setup sync --all-scopes` +## Agent integration maintenance -## Agent Setup +- Project-level hack commands auto-check integration drift and attempt auto-sync (docs/skills/MCP). +- Set `HACK_SETUP_SYNC_MODE=warn` to only warn, or `HACK_SETUP_SYNC_MODE=off` to disable. +- Refresh project + user integrations: `hack setup sync --all-scopes` +- Audit integration state only: `hack setup sync --all-scopes --check` +- Remove generated integration artifacts: `hack setup sync --all-scopes --remove` +- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). + +## Agent setup (CLI-first) - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Tickets skill: `hack setup tickets` +- Refresh all local agent integrations: `hack setup sync --all-scopes` +- Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` -- MCP (no shell only): `hack setup mcp` +- MCP (no-shell only): `hack setup mcp` - MCP install (explicit): `hack mcp install --all --scope project` + +## Optional extensions + +- A local git-backed tickets extension exists (`hack tickets`) — only use it when the project explicitly uses it. diff --git a/.codex/skills/hack-init/SKILL.md b/.codex/skills/hack-init/SKILL.md new file mode 100644 index 00000000..7e0f7aca --- /dev/null +++ b/.codex/skills/hack-init/SKILL.md @@ -0,0 +1,17 @@ +--- +name: hack-init +description: > + Stand up or adopt hack in a project (agent-assisted onboarding). + Trigger when asked to set a repo up with hack, migrate a project onto + hack, or finish/repair a partial hack setup. +--- + +# hack init (agent-assisted onboarding) + +The full onboarding prompt is generated by the hack CLI — do not improvise the steps. + +1. Fetch the canonical onboarding prompt: + - With shell access: run `hack agent onboard` (works for new and existing projects). + - Without shell access: fetch the `hack-init` MCP prompt from the hack MCP server. +2. Follow the printed prompt phase by phase (inventory, setup, platform nuances, running things, verification). +3. Finish only when `hack doctor` is clean and every routable service responds. diff --git a/.codex/skills/hack-tickets/SKILL.md b/.codex/skills/hack-tickets/SKILL.md deleted file mode 100644 index 50a1c764..00000000 --- a/.codex/skills/hack-tickets/SKILL.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -name: hack-tickets -description: > - Use the hack tickets extension (git-backed JSONL event log) to create/list/show/sync lightweight tickets. - Trigger when asked to track work items, decisions, or bugs inside a repo without external issue trackers. - Prefer `hack x tickets ...` commands; store lives on hidden ref `refs/hack/tickets` by default. ---- - -# hack tickets - -This repo uses the hack tickets extension (`dance.hack.tickets`). -Prefer `hack tickets ...` (alias) or `hack x tickets ...` over manual edits in `.hack/tickets/`. - -## Enable - -Enable globally: - -- `hack config set --global 'controlPlane.extensions["dance.hack.tickets"].enabled' true` -- `hack setup sync --all-scopes` (refresh generated agent instructions + skills + MCP config) - -Or per-project by adding `.hack/hack.config.json`: - -```json -{ - "controlPlane": { - "extensions": { - "dance.hack.tickets": { "enabled": true } - } - } -} -``` - -## Commands - -- Create: `hack x tickets create --title "..." [--body "..."] [--body-file ] [--body-stdin] [--depends-on "..."] [--blocks "..."] [--actor "..."] [--json]` -- List: `hack x tickets list [--json]` -- Tui: `hack x tickets tui` -- Show: `hack x tickets show [--json]` -- Update: `hack x tickets update [--title "..."] [--body "..."] [--depends-on "..."] [--blocks "..."] [--clear-depends-on] [--clear-blocks] [--json]` -- Status: `hack x tickets status [--json]` -- Sync: `hack x tickets sync [--json]` - -Recommended body template (Markdown): - -```md -## Context -## Goals -## Notes -## Links -``` - -Tip: use `--body-stdin` for multi-line markdown. - -## Data model - -- Tickets are derived from an append-only event log (JSONL). -- Local state lives in `.hack/tickets/` (gitignored on the main branch). -- Sync writes commits to a dedicated ref (`refs/hack/tickets` hidden by default) and pushes to your remote. -- Set `controlPlane.tickets.git.refMode` to `heads` to use a normal branch ref (and protect it if desired). - -## Tips - -- Keep ticket titles short; put detail in `--body`. -- Use `--json` for agent workflows and piping. -- Run `hack x tickets sync` before opening PRs if you want tickets to travel with the repo. -- Update status continuously (`open` -> `in_progress` -> `blocked`/`done`) so handoffs are explicit. -- Use `--depends-on` / `--blocks` links to model execution order for parallel agent work. diff --git a/.cursor/mcp.json b/.cursor/mcp.json index 25679fff..40116438 100644 --- a/.cursor/mcp.json +++ b/.cursor/mcp.json @@ -2,7 +2,10 @@ "mcpServers": { "hack": { "command": "hack", - "args": ["mcp", "serve"] + "args": [ + "mcp", + "serve" + ] } } } diff --git a/.cursor/rules/hack.mdc b/.cursor/rules/hack.mdc index 8f55277b..60dc8140 100644 --- a/.cursor/rules/hack.mdc +++ b/.cursor/rules/hack.mdc @@ -2,61 +2,86 @@ # Auto-generated by 'hack setup cursor' - do not remove these markers # BEGIN HACK INTEGRATION -This project uses `hack` for local runtime orchestration (compose + DNS/TLS + logs + sessions). +Hack v3 is local-first. This project uses `hack` for local runtime orchestration (compose + DNS/TLS + logs + sessions). Prefer `hack` when shell access is available. Use MCP only when shell access is unavailable. -## Core Rules -- Use `hack` CLI for start/stop/open/logs/run/session/tickets workflows. -- Do not run raw `docker compose up/down/logs` for normal project operations. -- Treat `.hack/.internal` and `.hack/.branch` as hack-managed artifacts; avoid manual edits there. -- Use lifecycle/startup config in `.hack/hack.config.json` instead of ad-hoc host process tabs. -- Use `--json` for machine-readable output when available. -- If state is inconsistent, run `hack doctor` then `hack doctor --fix` before manual repairs. -- For hostname routing, only services with Caddy labels on `hack-dev` are exposed (not every compose service). -- Use `.hack.gy` alias hosts for OAuth-style callback validation when `.hack` is rejected by providers. - -## Managed Files -- Source-of-truth: `.hack/docker-compose.yml`, `.hack/hack.config.json`, `.hack/hack.env.json`. -- Generated/local-only: `.hack/.internal/compose.override.yml`, `.hack/.internal/compose.env.override.yml`, `.hack/.branch/compose..override.yml`, `.hack/.internal/lifecycle/*`. -- Manage dynamic host mappings with `hack internal extra-hosts ...` (not manual file edits). - -## Quick Reference -```bash -hack init -hack up --detach -hack up -d -hack open --json -hack open -hack logs --pretty -hack logs -hack logs --json --no-follow -hack logs --loki --since 2h --pretty -hack run -hack restart -hack down -hack global status -hack session -hack tickets list -hack node list -hack node status --watch -hack node ssh setup --node -hack node routes status -hack node routes repair -hack dispatch run --project --node default --branch --runner generic -- "pwd" -hack internal extra-hosts list -hack internal extra-hosts set -hack internal extra-hosts unset -hack up --branch --detach -hack open --branch -hack logs --branch -hack down --branch -hack setup sync --all-scopes -``` - -## MCP (optional) -- Install only for no-shell clients: `hack setup mcp` -- Project-level hack commands auto-check integration drift and attempt auto-sync by default. -- Use `HACK_SETUP_SYNC_MODE=warn` (warn only) or `HACK_SETUP_SYNC_MODE=off` (disable). -- Refresh generated MCP/docs/instructions: `hack setup sync --all-scopes` +## Product boundary + +- Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. +- Removed surfaces: hosted auth/account/org/team flows, web dashboard, built-in GitHub workflows, and built-in Linear sync. +- Experimental and unsupported: remote/gateway/node/dispatch commands. They are hidden from default help (list with `hack help --all`) and warn on use; do not use them unless explicitly requested. + +## Operating rules + +- Prefer `hack` over raw `docker` / `docker compose` for project workflows. +- Do not start/stop services from Docker Desktop UI for `hack`-managed projects. +- Treat `.hack/.internal` and `.hack/.branch` as hack-managed artifacts; do not hand-edit generated files there. +- Use `--json` for machine-readable output when available; `hack up/down/restart/doctor --json` emit an `{ok, data | error: {code, message}}` envelope with stable E_* error codes. +- Scripted/agent runs: pass `--no-interactive` (or set `HACK_NO_INTERACTIVE=1`) so commands never block on prompts — they apply documented defaults or fail fast with E_INTERACTIVE_REQUIRED. +- Use MCP only when shell access is unavailable. +- If runtime state looks wrong, run `hack doctor`, then `hack doctor --fix` before manual repair. + +## Project files (managed vs generated) + +- Source-of-truth files: `.hack/docker-compose.yml`, `.hack/hack.config.json`, `.hack/hack.env.default.yaml`, and optional `.hack/hack.env..yaml`. +- Worktree-local env override files: `.hack/hack.env.local.yaml` and `.hack/hack.env..local.yaml`. +- Local-only files: `.hack.secret.key`, optional `.hack/.env` compatibility output, `.hack/.env.state.json`, and `.hack/.internal/` (runtime/local machine state; keep gitignored). +- Generated (do not hand-edit): `.hack/.internal/compose.override.yml`, `.hack/.internal/compose.env.override.yml`, `.hack/.branch/compose..override.yml`. +- Managed via CLI: `.hack/.internal/extra-hosts.json` (use `hack internal extra-hosts ...` commands). +- Lifecycle runtime files: `.hack/.internal/lifecycle/state.json`, `.hack/.internal/lifecycle/*.log`. +- Ignore rules: hack owns a committed `.hack/.gitignore` (self-healing on init/up) covering machine-local generated files (`.internal/`, `.branch/`, `.env`, `.env.state.json`, `hack.env*.local.yaml`); keep it committed, and if generated files leaked into git, `hack doctor --fix` untracks them (files stay on disk). + +## Linked git worktrees + +- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments. +- `hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; opt out with config `worktree.auto_branch=false` or an explicit `--branch`. +- `hack doctor` flags divergent secret keys and dev_host collisions across checkouts. + +## Standard workflow + +- If `.hack/` is missing: `hack init` +- Start services: `hack up --detach` (or `hack up -d`) +- Check status: `hack ps` or `hack status` +- Open app URL: `hack open --json` +- Restart: `hack restart` +- Stop services: `hack down` + +## Running things (decision guide) + +- One-off command in a fresh service container (deps started as needed): `hack run `. +- Command inside an already-running service container: `hack exec -- `. +- Host script that needs hack-stored env: `hack host exec --env --scope -- ` — this is THE way to run repo scripts; never read .env files directly. +- Interactive host shell with injected env: `hack host shell --env --scope `. +- Call a service over HTTP (from the host or between containers): use its Caddy hostname `https://.`; discover routable URLs with `hack open --json`. + +## Lifecycle + startup + +- Put host setup in `.hack/hack.config.json` under `startup`/`lifecycle` (not ad-hoc terminal tabs). +- Use `lifecycle.up.before` for pre-start hooks and `lifecycle.processes` for long-running host tasks. +- For fixed-port host helpers such as SSM tunnels or local proxies, set `singleton.ports` and usually `onConflict: "adopt"` so Hack reuses a healthy existing listener instead of starting duplicate tunnel stacks. +- `singleton` is a listener guard, not process ownership transfer; adopted external processes are left running on `hack down`. +- Inspect lifecycle status via `hack projects --details` and stream via `hack logs `. + +## Host-side env helpers + +- One-off host command with injected env: `hack host exec --env qa --scope api -- bun db:migrate` +- Host commands default to a host-local env view; use `--target compose` when you explicitly want container-oriented addresses. +- `--scope` selects which env scope to inject; it does not move execution into that service container. +- Interactive host shell with injected env: `hack host shell --env qa --scope api` +- Run inside an already-running service container: `hack exec api -- bun test` + +## Agent integration maintenance + +- Project-level hack commands auto-check integration drift and attempt auto-sync (docs/skills/MCP). +- Set `HACK_SETUP_SYNC_MODE=warn` to only warn, or `HACK_SETUP_SYNC_MODE=off` to disable. +- Refresh project + user integrations: `hack setup sync --all-scopes` +- Audit integration state only: `hack setup sync --all-scopes --check` +- Remove generated integration artifacts: `hack setup sync --all-scopes --remove` +- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). + +## Optional extensions + +- A local git-backed tickets extension exists (`hack tickets`) — only use it when the project explicitly uses it. # END HACK INTEGRATION diff --git a/.hack/hack.config.json b/.hack/hack.config.json index 9e6a9331..514ca61a 100644 --- a/.hack/hack.config.json +++ b/.hack/hack.config.json @@ -5,13 +5,13 @@ "controlPlane": { "extensions": { "dance.hack.github": { - "enabled": true + "enabled": false }, "dance.hack.tickets": { - "enabled": true + "enabled": false }, "dance.hack.linear": { - "enabled": true + "enabled": false } }, "gateway": { diff --git a/AGENTS.md b/AGENTS.md index 360e8bfe..7b38fe91 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,9 +55,13 @@ Never use any types and always default to leveraging generics and smart types to ## Verification Guardrails +- Docs currency is non-negotiable: ANY interface or behavior change (commands, flags, config keys, file layouts, env vars, defaults) must update the affected docs/ pages in the same patch — no matter what. +- The CLI reference is generated: after changing the CLI surface, run `bun run docs:cli-reference` and commit `docs/reference/cli.md` (a drift test fails otherwise). +- Agent-facing behavior phrasing lives in `src/agents/instruction-source.ts`; update it (not the generated surfaces) and run `hack setup sync --all-scopes`. - If a change affects `hack run`, `hack exec`, env resolution, runtime-state reconciliation, or lifecycle shell/process semantics, the patch must include both targeted tests and matching docs updates. - For env-sensitive command changes, verify the requested env, effective env, cached runtime-state env, and target-service-running matrix instead of a single happy path. - For lifecycle changes, verify `sh -c` semantics, process-group cleanup, stale pane/process metadata reconciliation, and interactive stdin behavior. +- Real end-to-end coverage lives in `tests/e2e/` (`bun run test:e2e:local`, docker tier via `test:e2e:local:docker`) — extend it when adding user-facing workflows. - When a semantic contract changes, update the closest durable doc or skill instruction in the same patch so future work starts from the current rules. ## Command Complexity @@ -66,33 +70,6 @@ Never use any types and always default to leveraging generics and smart types to - Before adding new branch-heavy logic there, prefer extracting a small helper with a narrow contract and direct tests. - Do not grow top-level command handlers when the real change is a decision table, state transition, or reusable readiness check. - -## Tickets (git-backed) - -This project uses `hack` tickets (extension: `dance.hack.tickets`). - -Common commands: -- Create: `hack tickets create --title "..." --body-stdin [--depends-on "T-00001"] [--blocks "T-00002"]` -- List: `hack tickets list` -- Tui: `hack tickets tui` -- Show: `hack tickets show T-00001` -- Update: `hack tickets update T-00001 [--title "..."] [--body "..."] [--depends-on "..."] [--blocks "..."]` -- Status: `hack tickets status T-00001 in_progress` -- Sync: `hack tickets sync` - -Recommended body template (Markdown): -```md -## Context -## Goals -## Notes -## Links -``` - -Tip: use `--body-stdin` for multi-line markdown. - -Data lives in `.hack/tickets/` (gitignored on the main branch) and syncs to hidden ref `refs/hack/tickets` by default. - - ## Landing the Plane (Session Completion) **When ending a work session**, you MUST complete ALL steps below. @@ -230,17 +207,19 @@ Most formatting and common issues are automatically fixed by Biome. Run `bun x u ## hack CLI (local dev + MCP) -Use `hack` as the single interface for local-first runtime orchestration (compose, DNS/TLS, logs, env, persistent project workspaces, and optional local tickets). +Use `hack` as the single interface for local-first runtime orchestration (compose, DNS/TLS, logs, env, and persistent project workspaces). Product boundary: -- Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, daemon, and optional local tickets. +- Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. - Removed surfaces: hosted auth/account/org/team flows, web dashboard, built-in GitHub workflows, and built-in Linear sync. -- Unsupported experimental: remote/gateway/node/dispatch. Do not use these as the default path unless explicitly requested. +- Experimental and unsupported: remote/gateway/node/dispatch commands. They are hidden from default help (list with `hack help --all`) and warn on use; do not use them unless explicitly requested. Operating rules: - Prefer `hack` over raw `docker` / `docker compose` for project workflows. - Do not start/stop services from Docker Desktop UI for `hack`-managed projects. - Treat `.hack/.internal` and `.hack/.branch` as hack-managed artifacts; do not hand-edit generated files there. +- Use `--json` for machine-readable output when available; `hack up/down/restart/doctor --json` emit an `{ok, data | error: {code, message}}` envelope with stable E_* error codes. +- Scripted/agent runs: pass `--no-interactive` (or set `HACK_NO_INTERACTIVE=1`) so commands never block on prompts — they apply documented defaults or fail fast with E_INTERACTIVE_REQUIRED. - Use MCP only when shell access is unavailable. - If runtime state looks wrong, run `hack doctor`, then `hack doctor --fix` before manual repair. @@ -273,10 +252,15 @@ Project files (managed vs generated): - Source-of-truth files: `.hack/docker-compose.yml`, `.hack/hack.config.json`, `.hack/hack.env.default.yaml`, and optional `.hack/hack.env..yaml`. - Worktree-local env override files: `.hack/hack.env.local.yaml` and `.hack/hack.env..local.yaml`. - Local-only files: `.hack.secret.key`, optional `.hack/.env` compatibility output, `.hack/.env.state.json`, and `.hack/.internal/` (runtime/local machine state; keep gitignored). -- Linked worktrees can inherit secret decryption through the git common dir; use `HACK_ENV_SECRET_KEY` in CI/managed containers. - Generated (do not hand-edit): `.hack/.internal/compose.override.yml`, `.hack/.internal/compose.env.override.yml`, `.hack/.branch/compose..override.yml`. - Managed via CLI: `.hack/.internal/extra-hosts.json` (use `hack internal extra-hosts ...` commands). - Lifecycle runtime files: `.hack/.internal/lifecycle/state.json`, `.hack/.internal/lifecycle/*.log`. +- Ignore rules: hack owns a committed `.hack/.gitignore` (self-healing on init/up) covering machine-local generated files (`.internal/`, `.branch/`, `.env`, `.env.state.json`, `hack.env*.local.yaml`); keep it committed, and if generated files leaked into git, `hack doctor --fix` untracks them (files stay on disk). + +Linked git worktrees: +- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments. +- `hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; opt out with config `worktree.auto_branch=false` or an explicit `--branch`. +- `hack doctor` flags divergent secret keys and dev_host collisions across checkouts. Advanced networking (extra_hosts + local proxies/tunnels): - Static host mappings: set `internal.extra_hosts` in `.hack/hack.config.json`. @@ -292,6 +276,13 @@ Standard workflow: - Restart: `hack restart` - Stop services: `hack down` +Running things (decision guide): +- One-off command in a fresh service container (deps started as needed): `hack run `. +- Command inside an already-running service container: `hack exec -- `. +- Host script that needs hack-stored env: `hack host exec --env --scope -- ` — this is THE way to run repo scripts; never read .env files directly. +- Interactive host shell with injected env: `hack host shell --env --scope `. +- Call a service over HTTP (from the host or between containers): use its Caddy hostname `https://.`; discover routable URLs with `hack open --json`. + Logs (default is compose): - Fast tail: `hack logs --pretty` - Per-service tail: `hack logs ` @@ -323,28 +314,10 @@ Host-side env helpers: - Interactive host shell with injected env: `hack host shell --env qa --scope api` - Run inside an already-running service container: `hack exec api -- bun test` -Tickets (git-backed): -- Create: `hack tickets create --title "..." --body-stdin` -- List/show: `hack tickets list`, `hack tickets show T-AB12CD34EF` -- Status/sync: `hack tickets status T-AB12CD34EF in_progress`, `hack tickets sync` - -Global infra: -- Bootstrap once: `hack global install` -- Start/stop/status: `hack global up`, `hack global down`, `hack global status` -- Use `hack global up` before Loki/Grafana queries if global logging is offline. - -Unsupported experimental remote nodes + dispatch: -- These commands are source-available but outside the supported v3 product contract. -- Pair/register nodes: `hack node pair ...`, then verify with `hack node list` and `hack node status --watch`. -- Repair SSH for remote Git/mutagen: `hack node ssh setup --node `. -- On node host, inspect workspace map via `hack node workspace list|resolve|attach|remove`. -- Inspect/repair controller-side route bridge with `hack node routes status` and `hack node routes repair`. -- Dispatch remote commands: `hack dispatch run --project --node default --branch --runner generic -- ""`. - -When to use a branch instance: -- You need two versions running at once (PR review, experiments, migrations). -- You want to keep a stable environment while testing another branch. -- Use `--branch ` on `hack up/open/logs/down` to target it. +Branch instances (parallel envs): +- Use a branch instance when you need two versions running at once (PR review, experiments, migrations) or want to keep a stable environment while testing another branch. +- Target one with `--branch ` on up/open/logs/down (for example: `hack up --branch --detach`). +- Linked worktrees pick a branch instance automatically (see Linked git worktrees). Run commands inside services: - One-off: `hack run ` (uses `docker compose run --rm`) @@ -357,6 +330,11 @@ Project targeting: - Else use `--project ` (registry) or `--path `. - List projects: `hack projects --json` +Global infra: +- Bootstrap once: `hack global install` +- Start/stop/status: `hack global up`, `hack global down`, `hack global status` +- Use `hack global up` before Loki/Grafana queries if global logging is offline. + Daemon (optional): - Start for faster JSON status/ps: `hack daemon start` - Check status: `hack daemon status` @@ -372,17 +350,21 @@ Agent integration maintenance: - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` - After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). Agent setup (CLI-first): - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Tickets skill: `hack setup tickets` - Refresh all local agent integrations: `hack setup sync --all-scopes` +- Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` - MCP (no-shell only): `hack setup mcp` - MCP install (explicit): `hack mcp install --all --scope project` + +Optional extensions: +- A local git-backed tickets extension exists (`hack tickets`) — only use it when the project explicitly uses it. ## Learned Workspace Facts diff --git a/CLAUDE.md b/CLAUDE.md index c600ca85..73cedaea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,32 +38,12 @@ TypeScript (strict). Runtimes: Bun 1.3+, Node 23. Prettier: 2 spaces, no semicol Always default to useing named paramaters in functions eg myFunction({ ctx, other }) vs myFunction(ctx, other) Never use any types and always default to leveraging generics and smart types to sensure the best possible tpye inference across the project. - -## Tickets (git-backed) - -This project uses `hack` tickets (extension: `dance.hack.tickets`). - -Common commands: -- Create: `hack tickets create --title "..." --body-stdin [--depends-on "T-00001"] [--blocks "T-00002"]` -- List: `hack tickets list` -- Tui: `hack tickets tui` -- Show: `hack tickets show T-00001` -- Update: `hack tickets update T-00001 [--title "..."] [--body "..."] [--depends-on "..."] [--blocks "..."]` -- Status: `hack tickets status T-00001 in_progress` -- Sync: `hack tickets sync` - -Recommended body template (Markdown): -```md -## Context -## Goals -## Notes -## Links -``` - -Tip: use `--body-stdin` for multi-line markdown. +## Docs Currency (non-negotiable) -Data lives in `.hack/tickets/` (gitignored on the main branch) and syncs to hidden ref `refs/hack/tickets` by default. - +- ANY interface or behavior change (commands, flags, config keys, file layouts, env vars, defaults) must update the affected docs/ pages in the same patch. +- The CLI reference is generated: after changing the CLI surface, run `bun run docs:cli-reference` and commit `docs/reference/cli.md` (a drift test fails otherwise). +- Agent-facing behavior phrasing lives in `src/agents/instruction-source.ts`; update it (not the generated surfaces) and run `hack setup sync --all-scopes`. +- Real end-to-end coverage lives in `tests/e2e/` (`bun run test:e2e:local`) — extend it when adding user-facing workflows. ## Project Notes (Obsidian) @@ -97,12 +77,19 @@ This project uses Obsidian for project context, specs, research, and progress tr ## hack CLI (local dev + MCP) -Use `hack` as the single interface for local runtime orchestration (compose, DNS/TLS, logs, persistent project workspaces). +Use `hack` as the single interface for local-first runtime orchestration (compose, DNS/TLS, logs, env, and persistent project workspaces). + +Product boundary: +- Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. +- Removed surfaces: hosted auth/account/org/team flows, web dashboard, built-in GitHub workflows, and built-in Linear sync. +- Experimental and unsupported: remote/gateway/node/dispatch commands. They are hidden from default help (list with `hack help --all`) and warn on use; do not use them unless explicitly requested. Operating rules: - Prefer `hack` over raw `docker` / `docker compose` for project workflows. - Do not start/stop services from Docker Desktop UI for `hack`-managed projects. - Treat `.hack/.internal` and `.hack/.branch` as hack-managed artifacts; do not hand-edit generated files there. +- Use `--json` for machine-readable output when available; `hack up/down/restart/doctor --json` emit an `{ok, data | error: {code, message}}` envelope with stable E_* error codes. +- Scripted/agent runs: pass `--no-interactive` (or set `HACK_NO_INTERACTIVE=1`) so commands never block on prompts — they apply documented defaults or fail fast with E_INTERACTIVE_REQUIRED. - Use MCP only when shell access is unavailable. - If runtime state looks wrong, run `hack doctor`, then `hack doctor --fix` before manual repair. @@ -133,10 +120,17 @@ TLS + valid-hostname constraints: Project files (managed vs generated): - Source-of-truth files: `.hack/docker-compose.yml`, `.hack/hack.config.json`, `.hack/hack.env.default.yaml`, and optional `.hack/hack.env..yaml`. +- Worktree-local env override files: `.hack/hack.env.local.yaml` and `.hack/hack.env..local.yaml`. - Local-only files: `.hack.secret.key`, optional `.hack/.env` compatibility output, `.hack/.env.state.json`, and `.hack/.internal/` (runtime/local machine state; keep gitignored). - Generated (do not hand-edit): `.hack/.internal/compose.override.yml`, `.hack/.internal/compose.env.override.yml`, `.hack/.branch/compose..override.yml`. - Managed via CLI: `.hack/.internal/extra-hosts.json` (use `hack internal extra-hosts ...` commands). - Lifecycle runtime files: `.hack/.internal/lifecycle/state.json`, `.hack/.internal/lifecycle/*.log`. +- Ignore rules: hack owns a committed `.hack/.gitignore` (self-healing on init/up) covering machine-local generated files (`.internal/`, `.branch/`, `.env`, `.env.state.json`, `hack.env*.local.yaml`); keep it committed, and if generated files leaked into git, `hack doctor --fix` untracks them (files stay on disk). + +Linked git worktrees: +- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments. +- `hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; opt out with config `worktree.auto_branch=false` or an explicit `--branch`. +- `hack doctor` flags divergent secret keys and dev_host collisions across checkouts. Advanced networking (extra_hosts + local proxies/tunnels): - Static host mappings: set `internal.extra_hosts` in `.hack/hack.config.json`. @@ -152,6 +146,13 @@ Standard workflow: - Restart: `hack restart` - Stop services: `hack down` +Running things (decision guide): +- One-off command in a fresh service container (deps started as needed): `hack run `. +- Command inside an already-running service container: `hack exec -- `. +- Host script that needs hack-stored env: `hack host exec --env --scope -- ` — this is THE way to run repo scripts; never read .env files directly. +- Interactive host shell with injected env: `hack host shell --env --scope `. +- Call a service over HTTP (from the host or between containers): use its Caddy hostname `https://.`; discover routable URLs with `hack open --json`. + Logs (default is compose): - Fast tail: `hack logs --pretty` - Per-service tail: `hack logs ` @@ -163,6 +164,8 @@ Logs (default is compose): Lifecycle + startup: - Put host setup in `.hack/hack.config.json` under `startup`/`lifecycle` (not ad-hoc terminal tabs). - Use `lifecycle.up.before` for pre-start hooks and `lifecycle.processes` for long-running host tasks. +- For fixed-port host helpers such as SSM tunnels or local proxies, set `singleton.ports` and usually `onConflict: "adopt"` so Hack reuses a healthy existing listener instead of starting duplicate tunnel stacks. +- `singleton` is a listener guard, not process ownership transfer; adopted external processes are left running on `hack down`. - Inspect lifecycle status via `hack projects --details` and stream via `hack logs `. Workspaces (mux-managed, tmux-first by default): @@ -175,30 +178,16 @@ Workspaces (mux-managed, tmux-first by default): - Stop workspace: `hack session stop ` Host-side env helpers: -- One-off host command with injected env: `hack env exec --env qa --service api -- bun db:migrate` -- Interactive host shell with injected env: `hack env shell --env qa --service api` - -Tickets (git-backed): -- Create: `hack tickets create --title "..." --body-stdin` -- List/show: `hack tickets list`, `hack tickets show T-AB12CD34EF` -- Status/sync: `hack tickets status T-AB12CD34EF in_progress`, `hack tickets sync` - -Global infra: -- Bootstrap once: `hack global install` -- Start/stop/status: `hack global up`, `hack global down`, `hack global status` -- Use `hack global up` before Loki/Grafana queries if global logging is offline. - -Remote nodes + dispatch: -- Pair/register nodes: `hack node pair ...`, then verify with `hack node list` and `hack node status --watch`. -- Repair SSH for remote Git/mutagen: `hack node ssh setup --node `. -- On node host, inspect workspace map via `hack node workspace list|resolve|attach|remove`. -- Inspect/repair controller-side route bridge with `hack node routes status` and `hack node routes repair`. -- Dispatch remote commands: `hack dispatch run --project --node default --branch --runner generic -- ""`. +- One-off host command with injected env: `hack host exec --env qa --scope api -- bun db:migrate` +- Host commands default to a host-local env view; use `--target compose` when you explicitly want container-oriented addresses. +- `--scope` selects which env scope to inject; it does not move execution into that service container. +- Interactive host shell with injected env: `hack host shell --env qa --scope api` +- Run inside an already-running service container: `hack exec api -- bun test` -When to use a branch instance: -- You need two versions running at once (PR review, experiments, migrations). -- You want to keep a stable environment while testing another branch. -- Use `--branch ` on `hack up/open/logs/down` to target it. +Branch instances (parallel envs): +- Use a branch instance when you need two versions running at once (PR review, experiments, migrations) or want to keep a stable environment while testing another branch. +- Target one with `--branch ` on up/open/logs/down (for example: `hack up --branch --detach`). +- Linked worktrees pick a branch instance automatically (see Linked git worktrees). Run commands inside services: - One-off: `hack run ` (uses `docker compose run --rm`) @@ -211,6 +200,11 @@ Project targeting: - Else use `--project ` (registry) or `--path `. - List projects: `hack projects --json` +Global infra: +- Bootstrap once: `hack global install` +- Start/stop/status: `hack global up`, `hack global down`, `hack global status` +- Use `hack global up` before Loki/Grafana queries if global logging is offline. + Daemon (optional): - Start for faster JSON status/ps: `hack daemon start` - Check status: `hack daemon status` @@ -226,15 +220,19 @@ Agent integration maintenance: - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` - After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). Agent setup (CLI-first): - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Tickets skill: `hack setup tickets` - Refresh all local agent integrations: `hack setup sync --all-scopes` +- Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` - MCP (no-shell only): `hack setup mcp` - MCP install (explicit): `hack mcp install --all --scope project` + +Optional extensions: +- A local git-backed tickets extension exists (`hack tickets`) — only use it when the project explicitly uses it. diff --git a/README.md b/README.md index 4248b4bf..77823204 100644 --- a/README.md +++ b/README.md @@ -4,40 +4,97 @@ # Hack -Hack is a local-first developer runtime for repo environments. +One command runs the whole project — services, env, secrets, TLS, logs — at its own +local HTTPS URL. Every project on your machine, all at once, none of them colliding. -It gives each project a predictable local runtime, stable HTTPS hostnames, resolved environment -variables, persistent work sessions, diagnostics, and optional git-backed tickets without requiring a -hosted Hack service. - -## What Hack Does - -Hack is built for local development on machines that run many projects, branches, agents, and -supporting services at the same time. - -- Start and stop project runtimes with `hack up`, `hack down`, and `hack restart`. -- Open stable local HTTPS URLs like `https://myapp.hack` with `hack open`. -- Route service subdomains through local Caddy and trusted development TLS. -- Resolve env overlays and secrets from `.hack/` and inject them into compose, host commands, and sessions. -- Keep tmux-backed project workspaces alive with `hack session`. -- Diagnose Docker, DNS, TLS, env, lifecycle, and stale runtime state with `hack doctor`. -- Track optional repo-local work with `hack tickets`. -- Use a slim macOS companion for local project status, controls, logs, and quick actions. +```bash +cd any-repo && hack up +# → https://myapp.hack is live, TLS trusted, env injected +``` -Hack v3 is intentionally self-contained. The supported product is the local CLI/runtime and macOS -companion. Hosted auth, account/org/team management, the web dashboard, built-in GitHub workflows, -and built-in Linear sync are not part of the v3 product. +Local development breaks down when one machine runs many things. Two projects fight +over port 3000. A repo you haven't touched in a month needs an afternoon of +archaeology to start. Secrets live in `.env` files passed around in Slack. Testing a +PR means tearing down the branch you were working on. + +Hack's bet is that these aren't separate problems. Project isolation isn't port +management — it's the whole environment, isolated together, or it isn't isolation. +So the environment is the unit: a small committed config (`.hack/`) running on +shared local infrastructure — Docker Compose for services, Caddy for DNS and +trusted TLS. Nothing is hosted. Your machine is the platform. + +## What you get + +**A real URL for every project.** `https://myapp.hack`, with per-service subdomains +like `api.myapp.hack` and locally-trusted certificates. No ports to remember, no +collisions, and OAuth-friendly alias hosts when a provider rejects `.hack`. + +**`hack up` is the whole answer to "how do I run this repo".** Services, env, +lifecycle hooks, and host-side helpers (tunnels, SSO, proxies) are declared in +`.hack/` and committed. Anyone who clones the repo — teammate or agent — is one +command from a running environment. + +**Env that travels with the repo.** Values live in committed YAML; secrets are +encrypted per-value, so they're safe to commit. One gitignored key decrypts them — +linked worktrees inherit it automatically, CI passes `HACK_ENV_SECRET_KEY`. Scripts +get injected env through `hack host exec` instead of reading `.env` files. + +**Parallel everything.** Branch instances run side by side with their own URLs. In a +linked git worktree, `hack up` automatically becomes a separate instance named after +the branch — review a PR while your main checkout keeps running. + +**Built for agents.** `hack init --with claude|codex` hands setup to an agent. +Machine surfaces are first-class: `--json` envelopes with stable error codes, +`--no-interactive`, and agent instructions that sync themselves into +AGENTS.md/CLAUDE.md, Cursor rules, and Codex skills. + +## More than ports + +Hack gets compared to local-domain tools like portless. Portless names your ports. +Hack runs your project — the URL is just the front door. + +A project isn't running until *all* of it is running, and every slice beyond the +URL usually means another tool, another script, another README section: + +| The job | The usual duct tape | With hack | +| --- | --- | --- | +| Local domains + trusted TLS | portless, mkcert, `/etc/hosts` edits | `https://myapp.hack`, CA trusted once | +| Secrets & env | Doppler, `.env` files in Slack | encrypted values committed with the repo | +| "How do I run this" | README + Makefile + tribal knowledge | `.hack/` config + `hack up` | +| Tunnels, SSO, proxies | ad-hoc shell scripts in a terminal tab | declared lifecycle processes, cleaned up on `down` | +| Logs | terminal scrollback, gone on restart | persisted history: `hack logs --loki --since 2h` | +| Parallel branches | a second clone and port surgery | branch instances; worktrees isolate automatically | +| Workspaces | hand-rolled tmux setups | `hack session` | + +Each row is a tool you don't install or a script you don't maintain. And the +pieces compose because they share one model of the project: the same identity that +routes `api.myapp.hack` also decrypts its secrets, starts its tunnels, labels its +logs, and namespaces its branch instances. That's why none of it needs glue code. + +## Your environment, anywhere + +Remote coding environments — Codex, Claude Code, Cursor, your own sandboxes — all +stall on the same problem: someone has to hand-build the project's environment +inside the box. Services, env, secrets, startup order. It's manual, it drifts, and +it has to be redone for every project and every platform. + +A hack project has already done this work. The environment is committed with the +repo and isolated by design, so it runs the same on a teammate's laptop, in CI, or +inside an agent's sandbox. Install hack, pass `HACK_ENV_SECRET_KEY`, run `hack up`. +That is the entire setup — make a repo portable once and every environment, +human or machine, gets it for free. + +There's a slim container image (`hackdance/hack:slim`) built for exactly this; see +[managed environments](./docs/guides/codex-managed-environments.md). ## Install -Homebrew: - ```bash brew tap hack-dance/tap brew install hack-dance/tap/hack ``` -Shell installer: +Or without Homebrew: ```bash curl -fsSL \ @@ -45,184 +102,50 @@ curl -fsSL \ | bash ``` -Codex or managed container installer: - -```bash -curl -fsSL \ - https://github.com/hack-dance/hack/releases/latest/download/hack-codex-install.sh \ - | bash -``` - -## Quick Start - -Bootstrap the global local infrastructure once: - -```bash -hack global install -``` - -Initialize a repo: +## Quick start ```bash +hack global install # once per machine: DNS, TLS, proxy cd /path/to/project -hack init -``` - -Run it: - -```bash +hack init # or: hack init --with claude hack up --detach hack open -hack logs --pretty ``` -Stop it: - -```bash -hack down -``` - -If anything looks wrong, start with: +When something looks wrong: ```bash hack doctor hack doctor --fix ``` -## Daily Commands - -```bash -hack status -hack ps -hack restart -hack open -hack logs --pretty -hack logs -hack exec -- bun test -hack run -- bun db:migrate -``` - -Host-side commands can use the same resolved project env: - -```bash -hack host exec --scope api -- bun test -hack host shell --env qa --scope api -``` - -Persistent workspaces: +## Everyday commands ```bash -hack session -hack session start -hack session exec "bun test" -``` - -Optional repo-local tickets: - -```bash -hack tickets create --title "Fix lifecycle cleanup" -hack tickets list -hack tickets show T-00001 -hack tickets status T-00001 in_progress -``` - -## Env And Secrets - -Hack uses YAML env files in `.hack/` as the source of truth: - -```text -.hack/hack.env.default.yaml -.hack/hack.env..yaml -.hack/hack.env.local.yaml -.hack/hack.env..local.yaml -``` - -Shared files can be committed. `*.local.yaml` files are worktree-local overrides. - -Runtime commands read the YAML model directly. Use `hack env materialize` only when an external tool -needs a compatibility `.hack/.env` file on disk. - -Secret key lookup is local-first: - -1. current checkout `.hack.secret.key` -2. shared key under the git common dir for linked worktrees -3. `HACK_ENV_SECRET_KEY` - -Use `HACK_ENV_SECRET_KEY` in CI and managed containers instead of copying `.hack.secret.key` into an -image. - -Read more in [Env & secrets](./docs/env.md). - -## Lifecycle Processes - -Projects often need host-side setup before the compose stack starts: AWS SSO, SSM tunnels, local -proxies, database forwards, or one-off bootstrap commands. - -Put that work in `.hack/hack.config.json` under `lifecycle` or `startup` so Hack can run it -consistently during `hack up` and clean up the processes it owns during `hack down`. - -For fixed-port helpers, use `singleton.ports` and usually `onConflict: "adopt"` when an existing -healthy listener set should be reused instead of starting duplicate tunnel stacks. - -Read more in [Lifecycle](./docs/lifecycle.md). - -## Portable Containers - -Hack publishes runtime images to Docker Hub and GHCR. - -```bash -docker pull hackdance/hack:latest -docker pull hackdance/hack:slim +hack status # what's running +hack logs --pretty # tail logs (or: hack logs ) +hack exec api -- bun test # run inside a running service +hack run api -- bun db:migrate # one-off container command +hack host exec --scope api -- bun db:seed # host command with injected env +hack up --branch review # parallel branch instance +hack session start myapp # persistent tmux workspace +hack down ``` -Use `hackdance/hack:latest` when you want the fuller runtime image with Docker CLI, compose, and -remote-node support available. - -Use `hackdance/hack:slim` as a smaller base for Codex, CI, or managed containers where you want -`hack`, Bun, env resolution, sessions, and tickets without the full host stack. - -For reproducible remote or managed environments, install project dependencies normally, install Hack, -and pass `HACK_ENV_SECRET_KEY` at runtime so encrypted project env can be resolved. - -Start with [Codex managed environments](./docs/guides/codex-managed-environments.md) for a concrete -setup guide. - -## macOS Companion - -The macOS app is a thin local companion for Hack-managed projects. - -It provides: - -- project list and project detail -- global runtime and daemon status -- `up`, `down`, `restart`, and `open` actions -- log entrypoints and a Ghostty-backed bottom panel -- doctor and trust guidance -- menu bar quick actions - -The CLI remains the source of truth. The app is there to make local runtime state easier to see and -operate. - -## Documentation +Every command works from the repo root, or anywhere with `--project `. -Start here: +## Learn more -- [Core docs](./docs/core.md) -- [CLI reference](./docs/cli.md) -- [Initialize a project](./docs/guides/init-project.md) -- [Env & secrets](./docs/env.md) -- [Lifecycle](./docs/lifecycle.md) -- [Sessions](./docs/sessions.md) -- [Tickets](./docs/guides/tickets.md) -- [Portable Codex environments](./docs/guides/codex-managed-environments.md) -- [Architecture](./docs/architecture.md) +- [Docs index](./docs/README.md) — concepts, guides, and reference +- [CLI overview](./docs/cli.md) and the [generated command reference](./docs/reference/cli.md) +- [Env & secrets](./docs/env.md) · [Lifecycle](./docs/lifecycle.md) · [Sessions](./docs/sessions.md) +- [Agent-first setup](./docs/guides/agent-first-setup.md) -Reference and advanced material: +A slim macOS companion app shows project status and quick actions; the CLI stays the +source of truth. Optional extensions and container images are covered in the docs. +Remote/gateway/node/dispatch commands are source-available but unsupported +experimental — hidden behind `hack help --all`. -- [Docs index](./docs/README.md) -- [Extensions and reference](./docs/reference.md) -- [Integrations boundary](./docs/integrations.md) -- [Unsupported experimental beta workflows](./docs/beta.md) +## License -Remote, gateway, node, and dispatch commands remain source-available but unsupported experimental. -They are not required for the default local development path. +See [LICENSE](./LICENSE). diff --git a/docs/README.md b/docs/README.md index 2af7bcfb..f2f2c8b4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,7 @@ # Docs -This directory contains the public documentation for `hack`. +Hack runs the whole project — services, env, secrets, TLS, logs — at its own local +HTTPS URL, for every project on your machine at once. These docs cover how. The docs are split into three areas so the default path stays on core local workflows. @@ -10,12 +11,13 @@ The docs are split into three areas so the default path stays on core local work Use this path for the main product story: -- local project setup -- isolated runtime orchestration +- local project setup (including agent-assisted onboarding via `hack init --with` / `hack agent onboard` — see the [agent-first setup guide](guides/agent-first-setup.md)) +- isolated runtime orchestration (including branch instances and linked worktrees) - stable local hostnames - env and secret management - lifecycle hooks - persistent sessions +- diagnostics (`hack doctor` / `hack doctor --fix`) Start here if you want to understand and use `hack` without remote or beta complexity by default. @@ -35,7 +37,7 @@ out of the default path. Use this path for: -- full command reference +- full command reference: [CLI overview](cli.md) plus the generated [CLI reference](reference/cli.md) (every command and flag) - extension configuration and authoring - tickets and integrations - gateway API and SDK details @@ -45,14 +47,17 @@ This section is easy to find, but it does not lead the product story. ## Quick links - New to `hack`: [Core docs](core.md) +- Setting up with a coding agent: [Agent-first setup](guides/agent-first-setup.md) +- Looking for command or API details: [Extensions & reference](reference.md), including the generated [CLI reference](reference/cli.md) - Need unsupported experimental remote workflows: [Beta workflows](beta.md) -- Looking for command or API details: [Extensions & reference](reference.md) - Need the bucket definitions: [Docs information architecture](docs-ia.md) ## Repo notes -- Specs remain in `SPECS/` as working notes +- Working notes and historical design docs live in `docs/plans/`; they are not the supported product contract - Root scripts orchestrate workspace tasks through Turbo - Package-local commands remain available via `bun run --cwd