voidforge-build 23.11.0 → 23.11.2

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.
Files changed (33) hide show
  1. package/dist/.claude/commands/git.md +36 -3
  2. package/dist/CHANGELOG.md +46 -0
  3. package/dist/VERSION.md +3 -1
  4. package/dist/docs/methods/RELEASE_MANAGER.md +26 -0
  5. package/dist/scripts/voidforge.d.ts +4 -2
  6. package/dist/scripts/voidforge.js +106 -17
  7. package/package.json +1 -1
  8. package/dist/wizard/lib/anomaly-detection.d.ts +0 -59
  9. package/dist/wizard/lib/anomaly-detection.js +0 -122
  10. package/dist/wizard/lib/asset-scanner.d.ts +0 -23
  11. package/dist/wizard/lib/asset-scanner.js +0 -107
  12. package/dist/wizard/lib/build-analytics.d.ts +0 -39
  13. package/dist/wizard/lib/build-analytics.js +0 -91
  14. package/dist/wizard/lib/codegen/erd-gen.d.ts +0 -16
  15. package/dist/wizard/lib/codegen/erd-gen.js +0 -98
  16. package/dist/wizard/lib/codegen/openapi-gen.d.ts +0 -15
  17. package/dist/wizard/lib/codegen/openapi-gen.js +0 -79
  18. package/dist/wizard/lib/codegen/prisma-types.d.ts +0 -15
  19. package/dist/wizard/lib/codegen/prisma-types.js +0 -44
  20. package/dist/wizard/lib/codegen/seed-gen.d.ts +0 -16
  21. package/dist/wizard/lib/codegen/seed-gen.js +0 -128
  22. package/dist/wizard/lib/correlation-engine.d.ts +0 -59
  23. package/dist/wizard/lib/correlation-engine.js +0 -152
  24. package/dist/wizard/lib/desktop-notify.d.ts +0 -27
  25. package/dist/wizard/lib/desktop-notify.js +0 -98
  26. package/dist/wizard/lib/image-gen.d.ts +0 -56
  27. package/dist/wizard/lib/image-gen.js +0 -159
  28. package/dist/wizard/lib/natural-language-deploy.d.ts +0 -30
  29. package/dist/wizard/lib/natural-language-deploy.js +0 -186
  30. package/dist/wizard/lib/route-optimizer.d.ts +0 -28
  31. package/dist/wizard/lib/route-optimizer.js +0 -93
  32. package/dist/wizard/lib/service-install.d.ts +0 -18
  33. package/dist/wizard/lib/service-install.js +0 -182
@@ -65,6 +65,14 @@ Stage and commit:
65
65
  4. Present the full commit message and staged file list to the user
66
66
  5. On user approval, execute the commit
67
67
 
68
+ ## Step 4.5 — Tag (Coulson)
69
+ Annotate the commit with the version tag (default-on; opt out via `--no-tag`):
70
+ 1. Run `git tag -a vX.Y.Z -m "vX.Y.Z: <one-line summary>"` against HEAD
71
+ 2. Verify: `git tag --list vX.Y.Z` returns the tag
72
+ 3. If a tag with that name already exists on a different commit, stop and surface the conflict — do not force-overwrite without user instruction.
73
+
74
+ Tags are local until pushed (Step 6). Why default-on: a release commit without a tag is invisible to `git describe`, GitHub releases, and most release tooling. **Field report: v23.10.0 and v23.11.0 shipped without tags in May 2026, blocking npm publish discovery downstream — exactly the kind of silent omission this step prevents.**
75
+
68
76
  ## Step 5 — Verify (Barton)
69
77
  Confirm everything is consistent:
70
78
  1. Run `git log -1 --format="%H %s"` — verify the commit exists and message is correct
@@ -73,6 +81,7 @@ Confirm everything is consistent:
73
81
  - `package.json` version matches
74
82
  - The **active changelog** (PROJECT_VERSION.md if present, else CHANGELOG.md) has an entry for this version
75
83
  - Commit message starts with the correct version tag
