Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/guides/agent-first-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,53 @@ never go stale on substance.
5. Verification loop — `hack up --json` → `hack ps --json` → `hack open --json`
→ curl or `hack logs`, iterating until `hack doctor` is clean.

## Partial adoption (backing services only)

Full containerization (every process in compose) is the default recommendation, but
it is not the only supported shape. hack works fine running only the backing
services — postgres, temporal, redis, and similar — while app dev servers (`bun
dev`, `dotnet watch`, `vite`) stay on the host.

**When to choose it:**
- Heavy native toolchains (.NET, large native builds) develop faster on the host
than in a container.
- The hot-path dev server (the one you restart constantly) benefits from host-native
file watching, debuggers, or hot reload that containers make slower or flakier.
- The team already has a working host-based dev workflow and containerizing it is
not worth the churn.

**What hack still gives you in this shape:**
- Stable hostnames and TLS for the backing services via Caddy (`postgres.<project>.hack`,
`temporal.<project>.hack`, ...), so host processes and containers address them the
same way.
- Branch instances for the containerized services (`--branch <name>`), even though
the host-run app processes are not branch-isolated on their own.
- Committed, hack-managed env (`hack env add`) for connection strings and secrets,
instead of hand-maintained `.env` files.
- Lifecycle hooks (`startup`/`lifecycle` in `.hack/hack.config.json`) for host-side
setup — tunnels, SSO bootstrap — that would otherwise be ad-hoc terminal steps.

**How to wire it:**
- Define only the backing services in `.hack/docker-compose.yml`; add Caddy labels
where a stable hostname is useful (databases usually don't need one — connect by
container port — but admin UIs, queues, or shared services often do).
- Give host dev servers their connection info via `hack host exec --scope <svc> --
<cmd>` (injects the resolved env without touching `.env` files) or `hack env list`
for a one-off lookup.
- App URLs can stay on plain `localhost:<port>` for the fast path, or get promoted
to a routed compose service later if you want a stable `*.hack` hostname for them
too — the two are not mutually exclusive and can be migrated incrementally.

**Tradeoffs vs full containerization:**
- No single `hack up` brings up the whole stack for a newcomer — they still need to
start the host dev servers themselves (document that step).
- Host-run app processes are not branch-isolated by hack; running two branches side
by side means varying ports/env per worktree yourself.

Partial adoption is a valid end state, not an incomplete migration — do not treat it
as something to "finish" into full containerization unless the tradeoffs above
actually bite.

## Copy-paste bootstrap

Paste this into a fresh Claude Code / Codex session started at the repo root:
Expand Down
38 changes: 38 additions & 0 deletions docs/guides/init-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,44 @@ Note:
- HTTP services via `https://*.hack` hostnames (matching your Caddy labels)
- non-HTTP services via Compose service hostnames (e.g. `db`, `redis`)

## What discovery checks (and what it can't)

`hack init` (interactive and `--auto`) discovers dev scripts across a repo/monorepo
and runs a validation pass over the results before writing `.hack/docker-compose.yml`.
It catches common scaffolding mistakes automatically, but it is not a substitute for
reviewing the generated compose file:

- **Duplicate/aggregator script dedupe**: when a package defines both `dev` and
`start` (or similar), only the best-scoring script becomes a service — no more
`web` + `web-2`. Root-level "aggregator" scripts that just delegate to a
workspace package's own dev script (e.g. `turbo run dev --filter=web`, or
`dotnet run --project apps/backend`) are also dropped in favor of the
package-local script. In the interactive wizard, the deduped set is
pre-selected by default — you can still add a dropped script back manually.
- **Port reassignment**: HTTP services that would collide on the same internal
port are deterministically reassigned to the next free port (ascending from
the collision), and the container command is rewritten to match. Each
reassignment is logged as a warning.
- **Runtime TODOs**: services whose dev script looks non-JS (`.csproj`/`.fsproj`,
`go.mod`, `Cargo.toml`, `pyproject.toml`/`requirements.txt`, `mix.exs`,
`Gemfile`, or a command like `dotnet run`/`go run`/`cargo`/`python`/`mix`) get an
obviously-wrong placeholder image (`alpine:3`) plus a `TODO(hack-init): ...`
comment above the service in the compose file — instead of silently getting the
default Bun/Node image. You must replace the image and command by hand.
- **Backing-service warnings**: dependencies (`pg`, `ioredis`, `@temporalio/*`,
`kafkajs`, `amqplib`, `mongodb`/`mongoose`, `prisma`, ...) and `.env`/`.env.example`
key names (never values) are scanned for signals of postgres, mysql, redis,
temporal, kafka, rabbitmq, or mongodb. These are **not** auto-scaffolded — they
show up as warnings and as a comment block at the top of the generated compose
file with a one-line suggestion each. If a `docker-compose*.yml`/`compose*.yml`
already exists at the repo root, it's flagged too ("treat it as ground truth for
backing services and images") instead of being re-derived from scratch.

None of this replaces reviewing the scaffold. Discovery is a best-effort heuristic
pass, not ground truth — always inventory the generated `.hack/docker-compose.yml`
against the real repo before running `hack up` (agents included; see the
onboarding prompt for the inventory-first review step).

When the local path is working and you intentionally want remote execution or gateway exposure, move
to [Beta workflows](../beta.md). For full command lookup and extension docs, use
[Extensions & reference](../reference.md).
4 changes: 3 additions & 1 deletion src/agents/onboarding-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function renderInventoryPhase(): string[] {
"- Read the root package.json, workspace globs, and lockfiles to identify the package manager and monorepo layout.",
"- Identify runnable services (web, api, workers): dev/start scripts, framework configs, and the ports they listen on.",
"- Detect databases, caches, and queues from code and config: ORM schemas (prisma/drizzle), migration dirs, existing Dockerfiles and compose files.",
"- List every `.env*` file. Their keys and values are candidates for `hack env add`; treat `.env.example` values as placeholders (ask the user for real values or leave them unset).",
"- List every `.env*` file. Their keys and values are candidates for `hack env add`; treat `.env.example` values as placeholders (ask the user for real values or leave them unset). Not every repo has `.env*` files — also harvest candidate keys from `environment:` blocks in existing compose files, `ENV` lines in Dockerfiles, and CI config (workflow env/secrets).",
"- Note repo scripts that need env vars to run (migrations, seeds, codegen) — they will run via `hack host exec` or an ops container later.",
"- Read README/docs for one-time setup steps you might otherwise miss.",
];
Expand All @@ -128,6 +128,8 @@ function renderSetupPhase(opts: {
"## Phase 2 — Set up hack",
"",
bootstrap,
"- Decide full containerization vs backing-services-only (partial adoption): if the inventory shows heavy native toolchains (.NET, large native builds) or the human prefers host dev servers, compose backing services (db/queue/cache) only and keep app dev servers on the host pointed at the routed services — ask the human when it's ambiguous; partial adoption is a valid end state, not a fallback to fix later.",
"- Check for reuse before authoring new services: if the repo already containerizes for prod (existing Dockerfiles/compose with build contexts, runtime-config injection points like an nginx-served runtime-config.js), prefer reusing those images/contexts with minimal glue over hand-authoring new dev-mode services from scratch.",
"- Define one compose service per runnable process in `.hack/docker-compose.yml`. HTTP services need the Caddy labels (`caddy`, `caddy.reverse_proxy`, `caddy.tls=internal`) and the `hack-dev` network to be routable.",
`- Design hostnames: the primary app answers on dev_host (${devHostExample}); every other routable service gets a subdomain (\`api.${devHostExample}\`, \`grafana.${devHostExample}\`, ...). Set dev_host in \`.hack/hack.config.json\`.`,
"- Migrate env values: for each key found during inventory run `hack env add <KEY> <value>` — add `--secret` for anything sensitive (API keys, tokens, passwords, connection strings with credentials). Never commit plaintext secrets to the repo.",
Expand Down
Loading
Loading