hack-cli-hack-dance-hack

Use the hack CLI for local dev environments (docker compose, logs, run commands) and agent integration setup. Trigger when asked to start/stop services, open project URLs, inspect logs, run commands i

SKILL.md

GitHub

hack CLI

Use hack as the primary interface for local-first development.

Integration freshness

  • These instructions were generated by hack CLI v4.0.3; use the content revision and command behavior to assess freshness.
  • Check guidance only when Hack is relevant and there is evidence of drift; use targeted hack setup checks for a read-only inventory. Routine tasks do not require a startup audit.
  • Content revision: 38d5802c523f (version alone is not a freshness guarantee).

Product boundary

  • Supported CLI surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon.
  • Hosted auth/account/org/team flows, the web dashboard, and built-in GitHub/Linear integrations are retired. The retained macOS app is unsupported and is not distributed with the CLI.
  • 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.
  • A task request authorizes its stated scope, not unrelated service, global config, or production changes. Preserve native credential/trust approvals and destructive-cleanup gates; do not ask again for the same authorized action.
  • Cleanup must prove resource ownership. Successful one-shot containers may exit with code 0; adopted external processes must remain running when Hack stops its own services.
  • If runtime state looks wrong, inspect with hack doctor; apply hack doctor --fix only for repairs covered by the request. Warnings do not authorize broader changes.

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 writes: hack config get <path>, hack config set <path> <value>, hack config set --global <path> <value>

Hostname routing + Caddy labels

  • Primary host comes from dev_host (default: <project>.hack).
  • Subdomain pattern is <sub>.<dev_host> (for example: api.myapp.hack).
  • OAuth alias (when enabled) also routes <dev_host>.<tld> and <sub>.<dev_host>.<tld> (default tld: gy).
  • Browser launches automatically prefer a routed OAuth alias when enabled; custom development hosts outside .hack stay on the primary host. Set open.prefer or pass hack open --prefer <auto|alias|dev> to override.
  • 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 <sub>, hack open --json.

TLS + valid-hostname constraints

  • hack uses Caddy internal PKI for HTTPS on routed hosts; trust CA with hack global trust.
  • Containers get a combined public+local trust bundle (SSL_CERT_FILE etc.) once hack global trust has run; public TLS (package registries, external APIs) keeps working alongside *.hack trust.
  • If the combined bundle is missing, only Node gets *.hack trust (NODE_EXTRA_CA_CERTS); OpenSSL-based tools keep public roots — run hack global trust to enable both.
  • .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.

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.<overlay>.yaml.
  • Worktree-local env override files: .hack/hack.env.local.yaml and .hack/hack.env.<overlay>.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/.internal/compose.runtime.override.yml, .hack/.branch/compose.<branch>.override.yml, .hack/.branch/compose.<branch>.runtime.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) plus the retired tickets/ cache path for upgrade safety; 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; a detached linked worktree requires an explicit --branch, unless config worktree.auto_branch=false explicitly opts into the base instance.
  • Before hack up or hack restart auto-targets a new branch instance, Hack warns when the same worktree already owns a non-terminal instance; pass --branch <name> to make the target explicit.
  • Implicit hack down retargets a uniquely owned same-checkout runtime after a Git branch rename, including Created and stopped containers; when multiple runtimes belong to the checkout, pass --branch <name> explicitly.
  • 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.
  • Dynamic host mappings: hack internal extra-hosts set <hostname> <target> / 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.

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 <service> <cmd...>.
  • Command inside an already-running service container: hack exec <service> -- <cmd...>.
  • Host script that needs managed env: hack host exec --env <overlay> --scope <service> -- <cmd...>. Inspect variable names and non-secret config; never expose secret values in prompts, command arguments, logs, or source control.
  • Interactive host shell with injected env: hack host shell --env <overlay> --scope <service>.
  • Browser/host URL: use hack open <service> --json (OAuth aliases are preferred when enabled). Container-to-container traffic should use Compose DNS.

