start-vibing-stacks 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -238,10 +238,23 @@ async function main() {
238
238
  console.log(chalk.green.bold(' 🎸 Ready to vibe!'));
239
239
  console.log('');
240
240
  if (!FLAGS.noClaude) {
241
- console.log(chalk.dim(' Launching Claude Code...'));
242
- console.log(chalk.dim(' Run: claude --dangerously-skip-permissions'));
241
+ console.log(chalk.dim(' Launching Claude Code...\n'));
242
+ const { execSync: run } = await import('child_process');
243
+ try {
244
+ run('claude --dangerously-skip-permissions', {
245
+ cwd: projectDir,
246
+ stdio: 'inherit',
247
+ env: { ...process.env },
248
+ });
249
+ }
250
+ catch {
251
+ console.log('');
252
+ ui.info('Claude Code session ended. Run again with: claude --dangerously-skip-permissions');
253
+ }
254
+ }
255
+ else {
256
+ console.log(chalk.dim(' Run: claude --dangerously-skip-permissions\n'));
243
257
  }
244
- console.log('');
245
258
  }
246
259
  main().catch((err) => {
247
260
  ui.error(`Unexpected error: ${err}`);
package/dist/setup.js CHANGED
@@ -134,8 +134,18 @@ export async function setupProject(projectDir, config, options = {}) {
134
134
  if (existsSync(sharedCommandsDir)) {
135
135
  copyDirRecursive(sharedCommandsDir, join(claudeDir, 'commands'), options.force);
136
136
  }
137
- // 13. Write settings.json for Claude Code
137
+ // 13. Write settings.json for Claude Code (complete config)
138
138
  const settings = {
139
+ model: 'sonnet',
140
+ max_tokens: 8192,
141
+ max_turns: 100,
142
+ enableAllProjectMcpServers: true,
143
+ context: {
144
+ compaction_threshold: 0.85,
145
+ enable_compaction: true,
146
+ enable_semantic_search: true,
147
+ memory_files: ['.claude/CLAUDE.md', 'CLAUDE.md'],
148
+ },
139
149
  permissions: {
140
150
  allow: [
141
151
  'Bash(*)',
@@ -149,7 +159,93 @@ export async function setupProject(projectDir, config, options = {}) {
149
159
  ],
150
160
  deny: [],
151
161
  },
162
+ hooks: {
163
+ UserPromptSubmit: [
164
+ {
165
+ matcher: '',
166
+ hooks: [
167
+ {
168
+ type: 'command',
169
+ command: 'npx tsx .claude/hooks/run-hook.ts user-prompt-submit',
170
+ timeout: 10,
171
+ },
172
+ ],
173
+ },
174
+ ],
175
+ Stop: [
176
+ {
177
+ hooks: [
178
+ {
179
+ type: 'command',
180
+ command: 'npx tsx .claude/hooks/run-hook.ts stop-validator',
181
+ timeout: 30,
182
+ },
183
+ ],
184
+ },
185
+ ],
186
+ },
187
+ agents: {
188
+ 'research-web': {
189
+ description: 'Researches best practices before implementation',
190
+ },
191
+ 'documenter': {
192
+ description: 'Creates and maintains domain documentation',
193
+ },
194
+ 'domain-updater': {
195
+ description: 'Records session learnings in domain docs',
196
+ },
197
+ 'commit-manager': {
198
+ description: 'Manages commits and git workflow',
199
+ },
200
+ 'claude-md-compactor': {
201
+ description: 'Compacts CLAUDE.md when it exceeds 40k chars',
202
+ },
203
+ 'tester': {
204
+ description: 'Creates tests using the stack-appropriate framework',
205
+ },
206
+ },
207
+ workflow: {
208
+ default_flow: [
209
+ 'research-web',
210
+ 'tester',
211
+ 'documenter',
212
+ 'domain-updater',
213
+ 'commit-manager',
214
+ ],
215
+ config_flow: ['domain-updater', 'commit-manager'],
216
+ },
217
+ rules: {
218
+ research: {
219
+ required_before_implementation: true,
220
+ must_cite_sources: true,
221
+ },
222
+ security: {
223
+ sanitize_all_responses: true,
224
+ owasp_validation_required: true,
225
+ },
226
+ testing: {
227
+ unit_tests_required: true,
228
+ edge_cases_research_required: true,
229
+ },
230
+ documentation: {
231
+ flow_docs_required: true,
232
+ research_must_be_documented: true,
233
+ },
234
+ },
152
235
  };
236
+ // Add stack-specific quality gates
237
+ const stackCfg = loadStackConfig(config.stack);
238
+ if (stackCfg) {
239
+ const gates = {};
240
+ for (const gate of stackCfg.qualityGates) {
241
+ gates[gate.name.toLowerCase().replace(/\s+/g, '_')] = {
242
+ command: gate.command,
243
+ required: gate.required,
244
+ blocking: gate.required,
245
+ };
246
+ }
247
+ settings.quality_gates = gates;
248
+ }
153
249
  writeFileSync(join(claudeDir, 'settings.json'), JSON.stringify(settings, null, '\t'));
154
250
  spinner.succeed(`Setup complete: ${agentCount} agents, ${sharedSkillCount + stackSkillCount} skills, ${hookCount} hooks`);
155
251
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-vibing-stacks",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "AI-powered multi-stack dev workflow for Claude Code. Supports PHP, Node.js, Python and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # Universal Hook Runner — Start Vibing Stacks
3
+ # Tries: bun → npx tsx
4
+
5
+ HOOK_NAME="$1"
6
+ HOOKS_DIR="$(dirname "$0")"
7
+
8
+ command_exists() { command -v "$1" >/dev/null 2>&1; }
9
+
10
+ if command_exists bun && [ -f "$HOOKS_DIR/$HOOK_NAME.ts" ]; then
11
+ exec bun "$HOOKS_DIR/$HOOK_NAME.ts"
12
+ fi
13
+
14
+ if command_exists npx && [ -f "$HOOKS_DIR/$HOOK_NAME.ts" ]; then
15
+ exec npx tsx "$HOOKS_DIR/$HOOK_NAME.ts"
16
+ fi
17
+
18
+ echo '{"decision":"approve","continue":true,"reason":"No runtime available"}'
19
+ exit 0
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Universal Hook Runner — Start Vibing Stacks
4
+ *
5
+ * Runtime fallbacks: bun → npx tsx
6
+ * Usage: npx tsx run-hook.ts <hook-name>
7
+ */
8
+
9
+ import { spawnSync } from 'child_process';
10
+ import { existsSync } from 'fs';
11
+ import { join, dirname } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const HOOKS_DIR = dirname(fileURLToPath(import.meta.url));
15
+
16
+ function checkRuntime(cmd: string): boolean {
17
+ try {
18
+ const result = spawnSync(cmd, ['--version'], {
19
+ stdio: 'pipe', shell: true, timeout: 5000,
20
+ });
21
+ return result.status === 0;
22
+ } catch { return false; }
23
+ }
24
+
25
+ function runWithRuntime(cmd: string, args: string[], input: string) {
26
+ try {
27
+ const result = spawnSync(cmd, args, {
28
+ input, shell: true, stdio: ['pipe', 'pipe', 'pipe'],
29
+ timeout: 30000, encoding: 'utf8',
30
+ });
31
+ return {
32
+ exitCode: result.status ?? 1,
33
+ output: result.stdout?.toString() || '',
34
+ error: result.stderr?.toString() || undefined,
35
+ };
36
+ } catch (err) {
37
+ return { exitCode: 1, output: '', error: String(err) };
38
+ }
39
+ }
40
+
41
+ async function readStdin(timeoutMs: number): Promise<string> {
42
+ return new Promise((resolve) => {
43
+ const timeout = setTimeout(() => { process.stdin.destroy(); resolve('{}'); }, timeoutMs);
44
+ let data = '';
45
+ process.stdin.setEncoding('utf8');
46
+ process.stdin.on('data', (chunk: string) => { data += chunk; });
47
+ process.stdin.on('end', () => { clearTimeout(timeout); resolve(data || '{}'); });
48
+ process.stdin.on('error', () => { clearTimeout(timeout); resolve('{}'); });
49
+ if (process.stdin.readableEnded) { clearTimeout(timeout); resolve('{}'); }
50
+ });
51
+ }
52
+
53
+ async function main(): Promise<void> {
54
+ const hookName = process.argv[2];
55
+ if (!hookName) {
56
+ console.error('[run-hook] Usage: npx tsx run-hook.ts <hook-name>');
57
+ process.exit(1);
58
+ }
59
+
60
+ const tsPath = join(HOOKS_DIR, `${hookName}.ts`);
61
+ if (!existsSync(tsPath)) {
62
+ console.error(`[run-hook] Hook not found: ${tsPath}`);
63
+ process.stdout.write(JSON.stringify({ decision: 'approve', continue: true, reason: 'Hook not found' }));
64
+ process.exit(0);
65
+ }
66
+
67
+ const stdinData = await readStdin(2000);
68
+ const runtimes = [
69
+ { name: 'bun', cmd: 'bun' },
70
+ { name: 'npx-tsx', cmd: 'npx tsx' },
71
+ ];
72
+
73
+ for (const runtime of runtimes) {
74
+ if (!checkRuntime(runtime.cmd.split(' ')[0])) continue;
75
+
76
+ const result = runWithRuntime(runtime.cmd, [tsPath], stdinData);
77
+ process.stdout.write(result.output);
78
+ if (result.error) process.stderr.write(result.error);
79
+ process.exit(result.exitCode);
80
+ }
81
+
82
+ console.error('[run-hook] No runtime available');
83
+ process.stdout.write(JSON.stringify({ decision: 'approve', continue: true, reason: 'No runtime available' }));
84
+ process.exit(0);
85
+ }
86
+
87
+ main().catch(() => {
88
+ process.stdout.write(JSON.stringify({ decision: 'approve', continue: true, reason: 'Hook error' }));
89
+ process.exit(0);
90
+ });