vibe-forge 0.8.2 → 0.8.5

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/README.md CHANGED
@@ -1,211 +1,211 @@
1
- # Vibe Forge
2
-
3
- A multi-agent development orchestration system for terminal-native vibe coding.
4
-
5
- ## What Is This?
6
-
7
- Vibe Forge transforms your terminal into a collaborative AI development environment. Multiple Claude agents, each with distinct personalities and specializations, work together to build software. You talk to a Planning Hub that coordinates the team, then spawn workers into separate terminal tabs to execute in parallel.
8
-
9
- ```
10
- YOU
11
- |
12
- /forge plan
13
- |
14
- +-----------+-----------+
15
- | PLANNING HUB |
16
- | Oracle Architect |
17
- | Aegis Pixel |
18
- | Ember Crucible |
19
- +-----------+-----------+
20
- |
21
- /forge spawn <agent>
22
- |
23
- +--------+-------+-------+--------+
24
- | | | | |
25
- Anvil Furnace Crucible Temper Scribe
26
- FE BE QA Review Docs
27
- ```
28
-
29
- ## Install
30
-
31
- ```bash
32
- npx vibe-forge init
33
- ```
34
-
35
- This sets up Vibe Forge in your project: detects your platform, configures your terminal, creates project context files, and installs the `/forge` slash command into Claude Code.
36
-
37
- **Prerequisites:** [Claude Code CLI](https://claude.ai/download), Node.js 18+, Git
38
-
39
- ## Quick Start
40
-
41
- ```bash
42
- # Start the Planning Hub (multi-expert planning session)
43
- /forge
44
-
45
- # Or jump straight to planning a feature
46
- /forge plan user authentication
47
-
48
- # Spawn a worker agent in a new terminal tab
49
- /forge spawn anvil
50
-
51
- # Check what's happening
52
- /forge status
53
-
54
- # See all commands
55
- /forge help
56
- ```
57
-
58
- ## Agents
59
-
60
- ### Planning Hub (Your Terminal)
61
-
62
- When you run `/forge`, a multi-voice planning session starts. These expert voices collaborate to help you scope, design, and decompose work:
63
-
64
- | Voice | Icon | Speaks When |
65
- |-------|------|-------------|
66
- | Forge Master | :fire: | Tasks, assignments, workflow, coordination |
67
- | Architect | :classical_building: | Architecture, patterns, tech decisions |
68
- | Aegis | :shield: | Auth, security, vulnerabilities |
69
- | Ember | :gear: | DevOps, CI/CD, deployment |
70
- | Pixel | :art: | UX, user flows, accessibility |
71
- | Oracle | :bar_chart: | Requirements, scope, priorities |
72
- | Crucible | :test_tube: | Edge cases, test strategy, quality |
73
- | Loki | :performing_arts: | Challenges assumptions, lateral thinking |
74
-
75
- ### Worker Agents (Separate Terminals)
76
-
77
- Spawn these into new terminal tabs to execute tasks:
78
-
79
- | Agent | Aliases | Role |
80
- |-------|---------|------|
81
- | anvil | frontend, ui, fe | Frontend Developer |
82
- | furnace | backend, api, be | Backend Developer |
83
- | crucible | test, qa | Tester / QA |
84
- | scribe | docs, documentation | Documentation |
85
- | herald | release, deploy | Release Manager |
86
- | ember | devops, infra | DevOps Engineer |
87
-
88
- ### Review Agents
89
-
90
- | Agent | Aliases | Role |
91
- |-------|---------|------|
92
- | temper | review, cr | Code Reviewer (compliance + correctness) |
93
- | crucible-x | adversarial, cx | Adversarial Reviewer (tries to break it) |
94
-
95
- ### Specialists
96
-
97
- | Agent | Aliases | Role |
98
- |-------|---------|------|
99
- | architect | arch, sage | System Architect |
100
- | aegis | security, sec | Security Specialist |
101
- | pixel | ux, ui-design | UX Designer |
102
- | oracle | product, po | Product Owner |
103
- | loki | brainstorm, contrarian | Assumption Challenger |
104
-
105
- ### Red Team
106
-
107
- | Agent | Aliases | Role |
108
- |-------|---------|------|
109
- | slag | redteam, pentest | Red Team Lead |
110
- | flux | infra-sec, chaos | Infrastructure Security |
111
-
112
- ## How It Works
113
-
114
- ### Planning Mode
115
-
116
- When you describe a goal, the Hub enters a 4-phase planning flow:
117
-
118
- 1. **Discovery** - Oracle asks clarifying questions about users, goals, constraints
119
- 2. **Decomposition** - Architect breaks the goal into epics with success metrics
120
- 3. **Tasking** - Forge Master creates stories and tasks, assigns to agents
121
- 4. **Commit** - Epic and task files written to disk, ready for workers
122
-
123
- ### Task System
124
-
125
- Tasks flow through folders on disk. No database required.
126
-
127
- ```
128
- pending/ -> in-progress/ -> completed/ -> review/ -> approved/ -> merged/
129
- |
130
- needs-changes/ (back to worker)
131
- ```
132
-
133
- Workers pick up tasks from `pending/` on startup. Temper reviews completed work. The daemon routes tasks automatically.
134
-
135
- ### Daemon
136
-
137
- An optional background daemon monitors the forge:
138
-
139
- ```bash
140
- ./bin/forge.sh daemon start # Start background monitoring
141
- ./bin/forge.sh daemon status # Check what's happening
142
- ./bin/forge.sh daemon stop # Stop the daemon
143
- ```
144
-
145
- The daemon provides:
146
- - Task routing between folders
147
- - Agent status tracking
148
- - Token budget warnings for long-running agents
149
- - Dependency resolution (respects `blocked_by` in task files)
150
- - Attention notifications when agents need help
151
-
152
- ### Dashboard
153
-
154
- A web dashboard at `http://localhost:2800` shows:
155
- - Task counts and status
156
- - Agent activity feed
157
- - Issue detection
158
- - Real-time updates via WebSocket
159
-
160
- ### Per-Project Customization
161
-
162
- Add agent-specific rules for your project in `context/agent-overrides/`:
163
-
164
- ```markdown
165
- <!-- context/agent-overrides/anvil.md -->
166
- - Use Tailwind CSS, no custom CSS files
167
- - All components in src/components/ with PascalCase
168
- - Use shadcn/ui for base components
169
- ```
170
-
171
- These rules are injected into the agent's system prompt at spawn time.
172
-
173
- ## Project Structure
174
-
175
- ```
176
- your-project/
177
- _vibe-forge/
178
- agents/ # Agent personalities (16 agents)
179
- bin/ # CLI, daemon, dashboard, spawn scripts
180
- config/ # agents.json, task templates
181
- context/ # Project context, agent overrides
182
- specs/ # Epics and stories
183
- tasks/ # Task lifecycle folders
184
- docs/ # Security, architecture, agent docs
185
- ```
186
-
187
- ## Commands Reference
188
-
189
- | Command | Description |
190
- |---------|-------------|
191
- | `/forge` | Start the Planning Hub |
192
- | `/forge plan <feature>` | Plan a feature with the full team |
193
- | `/forge status` | Show status dashboard |
194
- | `/forge spawn <agent>` | Spawn worker in new terminal |
195
- | `/forge task [desc]` | Create a new task |
196
- | `/forge redteam [scope]` | Launch red team engagement |
197
- | `/forge help` | Show all commands |
198
-
199
- ## Security
200
-
201
- Vibe Forge uses a defense-in-depth permission model:
202
-
203
- 1. **Allowlist** (`.claude/settings.json`) - Pre-approves safe operations
204
- 2. **Heimdall** (pre-tool hook) - Enforces forge policies (branch protection, naming)
205
- 3. **Claude Code prompts** - Anything not allowlisted still requires approval
206
-
207
- See [docs/security.md](docs/security.md) for the full security model.
208
-
209
- ## License
210
-
211
- MIT
1
+ # Vibe Forge
2
+
3
+ A multi-agent development orchestration system for terminal-native vibe coding.
4
+
5
+ ## What Is This?
6
+
7
+ Vibe Forge transforms your terminal into a collaborative AI development environment. Multiple Claude agents, each with distinct personalities and specializations, work together to build software. You talk to a Planning Hub that coordinates the team, then spawn workers into separate terminal tabs to execute in parallel.
8
+
9
+ ```
10
+ YOU
11
+ |
12
+ /forge plan
13
+ |
14
+ +-----------+-----------+
15
+ | PLANNING HUB |
16
+ | Oracle Architect |
17
+ | Aegis Pixel |
18
+ | Ember Crucible |
19
+ +-----------+-----------+
20
+ |
21
+ /forge spawn <agent>
22
+ |
23
+ +--------+-------+-------+--------+
24
+ | | | | |
25
+ Anvil Furnace Crucible Temper Scribe
26
+ FE BE QA Review Docs
27
+ ```
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npx vibe-forge init
33
+ ```
34
+
35
+ This sets up Vibe Forge in your project: detects your platform, configures your terminal, creates project context files, and installs the `/forge` slash command into Claude Code.
36
+
37
+ **Prerequisites:** [Claude Code CLI](https://claude.ai/download), Node.js 18+, Git
38
+
39
+ ## Quick Start
40
+
41
+ ```bash
42
+ # Start the Planning Hub (multi-expert planning session)
43
+ /forge
44
+
45
+ # Or jump straight to planning a feature
46
+ /forge plan user authentication
47
+
48
+ # Spawn a worker agent in a new terminal tab
49
+ /forge spawn anvil
50
+
51
+ # Check what's happening
52
+ /forge status
53
+
54
+ # See all commands
55
+ /forge help
56
+ ```
57
+
58
+ ## Agents
59
+
60
+ ### Planning Hub (Your Terminal)
61
+
62
+ When you run `/forge`, a multi-voice planning session starts. These expert voices collaborate to help you scope, design, and decompose work:
63
+
64
+ | Voice | Icon | Speaks When |
65
+ |-------|------|-------------|
66
+ | Forge Master | :fire: | Tasks, assignments, workflow, coordination |
67
+ | Architect | :classical_building: | Architecture, patterns, tech decisions |
68
+ | Aegis | :shield: | Auth, security, vulnerabilities |
69
+ | Ember | :gear: | DevOps, CI/CD, deployment |
70
+ | Pixel | :art: | UX, user flows, accessibility |
71
+ | Oracle | :bar_chart: | Requirements, scope, priorities |
72
+ | Crucible | :test_tube: | Edge cases, test strategy, quality |
73
+ | Loki | :performing_arts: | Challenges assumptions, lateral thinking |
74
+
75
+ ### Worker Agents (Separate Terminals)
76
+
77
+ Spawn these into new terminal tabs to execute tasks:
78
+
79
+ | Agent | Aliases | Role |
80
+ |-------|---------|------|
81
+ | anvil | frontend, ui, fe | Frontend Developer |
82
+ | furnace | backend, api, be | Backend Developer |
83
+ | crucible | test, qa | Tester / QA |
84
+ | scribe | docs, documentation | Documentation |
85
+ | herald | release, deploy | Release Manager |
86
+ | ember | devops, infra | DevOps Engineer |
87
+
88
+ ### Review Agents
89
+
90
+ | Agent | Aliases | Role |
91
+ |-------|---------|------|
92
+ | temper | review, cr | Code Reviewer (compliance + correctness) |
93
+ | crucible-x | adversarial, cx | Adversarial Reviewer (tries to break it) |
94
+
95
+ ### Specialists
96
+
97
+ | Agent | Aliases | Role |
98
+ |-------|---------|------|
99
+ | architect | arch, sage | System Architect |
100
+ | aegis | security, sec | Security Specialist |
101
+ | pixel | ux, ui-design | UX Designer |
102
+ | oracle | product, po | Product Owner |
103
+ | loki | brainstorm, contrarian | Assumption Challenger |
104
+
105
+ ### Red Team
106
+
107
+ | Agent | Aliases | Role |
108
+ |-------|---------|------|
109
+ | slag | redteam, pentest | Red Team Lead |
110
+ | flux | infra-sec, chaos | Infrastructure Security |
111
+
112
+ ## How It Works
113
+
114
+ ### Planning Mode
115
+
116
+ When you describe a goal, the Hub enters a 4-phase planning flow:
117
+
118
+ 1. **Discovery** - Oracle asks clarifying questions about users, goals, constraints
119
+ 2. **Decomposition** - Architect breaks the goal into epics with success metrics
120
+ 3. **Tasking** - Forge Master creates stories and tasks, assigns to agents
121
+ 4. **Commit** - Epic and task files written to disk, ready for workers
122
+
123
+ ### Task System
124
+
125
+ Tasks flow through folders on disk. No database required.
126
+
127
+ ```
128
+ pending/ -> in-progress/ -> completed/ -> review/ -> approved/ -> merged/
129
+ |
130
+ needs-changes/ (back to worker)
131
+ ```
132
+
133
+ Workers pick up tasks from `pending/` on startup. Temper reviews completed work. The daemon routes tasks automatically.
134
+
135
+ ### Daemon
136
+
137
+ An optional background daemon monitors the forge:
138
+
139
+ ```bash
140
+ ./bin/forge.sh daemon start # Start background monitoring
141
+ ./bin/forge.sh daemon status # Check what's happening
142
+ ./bin/forge.sh daemon stop # Stop the daemon
143
+ ```
144
+
145
+ The daemon provides:
146
+ - Task routing between folders
147
+ - Agent status tracking
148
+ - Token budget warnings for long-running agents
149
+ - Dependency resolution (respects `blocked_by` in task files)
150
+ - Attention notifications when agents need help
151
+
152
+ ### Dashboard
153
+
154
+ A web dashboard at `http://localhost:2800` shows:
155
+ - Task counts and status
156
+ - Agent activity feed
157
+ - Issue detection
158
+ - Real-time updates via WebSocket
159
+
160
+ ### Per-Project Customization
161
+
162
+ Add agent-specific rules for your project in `context/agent-overrides/`:
163
+
164
+ ```markdown
165
+ <!-- context/agent-overrides/anvil.md -->
166
+ - Use Tailwind CSS, no custom CSS files
167
+ - All components in src/components/ with PascalCase
168
+ - Use shadcn/ui for base components
169
+ ```
170
+
171
+ These rules are injected into the agent's system prompt at spawn time.
172
+
173
+ ## Project Structure
174
+
175
+ ```
176
+ your-project/
177
+ _vibe-forge/
178
+ agents/ # Agent personalities (16 agents)
179
+ bin/ # CLI, daemon, dashboard, spawn scripts
180
+ config/ # agents.json, task templates
181
+ context/ # Project context, agent overrides
182
+ specs/ # Epics and stories
183
+ tasks/ # Task lifecycle folders
184
+ docs/ # Security, architecture, agent docs
185
+ ```
186
+
187
+ ## Commands Reference
188
+
189
+ | Command | Description |
190
+ |---------|-------------|
191
+ | `/forge` | Start the Planning Hub |
192
+ | `/forge plan <feature>` | Plan a feature with the full team |
193
+ | `/forge status` | Show status dashboard |
194
+ | `/forge spawn <agent>` | Spawn worker in new terminal |
195
+ | `/forge task [desc]` | Create a new task |
196
+ | `/forge redteam [scope]` | Launch red team engagement |
197
+ | `/forge help` | Show all commands |
198
+
199
+ ## Security
200
+
201
+ Vibe Forge uses a defense-in-depth permission model:
202
+
203
+ 1. **Allowlist** (`.claude/settings.json`) - Pre-approves safe operations
204
+ 2. **Heimdall** (pre-tool hook) - Enforces forge policies (branch protection, naming)
205
+ 3. **Claude Code prompts** - Anything not allowlisted still requires approval
206
+
207
+ See [docs/security.md](docs/security.md) for the full security model.
208
+
209
+ ## License
210
+
211
+ MIT
package/bin/cli.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * npx vibe-forge --help Show help
10
10
  */
11
11
 
12
- const { execSync, spawn } = require('child_process');
12
+ const { execSync, spawn, spawnSync } = require('child_process');
13
13
  const fs = require('fs');
14
14
  const path = require('path');
15
15
  const os = require('os');
@@ -20,6 +20,72 @@ const VERSION = packageJson.version;
20
20
  const REPO_URL = 'https://github.com/sugar-crash-studios/vibe-forge.git';
21
21
  const FORGE_DIR = '_vibe-forge';
22
22
 
23
+ // ---------------------------------------------------------------------------
24
+ // Environment allowlist for child processes.
25
+ //
26
+ // forge-setup.sh runs with full user privileges, so we pass only the minimum
27
+ // env it needs instead of the entire parent environment. This keeps ambient
28
+ // secrets (AWS_*, GITHUB_TOKEN, NPM_TOKEN, OPENAI_API_KEY, ANTHROPIC_API_KEY,
29
+ // DATABASE_URL, STRIPE_*, etc.) from leaking into setup scripts if a user's
30
+ // shell happens to have them set at invocation time.
31
+ // ---------------------------------------------------------------------------
32
+ // Frozen so third parties (or a poisoned import chain) cannot push a secret
33
+ // name onto the allowlist at runtime and leak it into the next buildChildEnv
34
+ // call. The whole point of the allowlist is defense-in-depth, so the exported
35
+ // surface is treated as immutable.
36
+ const ENV_ALLOWLIST = Object.freeze([
37
+ // POSIX basics
38
+ 'PATH', 'HOME', 'USER', 'LOGNAME', 'SHELL', 'TERM', 'TMPDIR',
39
+ // Locale
40
+ 'LANG', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES',
41
+ // Windows basics
42
+ 'SYSTEMROOT', 'SYSTEMDRIVE', 'COMSPEC', 'USERPROFILE', 'LOCALAPPDATA',
43
+ 'APPDATA', 'TEMP', 'TMP', 'USERNAME', 'PATHEXT', 'WINDIR',
44
+ 'PROGRAMDATA', 'PROGRAMFILES', 'PROGRAMFILES(X86)', 'PROGRAMW6432',
45
+ 'PROCESSOR_ARCHITECTURE', 'PROCESSOR_IDENTIFIER',
46
+ // Git Bash / MSYS
47
+ 'MSYSTEM', 'MINGW_PREFIX', 'MINGW_CHOST',
48
+ // Proxies (needed for git/npm in corporate networks)
49
+ 'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY', 'ALL_PROXY',
50
+ 'http_proxy', 'https_proxy', 'no_proxy',
51
+ // TLS CA bundles — consistent partner of the proxy vars above. Corporate
52
+ // networks with intercepting proxies need these or node/git fail TLS.
53
+ 'NODE_EXTRA_CA_CERTS', 'SSL_CERT_FILE', 'SSL_CERT_DIR',
54
+ 'GIT_SSL_CAINFO', 'GIT_SSL_CAPATH',
55
+ // Claude Code — setup validates `claude --version`, which needs this on Windows
56
+ 'CLAUDE_CODE_GIT_BASH_PATH',
57
+ ]);
58
+
59
+ function buildChildEnv(ambient, extras = {}) {
60
+ // Reject non-object extras loudly. String/number/boolean extras silently
61
+ // spread wrong shapes (e.g. Object.assign({}, 'abc') → {0:'a',1:'b',2:'c'}),
62
+ // which produces a silently-wrong env instead of a caller-visible error.
63
+ if (extras !== undefined && extras !== null && (typeof extras !== 'object' || Array.isArray(extras))) {
64
+ throw new TypeError(`buildChildEnv: extras must be a plain object, got ${typeof extras}`);
65
+ }
66
+
67
+ const source = ambient || {};
68
+ const env = {};
69
+ for (const key of ENV_ALLOWLIST) {
70
+ if (source[key] !== undefined) {
71
+ env[key] = source[key];
72
+ }
73
+ }
74
+
75
+ // Merge extras, but treat `undefined` as a no-op rather than overwriting the
76
+ // allowlisted value with an undefined. The source-side loop deliberately
77
+ // skips undefined keys; extras should follow the same rule for symmetry.
78
+ if (extras) {
79
+ for (const key of Object.keys(extras)) {
80
+ if (extras[key] !== undefined) {
81
+ env[key] = extras[key];
82
+ }
83
+ }
84
+ }
85
+
86
+ return env;
87
+ }
88
+
23
89
  // Colors for terminal output
24
90
  // NOTE: Intentionally duplicated from src/lib/colors.sh
25
91
  // This is by design: cli.js runs standalone via npx before the rest of Vibe Forge
@@ -170,10 +236,7 @@ function runBashScript(scriptPath, args = []) {
170
236
  const child = spawn(bashPath, [scriptPath, ...args], {
171
237
  stdio: 'inherit',
172
238
  cwd: process.cwd(),
173
- env: {
174
- ...process.env,
175
- CLAUDE_CODE_GIT_BASH_PATH: bashPath,
176
- },
239
+ env: buildChildEnv(process.env, { CLAUDE_CODE_GIT_BASH_PATH: bashPath }),
177
240
  });
178
241
 
179
242
  child.on('close', (code) => {
@@ -209,18 +272,24 @@ async function initCommand() {
209
272
  logSuccess('Prerequisites OK');
210
273
  log('');
211
274
 
212
- // Clone the repository
275
+ // Clone the repository. Uses spawnSync with an arg array rather than
276
+ // execSync with a template string so there is no shell-injection surface
277
+ // if REPO_URL or FORGE_DIR are ever derived from untrusted input.
213
278
  logInfo(`Cloning Vibe Forge into ${FORGE_DIR}/...`);
214
- try {
215
- execSync(`git clone --depth 1 ${REPO_URL} ${FORGE_DIR}`, {
279
+ const cloneResult = spawnSync(
280
+ 'git',
281
+ ['clone', '--depth', '1', REPO_URL, FORGE_DIR],
282
+ {
216
283
  stdio: 'inherit',
217
284
  cwd: process.cwd(),
218
- });
219
- logSuccess('Clone complete');
220
- } catch {
285
+ env: buildChildEnv(process.env),
286
+ }
287
+ );
288
+ if (cloneResult.status !== 0) {
221
289
  logError('Failed to clone repository');
222
290
  process.exit(1);
223
291
  }
292
+ logSuccess('Clone complete');
224
293
 
225
294
  log('');
226
295
 
@@ -247,16 +316,26 @@ function validateAgentsConfig(forgeDir) {
247
316
  const checkScript = path.join(forgeDir, 'bin', 'lib', 'check-aliases.js');
248
317
  if (!fs.existsSync(checkScript)) return;
249
318
 
319
+ const agentsFile = path.join(forgeDir, 'config', 'agents.json');
320
+ const result = spawnSync('node', [checkScript, agentsFile], {
321
+ stdio: ['ignore', 'pipe', 'pipe'],
322
+ env: buildChildEnv(process.env),
323
+ });
324
+
325
+ if (result.status !== 0) {
326
+ logError('Agent alias collisions detected in config/agents.json');
327
+ const stderr = result.stderr ? result.stderr.toString().trim() : '';
328
+ logInfo(stderr || 'Run node src/lib/check-aliases.js for details');
329
+ logError('Fix collisions before using Vibe Forge.');
330
+ process.exit(1);
331
+ }
332
+
250
333
  try {
251
- const agentsFile = path.join(forgeDir, 'config', 'agents.json');
252
- execSync(`node "${checkScript}" "${agentsFile}"`, { stdio: 'pipe' });
253
334
  const config = JSON.parse(fs.readFileSync(agentsFile, 'utf8'));
254
335
  const agentCount = Object.keys(config.agents || {}).length;
255
336
  logSuccess(`Agent config valid (${agentCount} agents, no alias collisions)`);
256
- } catch (err) {
257
- logError('Agent alias collisions detected in config/agents.json');
258
- logInfo(err.stderr ? err.stderr.toString().trim() : 'Run node src/lib/check-aliases.js for details');
259
- logError('Fix collisions before using Vibe Forge.');
337
+ } catch {
338
+ logError('Agent config validation passed, but agents.json could not be parsed.');
260
339
  process.exit(1);
261
340
  }
262
341
  }
@@ -320,21 +399,29 @@ async function updateCommand() {
320
399
 
321
400
  logInfo('Updating Vibe Forge...');
322
401
 
323
- try {
324
- // Fetch and reset to origin/main - works even without tracking branch
325
- execSync('git fetch origin', {
326
- stdio: 'inherit',
327
- cwd: targetDir,
328
- });
329
- execSync('git reset --hard origin/main', {
330
- stdio: 'inherit',
331
- cwd: targetDir,
332
- });
333
- logSuccess('Update complete');
334
- } catch {
402
+ const childEnv = buildChildEnv(process.env);
403
+
404
+ const fetchResult = spawnSync('git', ['fetch', 'origin'], {
405
+ stdio: 'inherit',
406
+ cwd: targetDir,
407
+ env: childEnv,
408
+ });
409
+ if (fetchResult.status !== 0) {
335
410
  logError('Failed to update. Check your network connection or try deleting _vibe-forge and running init again.');
336
411
  process.exit(1);
337
412
  }
413
+
414
+ const resetResult = spawnSync('git', ['reset', '--hard', 'origin/main'], {
415
+ stdio: 'inherit',
416
+ cwd: targetDir,
417
+ env: childEnv,
418
+ });
419
+ if (resetResult.status !== 0) {
420
+ logError('Failed to update. Check your network connection or try deleting _vibe-forge and running init again.');
421
+ process.exit(1);
422
+ }
423
+
424
+ logSuccess('Update complete');
338
425
  }
339
426
 
340
427
  // Main entry point
@@ -366,7 +453,14 @@ async function main() {
366
453
  }
367
454
  }
368
455
 
369
- main().catch((err) => {
370
- logError(err.message);
371
- process.exit(1);
372
- });
456
+ if (require.main === module) {
457
+ main().catch((err) => {
458
+ logError(err.message);
459
+ process.exit(1);
460
+ });
461
+ }
462
+
463
+ module.exports = {
464
+ buildChildEnv,
465
+ ENV_ALLOWLIST,
466
+ };