vibe-forge 0.3.12 → 0.8.1

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 (85) hide show
  1. package/.claude/commands/clear-attention.md +63 -63
  2. package/.claude/commands/compact-context.md +52 -0
  3. package/.claude/commands/configure-vcs.md +102 -0
  4. package/.claude/commands/forge.md +218 -171
  5. package/.claude/commands/need-help.md +77 -77
  6. package/.claude/commands/update-status.md +64 -64
  7. package/.claude/commands/worker-loop.md +106 -106
  8. package/.claude/hooks/worker-loop.js +217 -0
  9. package/.claude/scripts/setup-worker-loop.sh +45 -45
  10. package/.claude/settings.json +89 -0
  11. package/LICENSE +21 -21
  12. package/README.md +253 -230
  13. package/agents/aegis/personality.md +303 -269
  14. package/agents/anvil/personality.md +278 -211
  15. package/agents/architect/personality.md +260 -0
  16. package/agents/crucible/personality.md +362 -285
  17. package/agents/crucible-x/personality.md +210 -0
  18. package/agents/ember/personality.md +293 -245
  19. package/agents/flux/personality.md +248 -0
  20. package/agents/furnace/personality.md +342 -262
  21. package/agents/herald/personality.md +249 -247
  22. package/agents/loki/personality.md +108 -0
  23. package/agents/oracle/personality.md +284 -0
  24. package/agents/pixel/personality.md +140 -0
  25. package/agents/planning-hub/personality.md +473 -251
  26. package/agents/scribe/personality.md +253 -231
  27. package/agents/slag/personality.md +268 -0
  28. package/agents/temper/personality.md +270 -0
  29. package/bin/cli.js +372 -325
  30. package/bin/dashboard/api/agents.js +333 -0
  31. package/bin/dashboard/api/dispatch.js +507 -0
  32. package/bin/dashboard/api/tasks.js +416 -0
  33. package/bin/dashboard/public/assets/index-BpHfsx1r.js +2 -0
  34. package/bin/dashboard/public/assets/index-QODv4Zn9.css +1 -0
  35. package/bin/dashboard/public/index.html +14 -0
  36. package/bin/dashboard/server.js +645 -0
  37. package/bin/forge-daemon.sh +477 -775
  38. package/bin/forge-setup.sh +661 -532
  39. package/bin/forge-spawn.sh +164 -159
  40. package/bin/forge.cmd +83 -83
  41. package/bin/forge.sh +566 -393
  42. package/bin/lib/agents.sh +177 -177
  43. package/bin/lib/check-aliases.js +50 -0
  44. package/bin/lib/colors.sh +44 -44
  45. package/bin/lib/config.sh +347 -271
  46. package/bin/lib/constants.sh +241 -171
  47. package/bin/lib/daemon/budgets.sh +107 -0
  48. package/bin/lib/daemon/dependencies.sh +146 -0
  49. package/bin/lib/daemon/display.sh +128 -0
  50. package/bin/lib/daemon/notifications.sh +273 -0
  51. package/bin/lib/daemon/routing.sh +93 -0
  52. package/bin/lib/daemon/state.sh +163 -0
  53. package/bin/lib/daemon/sync.sh +103 -0
  54. package/bin/lib/database.sh +357 -224
  55. package/bin/lib/frontmatter.js +106 -0
  56. package/bin/lib/heimdall-setup.js +113 -0
  57. package/bin/lib/heimdall.js +265 -0
  58. package/bin/lib/json.sh +264 -0
  59. package/bin/lib/terminal.js +452 -0
  60. package/bin/lib/util.sh +126 -0
  61. package/bin/lib/vcs.js +349 -0
  62. package/config/agent-manifest.yaml +237 -230
  63. package/config/agents.json +207 -85
  64. package/config/task-template.md +159 -87
  65. package/config/task-types.yaml +111 -106
  66. package/config/templates/handoff-template.md +40 -0
  67. package/context/agent-overrides/README.md +41 -0
  68. package/context/architecture.md +42 -0
  69. package/context/modern-conventions.md +129 -129
  70. package/context/project-context-template.md +122 -122
  71. package/docs/agents.md +473 -0
  72. package/docs/architecture.md +194 -0
  73. package/docs/commands.md +451 -0
  74. package/docs/security.md +195 -144
  75. package/package.json +77 -48
  76. package/.claude/hooks/worker-loop.sh +0 -141
  77. package/.claude/settings.local.json +0 -29
  78. package/agents/forge-master/capabilities.md +0 -144
  79. package/agents/forge-master/context-template.md +0 -128
  80. package/agents/forge-master/personality.md +0 -138
  81. package/agents/sentinel/personality.md +0 -194
  82. package/context/forge-state.yaml +0 -19
  83. package/docs/TODO.md +0 -176
  84. package/docs/npm-publishing.md +0 -95
  85. package/tasks/review/task-001.md +0 -78
