voidforge-build 23.11.1 → 23.11.3

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/dist/CHANGELOG.md CHANGED
@@ -6,6 +6,73 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
6
6
 
7
7
  ---
8
8
 
9
+ ## [23.11.3] - 2026-05-12
10
+
11
+ ### Issue #331 destructive-bug fix + HIGH CVE patch + dep contract pin + CI hardening
12
+
13
+ Three-phase pipeline output: `/architect --plan` → `/debrief --inbox` → `/campaign`. 12 fix missions + 2 ADRs + 1 LEARNINGS entry + 2 mechanical guards landed in one release.
14
+
15
+ ### Security
16
+
17
+ - **HIGH CVE patch** — `npm audit fix --omit=dev` resolved three vulnerabilities (2 HIGH, 1 MODERATE) in `fast-xml-parser` (≤5.6.0) and `fast-xml-builder` (≤1.1.6), pulled transitively via `@aws-sdk/xml-builder` through `@aws-sdk/client-ec2`, `client-rds`, `client-s3`, `client-elasticache`, `client-sts`. No SDK major bump required; lockfile-only change. AWS provisioner API surface unaffected.
18
+
19
+ ### Fixed
20
+
21
+ - **Issue #331 — `npx voidforge-build update` silently overwrote `~/CLAUDE.md` and 44 other methodology files in `$HOME`** when run outside a VoidForge project. Root cause: `findProjectRoot()` in `packages/voidforge/wizard/lib/marker.ts` had no `$HOME` boundary, and its `existsSync()` check could not distinguish the `.voidforge` file marker from the `~/.voidforge/` state directory (ADR-060). Fix: added `statSync().isFile()` guard and `$HOME` walk break. The function now returns null/undefined when no project root is found, never `$HOME` or `/`. Codified as ADR-063 and FORGE_KEEPER Rule #11. New integration test `no-home-writes.integration.test.ts` mechanically enforces "no escape writes" by spawning the built CLI against a temp HOME and asserting zero methodology files leak out of a project boundary.
22
+ - **Issue #260 — new users tried slash commands at their shell prompt and saw "command not found"** because no doc told them to launch Claude Code first. HOLOCRON Quick Start now opens with "Before any slash command: launch Claude Code" instructions before the install snippets.
23
+ - **Issue #333 (partial — npm-prefix only)** — `npm install -g voidforge-build` failing with `EACCES` on `/usr/local` is a fragile-globals problem, not a VoidForge bug, but worth a documented workaround. HOLOCRON now shows the `npm config set prefix ~/.npm-global` recipe.
24
+
25
+ ### Changed
26
+
27
+ - **Dep contract pin (ADR-062)** — `voidforge-build`'s `voidforge-build-methodology` dependency range changed from `"*"` to `"^23.11.3"`. The wildcard was live on the registry in v23.11.1 and v23.11.2, allowing any future breaking methodology major to silently pair with old CLI installs. Enforced mechanically going forward by a new `check-methodology-pin.sh` script wired as `voidforge-build`'s `prepublishOnly` — `npm publish` fails closed if the methodology range is `*`, `x`, `latest`, empty, or a `>`/`>=` open-ended range.
28
+ - **`packages/methodology/package.json`** — added `"engines": { "node": ">=20.11.0 <25.0.0" }` matching the CLI's existing constraint. Advisory, not enforced, but closes the silent-divergence gap.
29
+ - **`.github/workflows/publish.yml` hardening** (three coordinated changes):
30
+ - Post-publish `npm view` verification step appended to both publish jobs. 6 attempts × 10s = 60-second propagation window. Hard-fails the job on still-mismatch. Closes the silent "publish succeeded at API but registry never serves" failure mode (v23.11.2 deploy synthesis, Dors + Crusher both flagged it).
31
+ - `recover-partial` job runs `if: always() && (publish-voidforge.result == 'failure') != (publish-methodology.result == 'failure')` (XOR). On half-publish, it `npm deprecate`s the orphan with a clear "do not install" message and exits 1 to fail the workflow red. `npm unpublish` is intentionally not used (72-hour lockout breaks immutability).
32
+ - `publish-voidforge` now declares `needs: [test, publish-methodology]` (was `needs: test` only). Methodology publishes first; voidforge-build resolves its pinned `^23.11.3` methodology dep against a registry that already has it. Closes Bel Riose's parallel-publish race window.
33
+ - **`packages/voidforge/scripts/copy-assets.sh`** — `CLAUDE.md` copy now uses the same `sed` strip as `packages/methodology/scripts/prepack.sh` (ADR-058 `<!-- REMOVE-FOR-NPM-PUBLISH ... -->` markers). Closes the inconsistency Rhodes flagged in v23.11.2 deploy synthesis where `voidforge-build`'s bundled scaffold shipped the unstripped template Project block.
34
+ - **`docs/methods/RELEASE_MANAGER.md` Verification Checklist** — two new lines: "ROADMAP.md Current line matches VERSION.md" and "monorepo CLI's methodology dep range is `^<current-version>`, never `*` (ADR-062)". Mechanical drift like the 24-version ROADMAP gap should fail the checklist, not slip silently.
35
+ - **`ROADMAP.md`** — Current pointer bumped from `v23.8.11 (2026-04-12)` to `v23.11.3 (2026-05-12)`. Status block rewritten to reflect the v23.11 series shipped.
36
+
37
+ ### Added
38
+
39
+ - **ADR-062 — Always pin methodology dep to current version.** Mandates that every `voidforge-build` release bumps the `voidforge-build-methodology` dep range to `^<current-version>`. Enforced via `check-methodology-pin.sh` + prepublishOnly + a CI lint that can be added later.
40
+ - **ADR-063 — Never write to `$HOME`.** Any code path that resolves a project root via directory-walk MUST enforce a `$HOME` boundary. Enforced via `no-home-writes.integration.test.ts` running the CLI against a temp HOME.
41
+ - **`docs/LEARNINGS.md` entry** — generalizable lesson: any directory-tree walker must define a sentinel boundary (typically `$HOME` or `/`) or risk destructive past-root writes.
42
+ - **FORGE_KEEPER Rule #11** — "NEVER write to `$HOME` itself" (companion to Rule #10's "NEVER write to `~/.claude/`"). Codifies ADR-063 for Bombadil's sync logic.
43
+ - **Mechanical guards** — `check-methodology-pin.sh` (pin lint) and `no-home-writes.integration.test.ts` (boundary test). Per Frieren's planning recommendation: disciplines with silent-failure modes get mechanical enforcement; advisory disciplines (ROADMAP sync, partial-publish recovery procedure) stay documented.
44
+
45
+ ### Pipeline
46
+
47
+ This release is the first multi-phase pipeline executed end-to-end in a single session: `/architect --plan` (17 agents) → `/debrief --inbox` triage (Bashir, 13 open issues categorized) → `/campaign --plan` (16 planning agents merged Phase 1 + Phase 2 into 12 missions across 4 waves) → `/campaign` execution (18 specialist agents). Honest dissents from Faramir ("cut to 6") and Erwin ("split to 2 in v23.11.3, rest in v23.11.4") were surfaced; user selected the full scope. The destructive bug fix (#331) was filed one day before this release and was the highest-priority item — Picard's earlier "tightest patch" framing was correctly overridden by Bashir's triage.
48
+
49
+ ---
50
+
51
+ ## [23.11.2] - 2026-05-12
52
+
53
+ ### `voidforge init` mode prompt + methodology surfer-gate distribution
54
+
55
+ Two threads of polish: the CLI now asks before launching, and the Silver Surfer gate finally ships in the methodology npm package.
56
+
57
+ ### Added
58
+
59
+ - **`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.
60
+ - **`--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.
61
+ - **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.
62
+ - **`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`.
63
+ - **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.
64
+
65
+ ### Changed
66
+
67
+ - **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.
68
+ - **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.
69
+
70
+ ### Why this exists
71
+
72
+ 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.
73
+
74
+ ---
75
+
9
76
  ## [23.11.1] - 2026-05-10
10
77
 
11
78
  ### `/git` release-discipline patch — close the silent-release gap
package/dist/CLAUDE.md CHANGED
@@ -1,13 +1,5 @@
1
1
  # CLAUDE.md
2
2
 
3
- <!-- REMOVE-FOR-NPM-PUBLISH: Template section for monorepo root only. Stripped by prepack.sh per ADR-058. Published methodology consumers fill this via `npx voidforge-build init`. -->
4
- ## Project
5
-
6
- - **Name:** [PROJECT_NAME]
7
- - **One-liner:** [ONE_LINE_DESCRIPTION]
8
- - **Domain:** [DOMAIN]
9
- - **Repo:** [REPO_URL]
10
- <!-- END-REMOVE-FOR-NPM-PUBLISH -->
11
3
 
12
4
  ## Personality
13
5
 
package/dist/HOLOCRON.md CHANGED
@@ -22,6 +22,23 @@
22
22
 
23
23
  ## 1. Ignition
24
24
 
25
+ ### Before any slash command: launch Claude Code
26
+
27
+ VoidForge's slash commands (`/build`, `/campaign`, `/qa`, etc.) run **inside Claude Code**, not in your terminal. If you've never used Claude Code before:
28
+
29
+ 1. Install: `npm install -g @anthropic-ai/claude-code` (or follow [Claude Code's install guide](https://claude.com/claude-code))
30
+ 2. Open a terminal in your project directory and run `claude`
31
+ 3. Once Claude Code is running, type a slash command at its prompt — e.g., `/campaign`
32
+
33
+ The rest of this guide assumes you're already inside Claude Code. (Field report #260 — new users repeatedly tried slash commands at their shell prompt and saw "command not found.")
34
+
35
+ **If `npm install -g` fails with EACCES on `/usr/local`** (the global npm prefix), use a user-space prefix instead of `sudo`:
36
+ ```bash
37
+ npm config set prefix ~/.npm-global
38
+ export PATH="$HOME/.npm-global/bin:$PATH" # add to ~/.zshrc or ~/.bashrc
39
+ ```
40
+ Then retry the install. (Field report #333 — sudo-installing globals is fragile and varies by Node distribution.)
41
+
25
42
  ### What VoidForge Is
26
43
 
27
44
  VoidForge is a **methodology framework** for building full-stack applications with Claude Code. It's not a code template — it's a *process* template. Drop in a Product Requirements Document, and a named team of AI agents across 9 fictional universes builds your application through a 13-phase protocol.
package/dist/VERSION.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Version
2
2
 
3
- **Current:** 23.11.1
3
+ **Current:** 23.11.3
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.3 | 2026-05-12 | Three-phase pipeline (/architect → /debrief --inbox → /campaign) shipped 12 fixes + 2 ADRs + 1 LEARNINGS entry + 2 mechanical guards. **Issue #331** destructive-bug fix: `findProjectRoot()` now enforces `$HOME` boundary + `statSync().isFile()` guard, no more silent overwrite of `~/CLAUDE.md` on `npx voidforge-build update`. **HIGH CVE** fast-xml-parser/builder via `@aws-sdk/*` patched via `npm audit fix`. **Dep contract** pinned: `voidforge-build → voidforge-build-methodology` from `"*"` to `"^23.11.3"` (ADR-062), enforced mechanically by `check-methodology-pin.sh` prepublishOnly script. **engines.node** added to methodology package.json. **publish.yml hardening**: post-publish `npm view` verification step (both jobs, 6×10s retry), `recover-partial` job with `npm deprecate` on XOR-failure, `needs: publish-methodology` ordering on publish-voidforge. **copy-assets.sh** ADR-058 template strip applied (parity with methodology prepack). **Docs**: HOLOCRON Quick Start "launch Claude Code first" preamble + npm-prefix workaround (#260, #333p), FORGE_KEEPER Rule #11 "never write to $HOME" (ADR-063), RELEASE_MANAGER ROADMAP-sync checklist line, ROADMAP.md pointer v23.8.11 → v23.11.3 (24-version drift closed). Marker integration test `no-home-writes.integration.test.ts` mechanically enforces ADR-063. 1390 tests pass. |
18
+ | 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. |
17
19
  | 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. |
18
20
  | 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. |
19
21
  | 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. |
@@ -47,6 +47,7 @@ Keep your VoidForge installation current without breaking your project. Every up
47
47
  8. **Keep the mood light.** Bombadil sings. Updates are good news, not chores.
48
48
  9. **Batch sync when multiple versions behind.** Compare directly to the latest upstream version — don't step through each intermediate version. Sync to the latest in one pass and batch all content handoffs together. (Field report #35)
49
49
  10. **NEVER write to `~/.claude/`.** All methodology files go to the PROJECT root (where `.voidforge` or `.git` exists). Writing to `~/.claude/commands/` or `~/.claude/agents/` creates user-level duplicates that appear alongside project-level commands in Claude Code. Before any sync, check if `~/.claude/commands/` or `~/.claude/agents/` contain VoidForge files — if so, remove them automatically and warn the user.
50
+ 11. **NEVER write to `$HOME` itself.** Any code path that resolves a "project root" via directory-walk MUST enforce a `$HOME` boundary — the walk stops at `$HOME` and treats it as "no project found," not as the root. The boundary is mechanical (`statSync().isFile()` guard on the marker file + `$HOME` walk break), not advisory. A walk that falls through to `$HOME` and starts writing methodology files there destructively overwrites the user's personal config (e.g. `~/CLAUDE.md`). Enforced by the `no-home-writes.integration.test.ts` integration test. (ADR-063, Issue #331 — the `npx voidforge-build update` $HOME write incident, v23.11.3.)
50
51
 
51
52
  ## Shared Methodology Files
52
53
 
@@ -120,6 +120,8 @@ After every commit, Barton verifies:
120
120
  - [ ] `git status` shows clean working tree
121
121
  - [ ] No untracked files that should have been included
122
122
  - [ ] If `--npm` was used: every published package returns the new version from `npm view <name> version`
123
+ - [ ] `ROADMAP.md` "Current:" line matches `VERSION.md` (added v23.11.3 — field-report #309 Fix 4 and v23.11.2 deploy synthesis both flagged drift; ROADMAP had been pinned ~24 versions back before this checklist line existed)
124
+ - [ ] For monorepo CLI/methodology pairs: the CLI's `voidforge-build-methodology` dep range is `^<current-version>`, never `"*"` (ADR-062 — pin tightening shipped in v23.11.3 to close the silent-cross-major drift)
123
125
 
124
126
  ## CLAUDE.md Command Table Integrity Check
125
127
 
@@ -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') {
@@ -18,6 +18,13 @@ export declare function createMarker(version: string, tier?: VoidForgeMarker['ti
18
18
  /**
19
19
  * Walk up from `startDir` to find the nearest `.voidforge` marker.
20
20
  * Returns the directory containing the marker, or null if none found.
21
+ *
22
+ * Safety guards (issue #331):
23
+ * Option A — marker MUST be a regular file. A `.voidforge` directory
24
+ * (e.g. someone's stray folder, or an extension's data dir) is ignored.
25
+ * Option B — the walk stops at the user's home directory. We never treat
26
+ * `$HOME` or any ancestor as a project root, so `update`/`init` cannot
27
+ * overwrite files in `~/` when invoked outside a project.
21
28
  */
22
29
  export declare function findProjectRoot(startDir?: string): string | null;
23
30
  /**
@@ -5,8 +5,8 @@
5
5
  * The CLI walks up from cwd to find it, determining the project root.
6
6
  */
7
7
  import { readFile, writeFile } from 'node:fs/promises';
8
- import { existsSync } from 'node:fs';
9
- import { join, dirname } from 'node:path';
8
+ import { existsSync, statSync } from 'node:fs';
9
+ import { join, dirname, resolve } from 'node:path';
10
10
  import { randomUUID } from 'node:crypto';
11
11
  import { homedir } from 'node:os';
12
12
  // ── Constants ────────────────────────────────────────────
@@ -44,12 +44,32 @@ export function createMarker(version, tier = 'full', extensions = []) {
44
44
  /**
45
45
  * Walk up from `startDir` to find the nearest `.voidforge` marker.
46
46
  * Returns the directory containing the marker, or null if none found.
47
+ *
48
+ * Safety guards (issue #331):
49
+ * Option A — marker MUST be a regular file. A `.voidforge` directory
50
+ * (e.g. someone's stray folder, or an extension's data dir) is ignored.
51
+ * Option B — the walk stops at the user's home directory. We never treat
52
+ * `$HOME` or any ancestor as a project root, so `update`/`init` cannot
53
+ * overwrite files in `~/` when invoked outside a project.
47
54
  */
48
55
  export function findProjectRoot(startDir = process.cwd()) {
49
- let current = startDir;
56
+ const home = resolve(process.env['HOME'] ?? process.env['USERPROFILE'] ?? homedir());
57
+ let current = resolve(startDir);
50
58
  while (true) {
51
- if (existsSync(join(current, MARKER_FILE))) {
52
- return current;
59
+ // Option B: never accept $HOME (or anything at/above it) as a project root.
60
+ if (current === home)
61
+ return null;
62
+ const markerPath = join(current, MARKER_FILE);
63
+ if (existsSync(markerPath)) {
64
+ // Option A: marker must be a regular file, not a directory.
65
+ try {
66
+ if (statSync(markerPath).isFile()) {
67
+ return current;
68
+ }
69
+ }
70
+ catch {
71
+ // stat raced with deletion or permission error — treat as no marker.
72
+ }
53
73
  }
54
74
  const parent = dirname(current);
55
75
  if (parent === current)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voidforge-build",
3
- "version": "23.11.1",
3
+ "version": "23.11.3",
4
4
  "description": "From nothing, everything. A methodology framework for building with Claude Code.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -14,6 +14,7 @@
14
14
  "deploy": "npx tsx scripts/voidforge.ts deploy",
15
15
  "build": "tsc && bash scripts/copy-assets.sh",
16
16
  "prepack": "bash scripts/prepack-patterns.sh && npm run build",
17
+ "prepublishOnly": "bash scripts/check-methodology-pin.sh",
17
18
  "typecheck": "npx tsc --noEmit",
18
19
  "test": "vitest run --pool forks",
19
20
  "test:watch": "vitest --pool forks",
@@ -44,7 +45,7 @@
44
45
  "@aws-sdk/client-rds": "^3.700.0",
45
46
  "@aws-sdk/client-s3": "^3.700.0",
46
47
  "@aws-sdk/client-sts": "^3.700.0",
47
- "voidforge-build-methodology": "*",
48
+ "voidforge-build-methodology": "^23.11.3",
48
49
  "node-pty": "^1.2.0-beta.12",
49
50
  "ws": "^8.19.0"
50
51
  },