Logs (default is compose)

  • Fast tail: hack logs --pretty
  • Per-service tail: hack logs <service>
  • Machine snapshot: hack logs --json --no-follow
  • Loki history/query: hack logs --loki --since 2h --pretty or hack logs --loki --query '{project="<name>"}'
  • Force compose backend: hack logs --compose
  • Global infra logs: hack global logs caddy --no-follow --tail 200

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.
  • Lifecycle hooks and processes receive the selected overlay's global values plus host overrides; service-scoped values remain container-specific unless a host command explicitly selects that scope.
  • 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 <service-or-process>.
  • Lifecycle session recovery is ownership-proven: Hack adopts healthy token-, definition-, and environment-matched sessions, replaces owned stale sessions, and refuses to kill same-name sessions without deterministic ownership proof.
  • hack doctor --fix reaps an orphan lifecycle session only when mux ownership is proven and its Compose instance is absent; unverified same-name sessions are never modified.
  • After hack up or hack restart, running services and successful one-shot services (exited with code 0) count as successful. Hack recognizes dependency installers, hack.service.one-shot=true, and services referenced by Compose condition: service_completed_successfully; other states return E_STARTUP_INCOMPLETE, and hack doctor warns about containers stuck in Created.
  • Detached startup is bounded; a hung Compose operation returns E_STARTUP_TIMEOUT, terminates its process group, and hack doctor --fix can start exact containers left in Created.
  • Target only affected services with hack up <service...> --detach, hack restart <service...>, or hack env apply --service <service>; scoped operations skip project lifecycle hooks and implicit dependency startup.
  • Use hack env explain <KEY> --env <overlay> --service <service> --target <host|compose> for redacted source, precedence, availability, and delivery diagnostics.
  • Dependency installer services are detected generically by command or hack.dependencies.bootstrap=true; registry env references are preflighted before container mutation. Optional hack.dependencies.cache-volume, hack.dependencies.lockfiles, and hack.dependencies.runtime-files labels enable lockfile/runtime-keyed volumes shared across compatible worktrees.
  • hack down --prune-caches can remove only confirmed Compose-owned named volumes mounted exclusively at .next destinations or explicitly labeled hack.cache.disposable=true; it is confirmation-gated, requires --yes for JSON/scripted runs, and never performs broad volume pruning.

Workspaces (mux-managed, tmux-first by default)

  • Picker: hack session for persistent project workspaces.
  • Reuse/create: hack session start <project>
  • Env-scoped workspace: hack session start <project> --env qa --service api --detach
  • Force isolated agent workspace: hack session start <project> --new --name agent-1 (<project>--agent-1).
  • Execute in workspace: hack session exec <workspace> "<command>"
  • Execute in workspace with injected env: hack session exec <workspace> --env qa --service api "bun db:migrate"
  • Stop workspace: hack session stop <workspace>

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 <name> on up/open/logs/down (for example: hack up --branch <name> --detach).
  • Linked worktrees pick a branch instance automatically (see Linked git worktrees).
  • Containers receive HACK_RUNTIME_METADATA plus HACK_DEV_URL, HACK_ALIAS_URL, and current-service URL fields derived from effective Caddy routes. Use Compose DNS for internal traffic and this metadata for browser-facing links, OAuth callbacks, and webhooks.

Project targeting

  • From repo root, commands use that project automatically.
  • Else use --project <name> (registry) or --path <repo-root>.
  • 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

Agent integration maintenance

  • Ordinary Hack commands, hack update, hack doctor --fix, and primer hooks never install plugins or mutate agent integration files.
  • Use the matching hack setup command only for the affected client and authorized project scope; add --global only when user-scope changes are authorized. Use --check for read-only inspection. Reserve hack setup sync --all-scopes for an explicitly requested full refresh.
  • Standalone setup and optional native plugins are alternative integration paths. A missing optional integration does not require installation. Verify the replacement loads before explicitly removing duplicate Hack-owned entries; preserve customizations.
  • CLI upgrades do not authorize integration changes. Edit guidance at its generator source and render the affected targets; restart only if the client cannot reload the changed hooks or skills.

Agent setup (CLI-first)

  • Cursor rules: hack setup cursor
  • Claude hooks: hack setup claude
  • Codex skill: hack setup codex
  • 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: choose the requested client and scope with hack mcp install --help; do not configure other clients as a side effect.