Skip to content

Commit 9dd469f

Browse files
authored
Merge pull request #58 from hack-dance/feat/init-discovery-validation
feat(init): discovery validation pass + field-report onboarding improvements
2 parents 2fbd8d7 + 739a581 commit 9dd469f

8 files changed

Lines changed: 1785 additions & 15 deletions

File tree

docs/guides/agent-first-setup.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,53 @@ never go stale on substance.
6464
5. Verification loop — `hack up --json``hack ps --json``hack open --json`
6565
→ curl or `hack logs`, iterating until `hack doctor` is clean.
6666

67+
## Partial adoption (backing services only)
68+
69+
Full containerization (every process in compose) is the default recommendation, but
70+
it is not the only supported shape. hack works fine running only the backing
71+
services — postgres, temporal, redis, and similar — while app dev servers (`bun
72+
dev`, `dotnet watch`, `vite`) stay on the host.
73+
74+
**When to choose it:**
75+
- Heavy native toolchains (.NET, large native builds) develop faster on the host
76+
than in a container.
77+
- The hot-path dev server (the one you restart constantly) benefits from host-native
78+
file watching, debuggers, or hot reload that containers make slower or flakier.
79+
- The team already has a working host-based dev workflow and containerizing it is
80+
not worth the churn.
81+
82+
**What hack still gives you in this shape:**
83+
- Stable hostnames and TLS for the backing services via Caddy (`postgres.<project>.hack`,
84+
`temporal.<project>.hack`, ...), so host processes and containers address them the
85+
same way.
86+
- Branch instances for the containerized services (`--branch <name>`), even though
87+
the host-run app processes are not branch-isolated on their own.
88+
- Committed, hack-managed env (`hack env add`) for connection strings and secrets,
89+
instead of hand-maintained `.env` files.
90+
- Lifecycle hooks (`startup`/`lifecycle` in `.hack/hack.config.json`) for host-side
91+
setup — tunnels, SSO bootstrap — that would otherwise be ad-hoc terminal steps.
92+
93+
**How to wire it:**
94+
- Define only the backing services in `.hack/docker-compose.yml`; add Caddy labels
95+
where a stable hostname is useful (databases usually don't need one — connect by
96+
container port — but admin UIs, queues, or shared services often do).
97+
- Give host dev servers their connection info via `hack host exec --scope <svc> --
98+
<cmd>` (injects the resolved env without touching `.env` files) or `hack env list`
99+
for a one-off lookup.
100+
- App URLs can stay on plain `localhost:<port>` for the fast path, or get promoted
101+
to a routed compose service later if you want a stable `*.hack` hostname for them
102+
too — the two are not mutually exclusive and can be migrated incrementally.
103+
104+
**Tradeoffs vs full containerization:**
105+
- No single `hack up` brings up the whole stack for a newcomer — they still need to
106+
start the host dev servers themselves (document that step).
107+
- Host-run app processes are not branch-isolated by hack; running two branches side
108+
by side means varying ports/env per worktree yourself.
109+
110+
Partial adoption is a valid end state, not an incomplete migration — do not treat it
111+
as something to "finish" into full containerization unless the tradeoffs above
112+
actually bite.
113+
67114
## Copy-paste bootstrap
68115

69116
Paste this into a fresh Claude Code / Codex session started at the repo root:

docs/guides/init-project.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,44 @@ Note:
2727
- HTTP services via `https://*.hack` hostnames (matching your Caddy labels)
2828
- non-HTTP services via Compose service hostnames (e.g. `db`, `redis`)
2929

30+
## What discovery checks (and what it can't)
31+
32+
`hack init` (interactive and `--auto`) discovers dev scripts across a repo/monorepo
33+
and runs a validation pass over the results before writing `.hack/docker-compose.yml`.
34+
It catches common scaffolding mistakes automatically, but it is not a substitute for
35+
reviewing the generated compose file:
36+
37+
- **Duplicate/aggregator script dedupe**: when a package defines both `dev` and
38+
`start` (or similar), only the best-scoring script becomes a service — no more
39+
`web` + `web-2`. Root-level "aggregator" scripts that just delegate to a
40+
workspace package's own dev script (e.g. `turbo run dev --filter=web`, or
41+
`dotnet run --project apps/backend`) are also dropped in favor of the
42+
package-local script. In the interactive wizard, the deduped set is
43+
pre-selected by default — you can still add a dropped script back manually.
44+
- **Port reassignment**: HTTP services that would collide on the same internal
45+
port are deterministically reassigned to the next free port (ascending from
46+
the collision), and the container command is rewritten to match. Each
47+
reassignment is logged as a warning.
48+
- **Runtime TODOs**: services whose dev script looks non-JS (`.csproj`/`.fsproj`,
49+
`go.mod`, `Cargo.toml`, `pyproject.toml`/`requirements.txt`, `mix.exs`,
50+
`Gemfile`, or a command like `dotnet run`/`go run`/`cargo`/`python`/`mix`) get an
51+
obviously-wrong placeholder image (`alpine:3`) plus a `TODO(hack-init): ...`
52+
comment above the service in the compose file — instead of silently getting the
53+
default Bun/Node image. You must replace the image and command by hand.
54+
- **Backing-service warnings**: dependencies (`pg`, `ioredis`, `@temporalio/*`,
55+
`kafkajs`, `amqplib`, `mongodb`/`mongoose`, `prisma`, ...) and `.env`/`.env.example`
56+
key names (never values) are scanned for signals of postgres, mysql, redis,
57+
temporal, kafka, rabbitmq, or mongodb. These are **not** auto-scaffolded — they
58+
show up as warnings and as a comment block at the top of the generated compose
59+
file with a one-line suggestion each. If a `docker-compose*.yml`/`compose*.yml`
60+
already exists at the repo root, it's flagged too ("treat it as ground truth for
61+
backing services and images") instead of being re-derived from scratch.
62+
63+
None of this replaces reviewing the scaffold. Discovery is a best-effort heuristic
64+
pass, not ground truth — always inventory the generated `.hack/docker-compose.yml`
65+
against the real repo before running `hack up` (agents included; see the
66+
onboarding prompt for the inventory-first review step).
67+
3068
When the local path is working and you intentionally want remote execution or gateway exposure, move
3169
to [Beta workflows](../beta.md). For full command lookup and extension docs, use
3270
[Extensions & reference](../reference.md).

src/agents/onboarding-prompt.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ function renderInventoryPhase(): string[] {
108108
"- Read the root package.json, workspace globs, and lockfiles to identify the package manager and monorepo layout.",
109109
"- Identify runnable services (web, api, workers): dev/start scripts, framework configs, and the ports they listen on.",
110110
"- Detect databases, caches, and queues from code and config: ORM schemas (prisma/drizzle), migration dirs, existing Dockerfiles and compose files.",
111-
"- 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).",
111+
"- 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).",
112112
"- Note repo scripts that need env vars to run (migrations, seeds, codegen) — they will run via `hack host exec` or an ops container later.",
113113
"- Read README/docs for one-time setup steps you might otherwise miss.",
114114
];
@@ -128,6 +128,8 @@ function renderSetupPhase(opts: {
128128
"## Phase 2 — Set up hack",
129129
"",
130130
bootstrap,
131+
"- 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.",
132+
"- 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.",
131133
"- 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.",
132134
`- 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\`.`,
133135
"- 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.",

0 commit comments

Comments
 (0)