package/bin/cli.js CHANGED
@@ -1,325 +1,372 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Vibe Forge CLI
5
- *
6
- * Usage:
7
- * npx vibe-forge init Initialize Forge in current project
8
- * npx vibe-forge update Update Forge to latest version
9
- * npx vibe-forge --help Show help
10
- */
11
-
12
- const { execSync, spawn } = require('child_process');
13
- const fs = require('fs');
14
- const path = require('path');
15
- const os = require('os');
16
-
17
- // Read version from package.json (single source of truth)
18
- const packageJson = require(path.join(__dirname, '..', 'package.json'));
19
- const VERSION = packageJson.version;
20
- const REPO_URL = 'https://github.com/SpasticPalate/vibe-forge.git';
21
- const FORGE_DIR = '_vibe-forge';
22
-
23
- // Colors for terminal output
24
- // NOTE: Intentionally self-contained - cli.js runs standalone via npx before
25
- // the rest of Vibe Forge is installed, so it cannot share with colors.sh
26
- const colors = {
27
- reset: '\x1b[0m',
28
- red: '\x1b[31m',
29
- green: '\x1b[32m',
30
- yellow: '\x1b[33m',
31
- blue: '\x1b[34m',
32
- cyan: '\x1b[36m',
33
- };
34
-
35
- function log(message, color = colors.reset) {
36
- console.log(`${color}${message}${colors.reset}`);
37
- }
38
-
39
- function logError(message) {
40
- log(`Error: ${message}`, colors.red);
41
- }
42
-
43
- function logSuccess(message) {
44
- log(message, colors.green);
45
- }
46
-
47
- function logInfo(message) {
48
- log(message, colors.blue);
49
- }
50
-
51
- function showBanner() {
52
- console.log(`
53
- ${colors.yellow}🔥 Vibe Forge${colors.reset}
54
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
55
- Multi-agent development orchestration
56
- `);
57
- }
58
-
59
- function showHelp() {
60
- showBanner();
61
- console.log(`Usage: npx vibe-forge <command>
62
-
63
- Commands:
64
- init Initialize Vibe Forge in the current project
65
- update Update Vibe Forge to the latest version
66
- version Show version information
67
- help Show this help message
68
-
69
- Examples:
70
- npx vibe-forge init # Set up Forge in your project
71
- npx vibe-forge update # Update to latest version
72
- `);
73
- }
74
-
75
- function showVersion() {
76
- console.log(`Vibe Forge v${VERSION}`);
77
- }
78
-
79
- function checkPrerequisites() {
80
- // Check for git
81
- try {
82
- execSync('git --version', { stdio: 'pipe' });
83
- } catch {
84
- logError('Git is not installed. Please install Git first.');
85
- logInfo(' Windows: winget install Git.Git');
86
- logInfo(' macOS: brew install git');
87
- logInfo(' Linux: sudo apt install git');
88
- process.exit(1);
89
- }
90
-
91
- // Check for Claude Code
92
- try {
93
- execSync('claude --version', { stdio: 'pipe' });
94
- } catch {
95
- logError('Claude Code CLI is not installed.');
96
- logInfo(' Install from: https://claude.ai/download');
97
- process.exit(1);
98
- }
99
- }
100
-
101
- function isWindows() {
102
- return os.platform() === 'win32';
103
- }
104
-
105
- function getBashPath() {
106
- if (!isWindows()) {
107
- return 'bash';
108
- }
109
-
110
- // Common Git Bash paths on Windows
111
- const paths = [
112
- 'C:\\Program Files\\Git\\bin\\bash.exe',
113
- 'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
114
- path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Git', 'bin', 'bash.exe'),
115
- ];
116
-
117
- for (const p of paths) {
118
- if (fs.existsSync(p)) {
119
- return p;
120
- }
121
- }
122
-
123
- // Try to find via where command
124
- try {
125
- const gitPath = execSync('where git', { stdio: 'pipe' }).toString().trim().split('\n')[0];
126
- const gitDir = path.dirname(path.dirname(gitPath));
127
- const bashPath = path.join(gitDir, 'bin', 'bash.exe');
128
- if (fs.existsSync(bashPath)) {
129
- return bashPath;
130
- }
131
- } catch {
132
- // Ignore
133
- }
134
-
135
- return null;
136
- }
137
-
138
- function runBashScript(scriptPath, args = []) {
139
- const bashPath = getBashPath();
140
-
141
- if (!bashPath) {
142
- logError('Could not find bash. Please install Git Bash on Windows.');
143
- process.exit(1);
144
- }
145
-
146
- return new Promise((resolve, reject) => {
147
- const child = spawn(bashPath, [scriptPath, ...args], {
148
- stdio: 'inherit',
149
- cwd: process.cwd(),
150
- env: {
151
- ...process.env,
152
- CLAUDE_CODE_GIT_BASH_PATH: bashPath,
153
- },
154
- });
155
-
156
- child.on('close', (code) => {
157
- if (code === 0) {
158
- resolve();
159
- } else {
160
- reject(new Error(`Script exited with code ${code}`));
161
- }
162
- });
163
-
164
- child.on('error', (err) => {
165
- reject(err);
166
- });
167
- });
168
- }
169
-
170
- async function initCommand() {
171
- showBanner();
172
-
173
- const targetDir = path.join(process.cwd(), FORGE_DIR);
174
-
175
- // Check if already initialized
176
- if (fs.existsSync(targetDir)) {
177
- logInfo(`Vibe Forge already exists at ${FORGE_DIR}/`);
178
- log('');
179
- log('To update, run: npx vibe-forge update');
180
- log('To reinitialize, delete the _vibe-forge folder first.');
181
- return;
182
- }
183
-
184
- logInfo('Checking prerequisites...');
185
- checkPrerequisites();
186
- logSuccess('Prerequisites OK');
187
- log('');
188
-
189
- // Clone the repository
190
- logInfo(`Cloning Vibe Forge into ${FORGE_DIR}/...`);
191
- try {
192
- execSync(`git clone --depth 1 ${REPO_URL} ${FORGE_DIR}`, {
193
- stdio: 'inherit',
194
- cwd: process.cwd(),
195
- });
196
- logSuccess('Clone complete');
197
- } catch {
198
- logError('Failed to clone repository');
199
- process.exit(1);
200
- }
201
-
202
- log('');
203
-
204
- // Update project's .gitignore to ignore tool internals but keep project data
205
- updateGitignore();
206
-
207
- // Run the setup script
208
- logInfo('Running setup...');
209
- log('');
210
-
211
- try {
212
- const setupScript = path.join(targetDir, 'bin', 'forge-setup.sh');
213
- await runBashScript(setupScript);
214
- } catch (err) {
215
- logError(`Setup failed: ${err.message}`);
216
- process.exit(1);
217
- }
218
- }
219
-
220
- function updateGitignore() {
221
- const gitignorePath = path.join(process.cwd(), '.gitignore');
222
- const forgeIgnoreMarker = '# Vibe Forge';
223
- const forgeIgnoreBlock = `
224
- # Vibe Forge
225
- # Tool internals (regenerated on update)
226
- _vibe-forge/.git/
227
- _vibe-forge/bin/
228
- _vibe-forge/agents/
229
- _vibe-forge/config/
230
- _vibe-forge/docs/
231
- _vibe-forge/tests/
232
- _vibe-forge/src/
233
- _vibe-forge/node_modules/
234
- _vibe-forge/package*.json
235
- _vibe-forge/*.md
236
- _vibe-forge/LICENSE
237
- _vibe-forge/.github/
238
-
239
- # Keep project data (tasks, context) - these ARE committed
240
- # !_vibe-forge/tasks/
241
- # !_vibe-forge/context/
242
- # !_vibe-forge/.claude/
243
- `;
244
-
245
- try {
246
- let content = '';
247
- if (fs.existsSync(gitignorePath)) {
248
- content = fs.readFileSync(gitignorePath, 'utf8');
249
- // Check if already has forge entries
250
- if (content.includes(forgeIgnoreMarker)) {
251
- return; // Already configured
252
- }
253
- }
254
-
255
- // Append forge ignore block
256
- const newContent = content.trimEnd() + '\n' + forgeIgnoreBlock;
257
- fs.writeFileSync(gitignorePath, newContent);
258
- logSuccess('Updated .gitignore for Vibe Forge');
259
- } catch (err) {
260
- logError(`Warning: Could not update .gitignore: ${err.message}`);
261
- }
262
- }
263
-
264
- async function updateCommand() {
265
- showBanner();
266
-
267
- const targetDir = path.join(process.cwd(), FORGE_DIR);
268
-
269
- // Check if initialized
270
- if (!fs.existsSync(targetDir)) {
271
- logError('Vibe Forge is not initialized in this project.');
272
- log('');
273
- log('Run: npx vibe-forge init');
274
- process.exit(1);
275
- }
276
-
277
- logInfo('Updating Vibe Forge...');
278
-
279
- try {
280
- // Fetch and reset to origin/main - works even without tracking branch
281
- execSync('git fetch origin', {
282
- stdio: 'inherit',
283
- cwd: targetDir,
284
- });
285
- execSync('git reset --hard origin/main', {
286
- stdio: 'inherit',
287
- cwd: targetDir,
288
- });
289
- logSuccess('Update complete');
290
- } catch {
291
- logError('Failed to update. Check your network connection or try deleting _vibe-forge and running init again.');
292
- process.exit(1);
293
- }
294
- }
295
-
296
- // Main entry point
297
- async function main() {
298
- const args = process.argv.slice(2);
299
- const command = args[0] || 'help';
300
-
301
- switch (command) {
302
- case 'init':
303
- await initCommand();
304
- break;
305
- case 'update':
306
- await updateCommand();
307
- break;
308
- case 'version':
309
- case '--version':
310
- case '-v':
311
- showVersion();
312
- break;
313
- case 'help':
314
- case '--help':
315
- case '-h':
316
- default:
317
- showHelp();
318
- break;
319
- }
320
- }
321
-
322
- main().catch((err) => {
323
- logError(err.message);
324
- process.exit(1);
325
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Vibe Forge CLI
5
+ *
6
+ * Usage:
7
+ * npx @sugar-crash-studios/vibe-forge init Initialize Forge in current project
8
+ * npx @sugar-crash-studios/vibe-forge update Update Forge to latest version
9
+ * npx @sugar-crash-studios/vibe-forge --help Show help
10
+ */
11
+
12
+ const { execSync, spawn } = require('child_process');
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const os = require('os');
16
+
17
+ // Read version from package.json (single source of truth)
18
+ const packageJson = require(path.join(__dirname, '..', 'package.json'));
19
+ const VERSION = packageJson.version;
20
+ const REPO_URL = 'https://github.com/sugar-crash-studios/vibe-forge.git';
21
+ const FORGE_DIR = '_vibe-forge';
22
+
23
+ // Colors for terminal output
24
+ // NOTE: Intentionally duplicated from bin/lib/colors.sh
25
+ // This is by design: cli.js runs standalone via npx before the rest of Vibe Forge
26
+ // is installed, so it cannot import colors.sh. If you update colors here,
27
+ // also update bin/lib/colors.sh to keep them in sync.
28
+ const colors = {
29
+ reset: '\x1b[0m',
30
+ red: '\x1b[31m',
31
+ green: '\x1b[32m',
32
+ yellow: '\x1b[33m',
33
+ blue: '\x1b[34m',
34
+ cyan: '\x1b[36m',
35
+ };
36
+
37
+ function log(message, color = colors.reset) {
38
+ console.log(`${color}${message}${colors.reset}`);
39
+ }
40
+
41
+ function logError(message) {
42
+ log(`Error: ${message}`, colors.red);
43
+ }
44
+
45
+ function logSuccess(message) {
46
+ log(message, colors.green);
47
+ }
48
+
49
+ function logInfo(message) {
50
+ log(message, colors.blue);
51
+ }
52
+
53
+ function showBanner() {
54
+ console.log(`
55
+ ${colors.yellow}🔥 Vibe Forge${colors.reset}
56
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
57
+ Multi-agent development orchestration
58
+ `);
59
+ }
60
+
61
+ function showHelp() {
62
+ showBanner();
63
+ console.log(`Usage: npx @sugar-crash-studios/vibe-forge <command>
64
+
65
+ Commands:
66
+ init Initialize Vibe Forge in the current project
67
+ update Update Vibe Forge to the latest version
68
+ agents List all agents, roles, and aliases
69
+ version Show version information
70
+ help Show this help message
71
+
72
+ Examples:
73
+ npx @sugar-crash-studios/vibe-forge init # Set up Forge in your project
74
+ npx @sugar-crash-studios/vibe-forge update # Update to latest version
75
+ npx @sugar-crash-studios/vibe-forge agents # Show agent roster
76
+ `);
77
+ }
78
+
79
+ function showAgents() {
80
+ const agentsFile = path.join(__dirname, '..', 'config', 'agents.json');
81
+ let agents = {};
82
+ try {
83
+ const data = JSON.parse(fs.readFileSync(agentsFile, 'utf8'));
84
+ agents = data.agents || {};
85
+ } catch (e) {
86
+ logError('Could not read agents.json');
87
+ logInfo('Run from inside a Vibe Forge installation, or run: npx @sugar-crash-studios/vibe-forge init');
88
+ return;
89
+ }
90
+
91
+ showBanner();
92
+ console.log(`${colors.yellow}Agent Roster${colors.reset}`);
93
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
94
+ console.log('');
95
+
96
+ const typeOrder = ['core', 'worker', 'specialist', 'advisor'];
97
+ const typeLabels = {
98
+ core: 'Core (Always Running)',
99
+ worker: 'Workers (Per-Task)',
100
+ specialist: 'Specialists (On-Demand)',
101
+ advisor: 'Advisors',
102
+ };
103
+
104
+ for (const type of typeOrder) {
105
+ const group = Object.entries(agents).filter(([, a]) => a.type === type);
106
+ if (group.length === 0) continue;
107
+ console.log(` ${colors.cyan}${typeLabels[type] || type}${colors.reset}`);
108
+ for (const [name, agent] of group) {
109
+ console.log(` ${agent.icon || ' '} ${colors.yellow}${name}${colors.reset} — ${agent.role}`);
110
+ if (agent.aliases && agent.aliases.length > 0) {
111
+ console.log(` aliases: ${agent.aliases.join(', ')}`);
112
+ }
113
+ }
114
+ console.log('');
115
+ }
116
+
117
+ console.log(`Spawn an agent:`);
118
+ console.log(` ${colors.yellow}./bin/forge.sh spawn <agent-name-or-alias>${colors.reset}`);
119
+ console.log('');
120
+ }
121
+
122
+ function showVersion() {
123
+ console.log(`Vibe Forge v${VERSION}`);
124
+ }
125
+
126
+ function checkPrerequisites() {
127
+ // Check for git
128
+ try {
129
+ execSync('git --version', { stdio: 'pipe' });
130
+ } catch {
131
+ logError('Git is not installed. Please install Git first.');
132
+ logInfo(' Windows: winget install Git.Git');
133
+ logInfo(' macOS: brew install git');
134
+ logInfo(' Linux: sudo apt install git');
135
+ process.exit(1);
136
+ }
137
+
138
+ // Check for Claude Code
139
+ try {
140
+ execSync('claude --version', { stdio: 'pipe' });
141
+ } catch {
142
+ logError('Claude Code CLI is not installed.');
143
+ logInfo(' Install from: https://claude.ai/download');
144
+ process.exit(1);
145
+ }
146
+ }
147
+
148
+ function isWindows() {
149
+ return os.platform() === 'win32';
150
+ }
151
+
152
+ function getBashPath() {
153
+ if (!isWindows()) {
154
+ return 'bash';
155
+ }
156
+ // Delegate to shared terminal utility for Windows Git Bash detection
157
+ const { findGitBash } = require('./lib/terminal.js');
158
+ return findGitBash() || null;
159
+ }
160
+
161
+ function runBashScript(scriptPath, args = []) {
162
+ const bashPath = getBashPath();
163
+
164
+ if (!bashPath) {
165
+ logError('Could not find bash. Please install Git Bash on Windows.');
166
+ process.exit(1);
167
+ }
168
+
169
+ return new Promise((resolve, reject) => {
170
+ const child = spawn(bashPath, [scriptPath, ...args], {
171
+ stdio: 'inherit',
172
+ cwd: process.cwd(),
173
+ env: {
174
+ ...process.env,
175
+ CLAUDE_CODE_GIT_BASH_PATH: bashPath,
176
+ },
177
+ });
178
+
179
+ child.on('close', (code) => {
180
+ if (code === 0) {
181
+ resolve();
182
+ } else {
183
+ reject(new Error(`Script exited with code ${code}`));
184
+ }
185
+ });
186
+
187
+ child.on('error', (err) => {
188
+ reject(err);
189
+ });
190
+ });
191
+ }
192
+
193
+ async function initCommand() {
194
+ showBanner();
195
+
196
+ const targetDir = path.join(process.cwd(), FORGE_DIR);
197
+
198
+ // Check if already initialized
199
+ if (fs.existsSync(targetDir)) {
200
+ logInfo(`Vibe Forge already exists at ${FORGE_DIR}/`);
201
+ log('');
202
+ log('To update, run: npx @sugar-crash-studios/vibe-forge update');
203
+ log('To reinitialize, delete the _vibe-forge folder first.');
204
+ return;
205
+ }
206
+
207
+ logInfo('Checking prerequisites...');
208
+ checkPrerequisites();
209
+ logSuccess('Prerequisites OK');
210
+ log('');
211
+
212
+ // Clone the repository
213
+ logInfo(`Cloning Vibe Forge into ${FORGE_DIR}/...`);
214
+ try {
215
+ execSync(`git clone --depth 1 ${REPO_URL} ${FORGE_DIR}`, {
216
+ stdio: 'inherit',
217
+ cwd: process.cwd(),
218
+ });
219
+ logSuccess('Clone complete');
220
+ } catch {
221
+ logError('Failed to clone repository');
222
+ process.exit(1);
223
+ }
224
+
225
+ log('');
226
+
227
+ // Validate agents.json (alias collisions)
228
+ validateAgentsConfig(targetDir);
229
+
230
+ // Update project's .gitignore to ignore tool internals but keep project data
231
+ updateGitignore();
232
+
233
+ // Run the setup script
234
+ logInfo('Running setup...');
235
+ log('');
236
+
237
+ try {
238
+ const setupScript = path.join(targetDir, 'bin', 'forge-setup.sh');
239
+ await runBashScript(setupScript);
240
+ } catch (err) {
241
+ logError(`Setup failed: ${err.message}`);
242
+ process.exit(1);
243
+ }
244
+ }
245
+
246
+ function validateAgentsConfig(forgeDir) {
247
+ const checkScript = path.join(forgeDir, 'bin', 'lib', 'check-aliases.js');
248
+ if (!fs.existsSync(checkScript)) return;
249
+
250
+ try {
251
+ const agentsFile = path.join(forgeDir, 'config', 'agents.json');
252
+ execSync(`node "${checkScript}" "${agentsFile}"`, { stdio: 'pipe' });
253
+ const config = JSON.parse(fs.readFileSync(agentsFile, 'utf8'));
254
+ const agentCount = Object.keys(config.agents || {}).length;
255
+ 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 bin/lib/check-aliases.js for details');
259
+ logError('Fix collisions before using Vibe Forge.');
260
+ process.exit(1);
261
+ }
262
+ }
263
+
264
+ function updateGitignore() {
265
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
266
+ const forgeIgnoreMarker = '# Vibe Forge';
267
+ const forgeIgnoreBlock = `
268
+ # Vibe Forge
269
+ # Tool internals (regenerated on update)
270
+ _vibe-forge/.git/
271
+ _vibe-forge/bin/
272
+ _vibe-forge/agents/
273
+ _vibe-forge/config/
274
+ _vibe-forge/docs/
275
+ _vibe-forge/tests/
276
+ _vibe-forge/src/
277
+ _vibe-forge/node_modules/
278
+ _vibe-forge/package*.json
279
+ _vibe-forge/*.md
280
+ _vibe-forge/LICENSE
281
+ _vibe-forge/.github/
282
+
283
+ # Keep project data (tasks, context) - these ARE committed
284
+ # !_vibe-forge/tasks/
285
+ # !_vibe-forge/context/
286
+ # !_vibe-forge/.claude/
287
+ `;
288
+
289
+ try {
290
+ let content = '';
291
+ if (fs.existsSync(gitignorePath)) {
292
+ content = fs.readFileSync(gitignorePath, 'utf8');
293
+ // Check if already has forge entries
294
+ if (content.includes(forgeIgnoreMarker)) {
295
+ return; // Already configured
296
+ }
297
+ }
298
+
299
+ // Append forge ignore block
300
+ const newContent = content.trimEnd() + '\n' + forgeIgnoreBlock;
301
+ fs.writeFileSync(gitignorePath, newContent);
302
+ logSuccess('Updated .gitignore for Vibe Forge');
303
+ } catch (err) {
304
+ logError(`Warning: Could not update .gitignore: ${err.message}`);
305
+ }
306
+ }
307
+
308
+ async function updateCommand() {
309
+ showBanner();
310
+
311
+ const targetDir = path.join(process.cwd(), FORGE_DIR);
312
+
313
+ // Check if initialized
314
+ if (!fs.existsSync(targetDir)) {
315
+ logError('Vibe Forge is not initialized in this project.');
316
+ log('');
317
+ log('Run: npx @sugar-crash-studios/vibe-forge init');
318
+ process.exit(1);
319
+ }
320
+
321
+ logInfo('Updating Vibe Forge...');
322
+
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 {
335
+ logError('Failed to update. Check your network connection or try deleting _vibe-forge and running init again.');
336
+ process.exit(1);
337
+ }
338
+ }
339
+
340
+ // Main entry point
341
+ async function main() {
342
+ const args = process.argv.slice(2);
343
+ const command = args[0] || 'help';
344
+
345
+ switch (command) {
346
+ case 'init':
347
+ await initCommand();
348
+ break;
349
+ case 'update':
350
+ await updateCommand();
351
+ break;
352
+ case 'agents':
353
+ showAgents();
354
+ break;
355
+ case 'version':
356
+ case '--version':
357
+ case '-v':
358
+ showVersion();
359
+ break;
360
+ case 'help':
361
+ case '--help':
362
+ case '-h':
363
+ default:
364
+ showHelp();
365
+ break;
366
+ }
367
+ }
368
+
369
+ main().catch((err) => {
370
+ logError(err.message);
371
+ process.exit(1);
372
+ });