84
+ - `git tag --list vX.Y.Z` returns the tag (unless `--no-tag` was used)
76
85
  - **ROADMAP.md cross-check (field report #309 Fix 4):** if `ROADMAP.md` exists, grep it for the new version string. If milestones in ROADMAP.md reference a higher version than `package.json`, that's drift — surface it and offer to bump. If ROADMAP claims a milestone is "DONE" at a version that doesn't match the just-committed bump, surface that too. Drift between ROADMAP and package.json typically goes unnoticed for weeks.
77
86
  3. Run `git status` — verify working tree is clean (no forgotten files)
78
87
  4. If any inconsistency found, flag it and offer to fix
@@ -81,8 +90,28 @@ Confirm everything is consistent:
81
90
  Only if the user explicitly requests:
82
91
  1. Check remote: `git remote -v`
83
92
  2. Check if branch tracks upstream: `git status -sb`
84
- 3. Push: `git push`
85
- 4. Verify: `git log --oneline -1` matches remote
93
+ 3. Push branch: `git push`
94
+ 4. Push tags (if Step 4.5 ran): `git push origin vX.Y.Z` (or `git push --tags` if multiple new tags exist)
95
+ 5. Verify: `git log --oneline -1` matches remote, `git ls-remote --tags origin vX.Y.Z` shows the tag on remote
96
+
97
+ ## Step 7 — Publish to npm (Dockson) [--npm only]
98
+ Only runs when the user passes `--npm`. Publishing is irreversible (npm forbids re-using version numbers) and broadcasts to every consumer, so explicit opt-in is required.
99
+
100
+ **Read this first.** If `.github/workflows/publish.yml` (or equivalent) is configured to publish on tag push, the canonical publish path is the CI workflow — pushing the tag in Step 6 already triggers it. Use `--npm` when CI is unreachable, broken, or when you need a same-session publish without waiting for the workflow. Running both in parallel is safe (the workflow's "already published" check will skip the duplicate) but wastes a CI minute.
101
+
102
+ **Tag-push ordering caveat.** If you push multiple tags in one `git push --tags` call and CI publishes them in parallel, npm's `latest` dist-tag lands on whichever finished last — not the highest semver. After a multi-tag push, verify with `npm view <name> dist-tags` and run `npm dist-tag add <name>@vX.Y.Z latest` to repoint if needed. (This bit us when v23.10.0 + v23.11.0 were pushed together in May 2026.)
103
+
104
+ 1. **Preflight.** Run `npm whoami` — if not logged in, stop and tell the user to run `npm login`. Run `git status` — if working tree is dirty, stop (publishing from a dirty tree creates packages that don't match the tagged commit).
105
+ 2. **Discover publishable packages.** Build the list:
106
+ - If `package.json` at repo root has no `"private": true`, include it.
107
+ - If a workspaces array exists, walk each workspace package; include those without `"private": true`.
108
+ - For monorepos without workspaces (e.g., `packages/*/package.json`), glob and filter the same way.
109
+ - Skip any package whose `version` field doesn't match the version just committed in Step 4 — surface the mismatch instead of publishing inconsistent versions.
110
+ 3. **Confirm.** Print the list (`name@version`) and the registry (`npm config get registry`), then ask for confirmation. Include a note when `publishConfig.access` is `public` vs scoped.
111
+ 4. **Publish in dependency order.** If one package depends on another in the list, publish the dependency first. For VoidForge specifically: `voidforge-build-methodology` publishes before `voidforge-build`.
112
+ 5. **Run `npm publish` per package** from each package's own directory. Surface the tarball summary line (`+ name@version`) on success. On `EPUBLISHCONFLICT` (version already published), stop — the user needs to bump and re-run.
113
+ 6. **Verify.** For each published package, run `npm view <name> version` and confirm it returns the new version. There is sometimes a few-second registry propagation lag — retry once after 5s if mismatched.
114
+ 7. **Final summary.** Print which packages were published and at what version. This is the line that closes the release.
86
115
 
87
116
  ## Step 5.5 — Command↔Doc Sync Check (Friday)
88
117
  If any `docs/methods/*.md` file was modified, verify the paired `.claude/commands/*.md` file reflects the same additions:
@@ -106,9 +135,13 @@ If any `docs/methods/*.md` file was modified, verify the paired `.claude/command
106
135
  If a method doc gained a new section, flag, or checklist item — flag it: "Method doc X changed but command file Y may need matching update." The user decides whether the command file needs updating.
107
136
 
108
137
  ## Arguments
109
- - `--dry-run` → Show version bump, changelog entry, and commit message without executing.
138
+ - `--dry-run` → Show version bump, changelog entry, commit message, tag, and (if `--npm`) the package list that would publish — without executing any of it.
139
+ - `--major` / `--minor` / `--patch` → Override the Step 2 recommendation.
140
+ - `--no-tag` → Skip Step 4.5. Use sparingly; tagless release commits are the bug that produced this flag.
141
+ - `--npm` → Run Step 7 after push: publish every non-private package whose version matches the bump. Requires `npm whoami` to succeed. For monorepos, publishes all matching packages in dependency order.
110
142
 
111
143
  ## Handoffs
112
144
  - If changes include security fixes → note for Kenobi (`/sentinel`)
113
145
  - If changes include infrastructure → note for Kusanagi (`/devops`)
114
146
  - If version is MAJOR → recommend Picard review (`/architect`)
147
+ - If `--npm` was used → the release is now public on npm; any rollback requires a new patch version (npm forbids unpublishing within 72h for security)
package/dist/CHANGELOG.md CHANGED
@@ -6,6 +6,52 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
6
6
 
7
7
  ---
8
8
 
9
+ ## [23.11.2] - 2026-05-12
10
+
11
+ ### `voidforge init` mode prompt + methodology surfer-gate distribution
12
+
13
+ Two threads of polish: the CLI now asks before launching, and the Silver Surfer gate finally ships in the methodology npm package.
14
+
15
+ ### Added
16
+
17
+ - **`voidforge init` mode prompt.** With no flag, `init` now asks "Browser wizard or CLI (headless)?" instead of silently launching the browser server. Prompt appears only when stdin is a TTY.
18
+ - **`--browser` flag on `voidforge init`.** Explicit opt-in to the wizard UI — skips the prompt for users (or scripts) that want the prior default behavior.
19
+ - **Interactive headless init.** `voidforge init --headless` now prompts for project name (required), directory (defaulted to `~/Projects/<slug>`), and optional oneliner/domain/repo when `--name` is omitted in a TTY. Fully non-interactive when all flags are passed.
20
+ - **`packages/methodology/scripts/surfer-gate/`** (8 files) — the Silver Surfer PreToolUse hook scripts (`check.sh`, `record-roster.sh`, `bypass.sh`, `_paths.sh`, `validate.sh`, `test.sh`, `README.md`, `settings-snippet.json`) now ship via the `voidforge-build-methodology` npm package, closing the ADR-051 distribution gap (#317). Previously the scripts lived only at the repo root and never reached downstream projects via `npx voidforge-build update`.
21
+ - **9 pattern-table rows** in `packages/methodology/CLAUDE.md` — propagates the v23.11.0 pattern additions (adr-verification-gate, multi-tenant-property-test, multi-tenant-pool-bypass, rls-test-fixture, structural-sql-sentinel, audit-log, refactor-extraction, ai-prompt-safety, llm-state-dedup) into the methodology package copy so they reach downstream projects.
22
+
23
+ ### Changed
24
+
25
+ - **Non-TTY `voidforge init` without a flag** now exits with a clear error directing the user to `--browser` or `--headless`. Previously it silently launched a wizard server into a context where no browser would ever open — a latent bug.
26
+ - **Surfer Gate orchestrator-contract bash commands** in `packages/methodology/CLAUDE.md` are now wrapped in `[ -x ... ] && ... || true` existence guards with documented fallback ("if the script does not exist, your project predates v23.10.0; pull the gate or re-run `npx voidforge-build init`"). Lets the methodology cleanly cover both pre-#317 and post-#317 projects.
27
+
28
+ ### Why this exists
29
+
30
+ The init server-launch was the loudest "this CLI doesn't ask before doing things" surface in onboarding — first-run users got a server URL with no opportunity to choose CLI mode. Two flags (`--browser`, `--headless`) now span the choice; the bare command asks. Separately, the surfer-gate scripts had been documented as shipping in the methodology package since v23.11.0 changelog #317, but the scripts themselves were never copied into `packages/methodology/`. This release actually ships them.
31
+
32
+ ---
33
+
34
+ ## [23.11.1] - 2026-05-10
35
+
36
+ ### `/git` release-discipline patch — close the silent-release gap
37
+
38
+ v23.10.0 and v23.11.0 reached `origin/main` with bumped `package.json` versions but no git tags and no npm publish. The `publish.yml` workflow fires on `v*` tag push, so without tags the release pipeline never ran — both versions sat stranded for a full release cycle until a downstream `/void` returned nothing. Coulson now tags by default and exposes `--npm` for same-session manual publishing.
39
+
40
+ ### Added
41
+
42
+ - **`.claude/commands/git.md` — Step 4.5 (Tag).** Default-on. After commit, annotate HEAD with `git tag -a vX.Y.Z -m "<summary>"`. Skippable via `--no-tag`. Conflict detection on existing tags.
43
+ - **`.claude/commands/git.md` — Step 7 (Publish to npm).** Opt-in via `--npm`. Preflight (`npm whoami`, clean tree), discover non-private packages whose version matches the bump, confirm + publish in dependency order (methodology before voidforge-build for VoidForge specifically), verify via `npm view ... version` with one retry. Notes the `latest` dist-tag race when multiple tags are pushed in one batch.
44
+ - **`.claude/commands/git.md` — Push tags in Step 6.** Branch push now also pushes new tags, verified against `git ls-remote --tags origin`.
45
+ - **`.claude/commands/git.md` — Arguments.** `--no-tag` and `--npm` documented in the flags block; handoff note covers npm's 72h unpublish lockout.
46
+ - **`docs/methods/RELEASE_MANAGER.md` — `/git --npm` Flag section.** Mirrors the command-file spec: when CI is the canonical path, when `--npm` is the fallback, hard rules (no dirty publish, no `--force`, no `--ignore-scripts`, stop on `EPUBLISHCONFLICT`).
47
+ - **`docs/methods/RELEASE_MANAGER.md` — Verification Checklist.** Adds `git tag --list vX.Y.Z` and post-publish `npm view <name> version` checks.
48
+
49
+ ### Why this exists
50
+
51
+ Field-report context lives inline in both files so the lesson survives without an external citation. Tag step is default-on because tagless release commits are invisible to `git describe`, GitHub releases, and (critically) the tag-triggered publish workflow. Publish is opt-in because broadcast actions deserve a deliberate trigger, and the CI workflow remains the canonical path when reachable.
52
+
53
+ ---
54
+
9
55
  ## [23.11.0] - 2026-05-10
10
56
 
11
57
  ### Field Report Triage — 18 reports closed (#313–#320, #322–#330)
package/dist/VERSION.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Version
2
2
 
3
- **Current:** 23.11.0
3
+ **Current:** 23.11.2
4
4
 
5
5
  ## Versioning Scheme
6
6
 
@@ -14,6 +14,8 @@ This project uses [Semantic Versioning](https://semver.org/):
14
14
 
15
15
  | Version | Date | Summary |
16
16
  |---------|------|---------|
17
+ | 23.11.2 | 2026-05-12 | `voidforge init` now prompts browser-vs-CLI when no mode flag is passed (TTY only) and `--browser` was added for explicit opt-in. Headless init prompts for name/dir/oneliner/domain/repo when `--name` is omitted in a TTY. Non-TTY no-flag now errors cleanly instead of silently launching a wizard server. Separately: `packages/methodology/scripts/surfer-gate/` (8 files) now ships in the npm methodology package — closes ADR-051 distribution gap (#317). 9 pattern-table rows + existence-guarded orchestrator-contract bash propagated into the methodology CLAUDE.md. |
18
+ | 23.11.1 | 2026-05-10 | `/git` release-discipline patch — Step 4.5 (auto-tag, default-on) and Step 7 (`--npm` opt-in publish). Closes the silent-release gap that stranded v23.10.0 and v23.11.0 between GitHub and npm. Tag-push triggers the existing `publish.yml` workflow; `--npm` is a same-session fallback. Documents the `latest` dist-tag race when multiple tags are pushed simultaneously. RELEASE_MANAGER.md mirrored. |
17
19
  | 23.11.0 | 2026-05-10 | Field Report Triage — 18 reports closed (#313-#320, #322-#330). Two combined batches across multi-tenant retrofit campaigns (#313-#320) and autonomous-mode + AI-execution campaigns (#322-#330). 9 new patterns: adr-verification-gate.md, audit-log.ts, multi-tenant-{pool-bypass,property-test}.ts, rls-test-fixture.py, structural-sql-sentinel.py, refactor-extraction.md, ai-prompt-safety.ts, llm-state-dedup.ts. ai-eval.ts extended with Claude-prompt-eval template. middleware.ts extended with hot-path logging gate. 18 method-doc sections across CAMPAIGN, SYSTEMS_ARCHITECT, SECURITY_AUDITOR, QA_ENGINEER, SUB_AGENTS, BACKEND_ENGINEER, RELEASE_MANAGER, GAUNTLET, AI_INTELLIGENCE, DEVOPS_ENGINEER, FORGE_KEEPER, TESTING, TIME_VAULT. Surfer Gate now ships via npm methodology package + wizard project-init (closes ADR-051 distribution gap #317). Operational learnings added to Picard, Sisko, Coulson, Bashir, Loki, Irulan, Silver Surfer. |
18
20
  | 23.10.0 | 2026-04-20 | Field Report Triage — 6 reports closed (#303-#308). 33 approved fixes: SPEC_HANDOFF.md (new method doc), deploy-preflight.ts + post-deploy-probe.sh (new patterns), LEARNINGS LRN-5..10, Deploy Surface Boundary + Deployment Hygiene (field report #305 remediation), Step 2.5 pre-deploy secret scan + Step 4.5 post-deploy probe, TECH_DEBT SLA, npm-name pre-flight, ADR-050 Rename Verification Checklist, Silver Surfer HARD CONSTRAINT + 4 agent operational learnings. |
19
21
  | 23.9.2 | 2026-04-20 | CI workflow idempotency + provenance baseline — `publish.yml` guards each publish with "already-published" check so re-runs skip cleanly. Tag-push re-publishes via CI to attach npm provenance attestation (absent on v23.9.1's manual publish). |
@@ -116,8 +116,10 @@ After every commit, Barton verifies:
116
116
  - [ ] `VERSION.md` history table has new row with correct date
117
117
  - [ ] `package.json` "version" field matches
118
118
  - [ ] `CHANGELOG.md` has `[X.Y.Z]` section with correct date
119
+ - [ ] `git tag --list vX.Y.Z` returns the tag (unless `--no-tag`)
119
120
  - [ ] `git status` shows clean working tree
120
121
  - [ ] No untracked files that should have been included
122
+ - [ ] If `--npm` was used: every published package returns the new version from `npm view <name> version`
121
123
 
122
124
  ## CLAUDE.md Command Table Integrity Check
123
125
 
@@ -136,6 +138,30 @@ When the user passes `--deploy` to `/git`, run `/deploy` automatically after the
136
138
 
137
139
  This enables one-command commit-and-deploy for ad-hoc changes outside of campaigns.
138
140
 
141
+ ## `/git --npm` Flag
142
+
143
+ When the user passes `--npm` to `/git`, run npm publish after the commit + tag + push succeeds. Publishing is irreversible (npm forbids re-using version numbers; unpublish blocked within 72h) — explicit opt-in is required.
144
+
145
+ **Why this flag exists.** VoidForge distributes via npm. Before this flag existed, Coulson's workflow ended at `git push`, leaving every release stranded between GitHub and the registry. Field report: v23.10.0 and v23.11.0 were committed, tagged with version strings in `package.json`, and pushed to origin/main — but never published. Downstream consumers running `npx voidforge-build update` saw nothing new for two release cycles until the gap was caught manually. Tagging defaults to on (Step 4.5); npm publish is opt-in because broadcast actions deserve a deliberate trigger.
146
+
147
+ **Procedure (Dockson handles the publish; Coulson orchestrates):**
148
+
149
+ 1. **Preflight.** `npm whoami` must succeed. Working tree must be clean. Tag must exist (Step 4.5 result).
150
+ 2. **Discover.** Enumerate publishable packages — root `package.json` and any workspace/`packages/*` packages that don't have `"private": true`. Skip any whose version field doesn't match the version just bumped.
151
+ 3. **Confirm.** Print the list (`name@version`) and registry, ask for go-ahead.
152
+ 4. **Order.** Resolve internal dependencies — if package B depends on package A inside the same monorepo, publish A first. For VoidForge: `voidforge-build-methodology` before `voidforge-build`.
153
+ 5. **Publish.** Run `npm publish` from each package's own directory. Capture the `+ name@version` line.
154
+ 6. **Verify.** `npm view <name> version` must return the new version for each published package. Retry once after 5s on lag.
155
+ 7. **Report.** Final summary line: which packages shipped, at what version, to which registry.
156
+
157
+ **Hard rules:**
158
+
159
+ - Never publish from a dirty working tree.
160
+ - Never publish if `npm whoami` fails — surface the error and stop.
161
+ - Never `--force` or `--ignore-scripts`. If `prepack` fails, the package is broken; fix it.
162
+ - On `EPUBLISHCONFLICT` (version exists), stop. The user must bump and re-run; do not attempt to dist-tag around it.
163
+ - Scoped/private packages are skipped silently unless the user explicitly names them.
164
+
139
165
  ## Per-Commit CHANGELOG Discipline
140
166
 
141
167
  CHANGELOG drift accumulates silently when entries are deferred to session boundaries. By the time someone notices, the test count trajectory is wrong and the per-mission delta is unrecoverable from the diff alone.
@@ -3,8 +3,10 @@
3
3
  * VoidForge CLI — v21.0 The Extraction
4
4
  *
5
5
  * npx voidforge Launch wizard (browser UI at :3141)
6
- * npx voidforge init Create new project (Gandalf flow)
7
- * npx voidforge init --headless Create project without browser
6
+ * npx voidforge init Create new project prompts: browser or CLI
7
+ * npx voidforge init --browser Skip the prompt, go straight to the browser wizard
8
+ * npx voidforge init --headless Create project without browser (CLI prompts for name/dir)
9
+ * npx voidforge init --headless --name "X" Fully non-interactive CLI init
8
10
  * npx voidforge init --core Minimal methodology (no Holocron, no patterns)
9
11
  * npx voidforge update Update project methodology (Bombadil)
10
12
  * npx voidforge update --self Update the wizard itself
@@ -3,8 +3,10 @@
3
3
  * VoidForge CLI — v21.0 The Extraction
4
4
  *
5
5
  * npx voidforge Launch wizard (browser UI at :3141)
6
- * npx voidforge init Create new project (Gandalf flow)
7
- * npx voidforge init --headless Create project without browser
6
+ * npx voidforge init Create new project prompts: browser or CLI
7
+ * npx voidforge init --browser Skip the prompt, go straight to the browser wizard
8
+ * npx voidforge init --headless Create project without browser (CLI prompts for name/dir)
9
+ * npx voidforge init --headless --name "X" Fully non-interactive CLI init
8
10
  * npx voidforge init --core Minimal methodology (no Holocron, no patterns)
9
11
  * npx voidforge update Update project methodology (Bombadil)
10
12
  * npx voidforge update --self Update the wizard itself
@@ -167,6 +169,41 @@ async function launchWizard(mode = 'init') {
167
169
  if (!isRemote && !isLan)
168
170
  await openBrowser(url);
169
171
  }
172
+ function slugifyName(name) {
173
+ return name.replace(/[^a-zA-Z0-9-_ ]/g, '').replace(/\s+/g, '-').toLowerCase();
174
+ }
175
+ function defaultProjectDir(name) {
176
+ return join(process.env['HOME'] ?? process.env['USERPROFILE'] ?? '.', 'Projects', slugifyName(name));
177
+ }
178
+ async function prompt(question) {
179
+ const { createInterface } = await import('node:readline');
180
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
181
+ return new Promise((res) => {
182
+ rl.question(question, (answer) => { rl.close(); res(answer.trim()); });
183
+ });
184
+ }
185
+ /**
186
+ * Asks the user to pick browser vs headless mode for `voidforge init`.
187
+ * Requires a TTY — caller must guard with process.stdin.isTTY.
188
+ */
189
+ async function promptInitMode() {
190
+ console.log('');
191
+ console.log(' VoidForge — init');
192
+ console.log('');
193
+ console.log(' How would you like to set up this project?');
194
+ console.log(' 1) Browser wizard (Gandalf) — interactive, guided UI');
195
+ console.log(' 2) CLI (headless) — prompts here in the terminal');
196
+ console.log('');
197
+ // Loop until we get a valid answer.
198
+ for (;;) {
199
+ const answer = (await prompt(' Choice [1]: ')).toLowerCase();
200
+ if (answer === '' || answer === '1' || answer === 'b' || answer === 'browser')
201
+ return 'browser';
202
+ if (answer === '2' || answer === 'c' || answer === 'cli' || answer === 'h' || answer === 'headless')
203
+ return 'headless';
204
+ console.log(' Please enter 1 (browser) or 2 (CLI).');
205
+ }
206
+ }
170
207
  async function cmdInitHeadless() {
171
208
  const nameFlag = args.find((a, i) => args[i - 1] === '--name');
172
209
  const dirFlag = args.find((a, i) => args[i - 1] === '--dir');
@@ -174,25 +211,61 @@ async function cmdInitHeadless() {
174
211
  const domainFlag = args.find((a, i) => args[i - 1] === '--domain');
175
212
  const repoFlag = args.find((a, i) => args[i - 1] === '--repo');
176
213
  const isCore = args.includes('--core');
177
- if (!nameFlag) {
178
- console.error('Error: --name is required for headless init.');
179
- console.error('Usage: npx voidforge init --headless --name "My Project" [--dir path] [--oneliner "..."]');
180
- process.exit(1);
214
+ let name = nameFlag;
215
+ let directory = dirFlag;
216
+ let oneliner = onelinerFlag;
217
+ let domain = domainFlag;
218
+ let repoUrl = repoFlag;
219
+ // If --name is missing, prompt interactively (TTY only).
220
+ if (!name) {
221
+ if (!process.stdin.isTTY) {
222
+ console.error('Error: --name is required for headless init in non-interactive contexts.');
223
+ console.error('Usage: npx voidforge init --headless --name "My Project" [--dir path] [--oneliner "..."]');
224
+ process.exit(1);
225
+ }
226
+ console.log('');
227
+ console.log(' VoidForge — Headless Init');
228
+ console.log(' Press Enter to accept defaults shown in [brackets]. Optional fields can be left blank.');
229
+ console.log('');
230
+ while (!name) {
231
+ const answer = await prompt(' Project name: ');
232
+ if (answer)
233
+ name = answer;
234
+ else
235
+ console.log(' Project name is required.');
236
+ }
237
+ const defaultDir = defaultProjectDir(name);
238
+ if (!directory) {
239
+ const answer = await prompt(` Directory [${defaultDir}]: `);
240
+ directory = answer || defaultDir;
241
+ }
242
+ if (!oneliner) {
243
+ const answer = await prompt(' One-line description (optional): ');
244
+ oneliner = answer || undefined;
245
+ }
246
+ if (!domain) {
247
+ const answer = await prompt(' Domain (optional): ');
248
+ domain = answer || undefined;
249
+ }
250
+ if (!repoUrl) {
251
+ const answer = await prompt(' Repo URL (optional): ');
252
+ repoUrl = answer || undefined;
253
+ }
181
254
  }
182
- const defaultDir = join(process.env['HOME'] ?? process.env['USERPROFILE'] ?? '.', 'Projects', nameFlag.replace(/[^a-zA-Z0-9-_ ]/g, '').replace(/\s+/g, '-').toLowerCase());
183
- const directory = dirFlag ?? defaultDir;
255
+ if (!directory)
256
+ directory = defaultProjectDir(name);
184
257
  const { createProject } = await import('../wizard/lib/project-init.js');
185
258
  const result = await createProject({
186
- name: nameFlag,
259
+ name,
187
260
  directory,
188
- oneliner: onelinerFlag,
189
- domain: domainFlag,
190
- repoUrl: repoFlag,
261
+ oneliner,
262
+ domain,
263
+ repoUrl,
191
264
  core: isCore,
192
265
  });
193
266
  console.log('');
194
267
  console.log(` VoidForge — Project Created`);
195
- console.log(` Name: ${nameFlag}`);
268
+ console.log(` Name: ${name}`);
196
269
  console.log(` Directory: ${result.projectDir}`);
197
270
  console.log(` Files: ${result.filesCreated} methodology files`);
198
271
  console.log(` Marker: ${result.markerId}`);
@@ -293,7 +366,8 @@ function showHelp() {
293
366
  console.log(' --help, -h Show this help');
294
367
  console.log(' --remote Launch in remote mode (0.0.0.0 + auth)');
295
368
  console.log(' --lan Launch in LAN mode');
296
- console.log(' --headless CLI-only (no browser)');
369
+ console.log(' --browser init: skip the mode prompt and launch the browser wizard');
370
+ console.log(' --headless init: CLI-only flow (prompts for name/dir if not passed)');
297
371
  console.log(' --self Deploy VoidForge itself');
298
372
  console.log(' --env-only Write vault credentials to .env');
299
373
  console.log('');
@@ -536,14 +610,29 @@ async function main() {
536
610
  console.log(' To rollback: restore from the backup directory.\n');
537
611
  break;
538
612
  }
539
- case 'init':
540
- if (args.includes('--headless')) {
613
+ case 'init': {
614
+ const wantsHeadless = args.includes('--headless');
615
+ const wantsBrowser = args.includes('--browser');
616
+ if (wantsHeadless) {
541
617
  await cmdInitHeadless();
542
618
  }
543
- else {
619
+ else if (wantsBrowser) {
544
620
  await launchWizard('init');
545
621
  }
622
+ else if (process.stdin.isTTY) {
623
+ const mode = await promptInitMode();
624
+ if (mode === 'headless')
625
+ await cmdInitHeadless();
626
+ else
627
+ await launchWizard('init');
628
+ }
629
+ else {
630
+ console.error('Error: `voidforge init` was run in a non-interactive context without a mode flag.');
631
+ console.error('Pass --browser to launch the wizard UI, or --headless [--name "..."] for CLI init.');
632
+ process.exit(1);
633
+ }
546
634
  break;
635
+ }
547
636
  case 'heartbeat': {
548
637
  const subCmd = args[1]; // start | stop | status
549
638
  if (subCmd !== 'start') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voidforge-build",
3
- "version": "23.11.0",
3
+ "version": "23.11.2",
4
4
  "description": "From nothing, everything. A methodology framework for building with Claude Code.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,59 +0,0 @@
1
- /**
2
- * Anomaly Detection — Spend spikes, traffic drops, conversion changes (§9.17).
3
- *
4
- * Runs hourly as a heartbeat daemon scheduled job.
5
- * Compares current metrics against rolling averages.
6
- * Alerts when deviations exceed thresholds.
7
- *
8
- * PRD Reference: §9.7 (hourly anomaly detection), §9.17 (thresholds)
9
- */
10
- type Cents = number & {
11
- readonly __brand: 'Cents';
12
- };
13
- type AnomalyType = 'spend_spike' | 'traffic_drop' | 'conversion_change' | 'roas_drop';
14
- type AnomalySeverity = 'warning' | 'alert' | 'critical';
15
- interface Anomaly {
16
- type: AnomalyType;
17
- severity: AnomalySeverity;
18
- platform?: string;
19
- metric: string;
20
- currentValue: number;
21
- expectedValue: number;
22
- deviationPercent: number;
23
- message: string;
24
- timestamp: string;
25
- }
26
- declare const THRESHOLDS: {
27
- spendSpikeWarning: number;
28
- spendSpikeAlert: number;
29
- spendSpikeCritical: number;
30
- trafficDropWarning: number;
31
- trafficDropAlert: number;
32
- trafficDropCritical: number;
33
- conversionChangeThreshold: number;
34
- roasDropWarning: number;
35
- roasDropAlert: number;
36
- };
37
- /** Run all anomaly checks for the current period */
38
- export declare function runAnomalyDetection(metrics: {
39
- spendByPlatform: Array<{
40
- platform: string;
41
- currentHour: Cents;
42
- avgHourly: Cents;
43
- }>;
44
- traffic: {
45
- currentDay: number;
46
- avgDaily: number;
47
- };
48
- conversion: {
49
- currentRate: number;
50
- avgRate: number;
51
- };
52
- roasByPlatform: Array<{
53
- platform: string;
54
- current: number;
55
- avg: number;
56
- }>;
57
- }): Anomaly[];
58
- export type { Anomaly, AnomalyType, AnomalySeverity };
59
- export { THRESHOLDS };
@@ -1,122 +0,0 @@
1
- /**
2
- * Anomaly Detection — Spend spikes, traffic drops, conversion changes (§9.17).
3
- *
4
- * Runs hourly as a heartbeat daemon scheduled job.
5
- * Compares current metrics against rolling averages.
6
- * Alerts when deviations exceed thresholds.
7
- *
8
- * PRD Reference: §9.7 (hourly anomaly detection), §9.17 (thresholds)
9
- */
10
- // ── Thresholds ────────────────────────────────────────
11
- const THRESHOLDS = {
12
- // Spend spike: current hour spend > X% of daily average hourly spend
13
- spendSpikeWarning: 50, // 50% above average
14
- spendSpikeAlert: 100, // 100% above average (double)
15
- spendSpikeCritical: 200, // 200% above average (triple)
16
- // Traffic drop: current day traffic > X% below 7-day average
17
- trafficDropWarning: 20, // 20% below average
18
- trafficDropAlert: 40, // 40% below average
19
- trafficDropCritical: 60, // 60% below average
20
- // Conversion rate change: > X% from 7-day average
21
- conversionChangeThreshold: 20, // 20% change in either direction
22
- // ROAS drop: current < X% of 7-day average
23
- roasDropWarning: 20, // 20% below average
24
- roasDropAlert: 40, // 40% below average
25
- };
26
- // ── Detection Functions ───────────────────────────────
27
- function detectSpendSpike(currentHourSpend, avgHourlySpend, platform) {
28
- if (avgHourlySpend === 0)
29
- return null;
30
- const deviation = ((currentHourSpend - avgHourlySpend) / avgHourlySpend) * 100;
31
- if (deviation < THRESHOLDS.spendSpikeWarning)
32
- return null;
33
- const severity = deviation >= THRESHOLDS.spendSpikeCritical ? 'critical' :
34
- deviation >= THRESHOLDS.spendSpikeAlert ? 'alert' : 'warning';
35
- return {
36
- type: 'spend_spike',
37
- severity,
38
- platform,
39
- metric: 'hourly_spend',
40
- currentValue: currentHourSpend,
41
- expectedValue: avgHourlySpend,
42
- deviationPercent: Math.round(deviation),
43
- message: `Spend spike on ${platform}: $${(currentHourSpend / 100).toFixed(2)}/hr vs $${(avgHourlySpend / 100).toFixed(2)}/hr average (+${Math.round(deviation)}%)`,
44
- timestamp: new Date().toISOString(),
45
- };
46
- }
47
- function detectTrafficDrop(currentDayTraffic, avgDailyTraffic) {
48
- if (avgDailyTraffic === 0)
49
- return null;
50
- const deviation = ((avgDailyTraffic - currentDayTraffic) / avgDailyTraffic) * 100;
51
- if (deviation < THRESHOLDS.trafficDropWarning)
52
- return null;
53
- const severity = deviation >= THRESHOLDS.trafficDropCritical ? 'critical' :
54
- deviation >= THRESHOLDS.trafficDropAlert ? 'alert' : 'warning';
55
- return {
56
- type: 'traffic_drop',
57
- severity,
58
- metric: 'daily_traffic',
59
- currentValue: currentDayTraffic,
60
- expectedValue: avgDailyTraffic,
61
- deviationPercent: -Math.round(deviation),
62
- message: `Traffic drop: ${currentDayTraffic} visitors today vs ${avgDailyTraffic} average (-${Math.round(deviation)}%)`,
63
- timestamp: new Date().toISOString(),
64
- };
65
- }
66
- function detectConversionChange(currentRate, avgRate) {
67
- if (avgRate === 0)
68
- return null;
69
- const deviation = ((currentRate - avgRate) / avgRate) * 100;
70
- if (Math.abs(deviation) < THRESHOLDS.conversionChangeThreshold)
71
- return null;
72
- return {
73
- type: 'conversion_change',
74
- severity: Math.abs(deviation) >= 40 ? 'alert' : 'warning',
75
- metric: 'conversion_rate',
76
- currentValue: currentRate,
77
- expectedValue: avgRate,
78
- deviationPercent: Math.round(deviation),
79
- message: `Conversion rate ${deviation > 0 ? 'increase' : 'decrease'}: ${currentRate.toFixed(1)}% vs ${avgRate.toFixed(1)}% average (${deviation > 0 ? '+' : ''}${Math.round(deviation)}%)`,
80
- timestamp: new Date().toISOString(),
81
- };
82
- }
83
- function detectRoasDrop(currentRoas, avgRoas, platform) {
84
- if (avgRoas === 0)
85
- return null;
86
- const deviation = ((avgRoas - currentRoas) / avgRoas) * 100;
87
- if (deviation < THRESHOLDS.roasDropWarning)
88
- return null;
89
- return {
90
- type: 'roas_drop',
91
- severity: deviation >= THRESHOLDS.roasDropAlert ? 'alert' : 'warning',
92
- platform,
93
- metric: 'roas',
94
- currentValue: currentRoas,
95
- expectedValue: avgRoas,
96
- deviationPercent: -Math.round(deviation),
97
- message: `ROAS drop on ${platform}: ${currentRoas.toFixed(1)}x vs ${avgRoas.toFixed(1)}x average (-${Math.round(deviation)}%)`,
98
- timestamp: new Date().toISOString(),
99
- };
100
- }
101
- /** Run all anomaly checks for the current period */
102
- export function runAnomalyDetection(metrics) {
103
- const anomalies = [];
104
- for (const s of metrics.spendByPlatform) {
105
- const a = detectSpendSpike(s.currentHour, s.avgHourly, s.platform);
106
- if (a)
107
- anomalies.push(a);
108
- }
109
- const td = detectTrafficDrop(metrics.traffic.currentDay, metrics.traffic.avgDaily);
110
- if (td)
111
- anomalies.push(td);
112
- const cc = detectConversionChange(metrics.conversion.currentRate, metrics.conversion.avgRate);
113
- if (cc)
114
- anomalies.push(cc);
115
- for (const r of metrics.roasByPlatform) {
116
- const a = detectRoasDrop(r.current, r.avg, r.platform);
117
- if (a)
118
- anomalies.push(a);
119
- }
120
- return anomalies;
121
- }
122
- export { THRESHOLDS };
@@ -1,23 +0,0 @@
1
- /**
2
- * PRD asset scanner — identifies image/visual requirements from PRD prose.
3
- * Used by Celebrimbor's /imagine command to find what needs generating.
4
- * Pure text analysis — no API calls, no side effects.
5
- */
6
- export interface AssetRequirement {
7
- description: string;
8
- category: string;
9
- context: string;
10
- width: number;
11
- height: number;
12
- section: string;
13
- }
14
- /**
15
- * Scan a PRD document for visual asset requirements.
16
- * Returns a list of assets that need generating.
17
- */
18
- export declare function scanPrdForAssets(prdContent: string): AssetRequirement[];
19
- /**
20
- * Extract brand/style keywords from the PRD for style prefix generation.
21
- * Looks for Section 14 (Brand) or any section mentioning "brand", "style", "aesthetic".
22
- */
23
- export declare function extractBrandStyle(prdContent: string): string[];