@haus-tech/haus-workflow 0.24.1 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.25.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.24.1...v0.25.0) (2026-06-12)
4
+
5
+ ### Features
6
+
7
+ - **cloneandsetup:** deterministic env phase, prereq gate, standalone needs, clone fallback ([#105](https://github.com/WeAreHausTech/haus-workflow/issues/105)) ([c6f5858](https://github.com/WeAreHausTech/haus-workflow/commit/c6f585850591eba48d08f7f6e2d8b1a2aa50c355))
8
+
3
9
  ## [0.24.1](https://github.com/WeAreHausTech/haus-workflow/compare/v0.24.0...v0.24.1) (2026-06-12)
4
10
 
5
11
  ### Bug Fixes
@@ -33,4 +33,7 @@ Clone a whole **workspace** from its manifest. Workspace-only (a `repos.manifest
33
33
  - **Cancel** — do nothing.
34
34
  5. Show the concrete plan before touching anything: list which repos will be cloned (and into which `folder`) and which will be reused/skipped. Get a final go-ahead.
35
35
  6. For each repo to clone, run (quoting it first): `haus clone <repo-url> <folder>` from the workspace root. Offer `--dry-run` first if the user wants a preview. If one repo fails, report it and continue to the next.
36
+
37
+ **Transport fallback.** Manifest URLs may be SSH (`git@github.com:org/repo.git` or `ssh://…`). Probe SSH connectivity once up front (`ssh -T git@github.com`); if it fails, **auto-fall back to the HTTPS URL** (`https://github.com/org/repo.git`) using your `gh auth` credentials, clone over HTTPS, and **report that you switched transport** so the user knows their SSH is down. Don't halt the run for an SSH outage when HTTPS works.
38
+
36
39
  7. After the loop, report which repos were cloned, reused (local), skipped (already present), and failed. Remind the user that installing dependencies and configuring each repo (`.env`, services) is still a manual step for now.
@@ -6,16 +6,23 @@ Clone a project's repos **and** set each one up for local development — node v
6
6
 
7
7
  Run the full `project:clone` flow by following `~/.claude/commands/haus-clone.md` end to end (name → one repo; no name → workspace repos from `repos.manifest.json`). Carry the resulting repo list into Step 2.
8
8
 
9
- ## Step 2 — Confirm the setup pass
9
+ ## Step 2 — Prerequisite gate (one consolidated check)
10
10
 
11
- 1. List the repos and what each will run (node, deps, localdev steps). Get a go-ahead. For **reused** local clones, ask whether to re-run setup.
12
- 2. Check `NODE_AUTH_TOKEN` is exported if any repo needs private `@`-scoped packages; if missing, tell the user — those installs fail without it.
11
+ Before any setup work, probe **everything** the workspace will need and surface **all** gaps in a single prompt — never discover them piecemeal mid-flow. For the repos being set up, check:
12
+
13
+ - **Auth tokens** — `NODE_AUTH_TOKEN` exported (private `@`-scoped npm packages fail without it); `~/.composer/auth.json` (or a repo-local `auth.json`) present if any repo has private composer deps.
14
+ - **Docker daemon** — running (`docker info`); required for any repo's `needs:` services.
15
+ - **PHP environment** — `valet`, `herd`, `ddev`, or `php` on PATH, if any repo is a PHP/WordPress site.
16
+ - **WP-CLI** — `wp --version`, if any repo's `seed:` pulls a WordPress DB.
17
+ - **Node versions** — the versions named in each repo's `.nvmrc` / `engines.node` are installable via `nvm`.
18
+
19
+ Then list the repos and what each will run (node, deps, localdev steps), **report every gap at once**, and get a single go-ahead. For **reused** local clones, ask whether to re-run setup. For each gap, name what it blocks; the user decides whether to fix it now (**ask before any global/system install**) or proceed and skip the affected steps. Don't begin per-repo work until this gate is acknowledged.
13
20
 
14
21
  ## Step 3 — Per-repo dependency pass
15
22
 
16
23
  For each repo, in its own directory, detect and install from the repo's own files (read its `docs/setup.md` / `CLAUDE.md` / `README.md` first — they win). Select node from `.nvmrc`/`engines.node` (`nvm install`), enable the pinned package manager (`corepack enable`), install JS deps (`yarn`/`pnpm`/`npm` by lockfile), composer deps if `composer.json` + `composer` present. Run each repo's steps in one login shell so the node version stays active. Per-repo failure is reported and skipped, not fatal.
17
24
 
18
- **Scaffold `.env`** so the env-wiring in Step 4 has a file to write to: if `.env.example` exists and `.env` does not, copy it; otherwise create an empty `.env`. Per decision D5, write `.env`; if the write is blocked, print the values for the user to add. Tell the user real secrets still need filling.
25
+ **Leave `.env` to Step 4 "Env".** The dependency pass installs only; the env phase writes each repo's `.env` deterministically once services, links, and values are in place.
19
26
 
20
27
  ## Step 4 — Local-dev orchestration
21
28
 
@@ -27,7 +34,7 @@ Specify the least. Infer the rest from the repo's stack + these conventions, and
27
34
 
28
35
  - **IMPORTANT — never install anything without asking first; global/system tools especially** (Homebrew, Docker, Laravel Herd, global `npm`/`composer` packages). Detect what's already present; if something required is missing, name it, say why it's needed, and get an explicit **yes** before installing. Prefer the least-invasive option, and don't switch or override tools the dev already has.
29
36
  - **PHP / WordPress sites are served by the developer's own PHP environment.** If they already have one — detect `valet`, `herd`, `ddev`, or `php` on PATH — **use it**: just satisfy the env contract (docroot → the repo's `web/`, HTTPS) and report the URL; never override what they already run. **Only when no local PHP environment exists** (a completely fresh machine) suggest installing **[Laravel Herd](https://herd.laravel.com)** as the default (asking first) and point its docroot at `web/` + `herd secure`.
30
- - **Databases and other services (a repo's `needs:`) run in Docker.** **If Docker isn't installed**, it's a prerequisite for these tell the user and **ask before installing it** (it's a global install). Once Docker is available, provision services the simplest way (a one-off `docker run`, or the repo's own compose if it ships one), then wire the matching env vars to point at them. Confirm before creating/overwriting data.
37
+ - **Databases and other services (a repo's `needs:`) run in Docker as standalone containers.** Bring each up with a **clean `docker run`** — image, host port, and env from the house convention for that service (e.g. `mysql` → `mysql:8` on `127.0.0.1:3306`, root password from generated secrets; `postgres` → `postgres:16` on `5432`). **Do not provision a `needs:` service from the repo's own `docker-compose.yml` when that compose bind-mounts repo-relative init files** (e.g. `./seed.sql`, `./docker-entrypoint-initdb.d/`): those mounts need files the guard won't let us create, and they conflate bring-up with seeding (which is the separate `seed:` step). **If Docker isn't installed** it's a prerequisite (surfaced in the Step 2 gate) **ask before installing it** (global install). **Port-conflict caveat:** if the conventional host port is already taken, pick the next free port, use it, and **record the chosen port in the workspace `localdev.yml` `env` map** so sink repos point at the right place. A `needs:` service comes up **empty** — populating it is always the separate `seed:` step.
31
38
  - **Dependencies** install from the lockfile (Step 3).
32
39
  - A repo's `localdev.yml` carries **only what can't be inferred** — its `needs`, repo-specific `build`/`seed` commands, env keys, and URL. Detect the stack (e.g. Bedrock = `composer.json` + `web/` docroot + `wp-cli.yml`; Vendure/Node = `docker-compose.yml` + `@vendure/*`) and apply the matching convention.
33
40
 
@@ -57,6 +64,8 @@ steps: # optional escape hatch: explicit ordered shell steps when intent isn't e
57
64
 
58
65
  **Workspace — `<workspace>/.haus-workflow/localdev.yml`** (the glue BETWEEN repos):
59
66
 
67
+ The workspace `env` map is the **single source of truth for cross-repo values** — DB names, ports, host URLs are chosen once and recorded here as literals. The env phase reads values **from here** and writes them into each repo's `.env`. Recording them here (not only in a repo's `.env`) means a later `seed:` / `db:pull` step can find them without depending on env-file load order.
68
+
60
69
  ```yaml
61
70
  order: [repo-a, repo-b] # setup/startup order, by manifest id
62
71
  links:
@@ -64,9 +73,10 @@ links:
64
73
  - { type: composer-path, in: <repo>, dep: <sibling-repo> }
65
74
  - { type: yarn-link, in: [<repo>, ...], dep: <sibling-package-repo> }
66
75
  env:
67
- - source: { repo: <repo>, provides: '<value>' }
76
+ - value: 'app_local' # a chosen literal — the recorded source of truth, OR …
77
+ # source: { repo: <repo>, provides: '<value>' } # … a value produced by another repo
68
78
  sinks:
69
- - { repo: <repo>, key: ENV_KEY }
79
+ - { repo: <repo>, key: DB_NAME } # written under `key` into each sink's .env
70
80
  ```
71
81
 
72
82
  ### Run order
@@ -74,23 +84,46 @@ env:
74
84
  1. **Discover** `.haus-workflow/localdev.yml` in the workspace root and each repo.
75
85
  2. **Resolve order** from the workspace `order` (repos not listed run last, in manifest order). No workspace file → manifest order.
76
86
  3. **Per repo, in order**, apply intent via the conventions:
77
- - **`needs`** → provision each service in Docker if not already running, and wire its env vars. Confirm before creating/overwriting data.
87
+ - **`needs`** → bring up each service as a **standalone `docker run`** (image/port/env from conventions; **not** the repo's compose when it bind-mounts repo-relative init files) if not already running. The service comes up **empty**; record its values in the workspace `localdev.yml` `env` map. No data is created here, so no overwrite prompt at this step — data lands in `seed:`.
78
88
  - **`build`** → run it (honor any node version the repo's docs note).
79
89
  - **`serve`** → for `via: herd` / PHP envs, install nothing — verify the dev's environment serves `web/`, and report the URL.
80
- - **`seed`** → run it; **confirm first** if it's remote (SSH) or destructive (overwrites data).
90
+ - **`seed`** → populate the empty datastore. **Always a distinct, confirm-gated step, separate from `needs:` bring-up** (don't conflate "DB up" with "DB has data"). Before running, **check its prerequisites and report any gap instead of running blind** — e.g. WP-CLI present (for `wp` / `db:pull` seeds), the target repo's **`.env` written** (the seed reads connection values from it — done in the Env step above), the **SSH alias resolves** (for remote pulls like `dep db:pull staging-oderland`). **Confirm first** for every remote (SSH) or destructive (overwrites data) seed — every run, even on re-run. Missing prerequisite → skip with a clear message, don't guess.
81
91
  - **`steps`** (escape hatch) → run in order, selecting `node:` per step, `optional:` failures continue, **confirming before any `remote:`/`destructive:` step** — every run, even on re-run.
82
92
  4. **Links** (workspace-owned, performed generically — do NOT call a repo's own `setup-dev-mode.sh`, which is deprecated):
83
93
  - `symlink` → `ln -s <from> <to>`; replace an existing symlink, but never clobber a real directory without confirmation.
84
94
  - `composer-path` → in `in`'s `composer.json`, set the `dep`'s require to `{ "type": "path", "url": "../<dep-folder>", "options": { "symlink": true } }`, then `composer update <vendor/dep>`.
85
95
  - `yarn-link` → `yarn link` in the `dep` repo, then `yarn link <pkg-name>` in each `in` repo (read `<pkg-name>` from the dep's `package.json`).
86
- 5. **Env:** for each workspace `env` entry, confirm the `source` is satisfiable, then upsert the value into each sink repo's `.env` under its `key`**creating `.env` if it does not exist** (Step 3 scaffolds it, but don't assume). **If the write is blocked or fails, print the exact `KEY=value` lines for the user to paste** (decision D5). Real secrets (DB passwords, tokens) remain the user's to fill.
96
+ 5. **Env (deterministic):** write each repo's `.env` from known valuesno improvising.
97
+ 1. **Compute** each repo's values, in this precedence: the workspace `localdev.yml` `env` map (chosen literals + cross-repo `source` values — the single source of truth) → generated secrets (DB passwords/tokens minted this run) → the **dev-defaults table** below for anything still unset.
98
+ 2. **Write** them into each repo's `.env` (create it if absent; **upsert** keys, never clobbering a value the user already set). These are local dev files — write them directly.
99
+ 3. **Real secrets the generator can't mint** (third-party API keys, prod credentials) go in as clearly-marked `KEY=` blanks for the user to fill; report exactly which keys are still blank.
100
+
101
+ **Dev-defaults table** — used only when neither `localdev.yml` nor a generated secret supplies the value:
102
+
103
+ | Key | Default |
104
+ | -------------------------------- | ------------------------------------------------------------------- |
105
+ | `DB_HOST` | `127.0.0.1` |
106
+ | `DB_PORT` | `3306` (mysql) / `5432` (postgres) — or the port chosen on conflict |
107
+ | `DB_USER` / `DB_PASSWORD` | `root` / a generated secret |
108
+ | `WP_HOME`, `WP_SITEURL`, `*_URL` | the repo's `serve.url` |
109
+ | `WP_ENV` / `APP_ENV` | `development` |
110
+
87
111
  6. **Report, then offer to start.** Give the per-repo summary, then **ask the user whether to start everything now.**
88
112
  - **Yes** → start each repo in the workspace `order` (its `serve.start`, e.g. `yarn dev`; bring up any remaining foreground services), run any follow-ups (e.g. `wp sync-products sync`), then **print the live URLs** (each repo's `serve.url`).
89
113
  - **No** → just print the ordered start commands + follow-ups as next steps; start nothing.
90
114
 
91
- **Default is "ready to run" (D2):** the preparation — datastores up (`docker compose up -d`), DBs pulled, links, builds, env wired — always happens. Starting the **foreground** dev servers and the initial product sync happens **only if the user says yes** to the start prompt above; otherwise they're printed, not run.
115
+ **Default is "ready to run" (D2):** the preparation — datastores up (standalone `docker run`), seeds applied (confirm-gated), links, builds, each repo's `.env` written — always happens. Starting the **foreground** dev servers and the initial product sync happen **only if the user says yes** to the start prompt above; otherwise they're printed, not run.
116
+
117
+ ## Step 5 — Report and define "done"
118
+
119
+ **"Done" is an explicit terminal state**, not "looks set up". The preparation reaches:
120
+
121
+ - datastores up (standalone containers),
122
+ - dependencies installed and builds green,
123
+ - links wired,
124
+ - each repo's `.env` written from known values.
92
125
 
93
- ## Step 5Report
126
+ From there, **live URLs are reachable only if** every required secret is satisfiable **and** the user starts the servers. If a secret can't be minted (a third-party key, a prod credential), say exactly which keys are still blank and what the user must fill don't imply links a missing secret will quietly break.
94
127
 
95
128
  Summarise per repo (node, deps, localdev steps, links, env). Then **ask whether to start everything now**:
96
129
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/haus-workflow",
3
- "version": "0.24.1",
3
+ "version": "0.25.0",
4
4
  "description": "Haus AI workflow CLI for Claude Code.",
5
5
  "type": "module",
6
6
  "bin": {