create-smash-os 0.1.2 → 0.1.4

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 (2) hide show
  1. package/install.mjs +379 -4
  2. package/package.json +1 -1
package/install.mjs CHANGED
@@ -3,23 +3,31 @@
3
3
  * smash-os-install — Install the SmashOS Claude Code harness into any repo.
4
4
  *
5
5
  * Usage (inside your repo root):
6
- * npx smash-os-install
6
+ * npx smash-os-install — connected mode (requires SmashOS web app)
7
+ * npx smash-os-install --local — local-only mode (no web app, no API keys)
7
8
  *
8
- * What it does:
9
+ * What it does (connected):
9
10
  * 1. Prompts for your SmashOS URL, Repo ID, and API key
10
11
  * 2. Validates credentials against the SmashOS API
11
12
  * 3. Fetches the generated harness files for your repo
12
13
  * 4. Writes CLAUDE.md, .claude/hooks/, .claude/skills/, .env.smash-os
13
14
  * 5. Merges hooks into existing .claude/settings.json (if present)
14
15
  * 6. Adds .env.smash-os to .gitignore
16
+ *
17
+ * What it does (--local):
18
+ * 1. Prompts for project name and tech stack only
19
+ * 2. Writes CLAUDE.md + /ai/ skeleton + 2 skills locally
20
+ * 3. No web app connection required
15
21
  */
16
22
 
17
23
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
18
- import { join, dirname } from 'path';
24
+ import { join, dirname, basename } from 'path';
25
+ import { homedir } from 'os';
19
26
  import prompts from 'prompts';
20
27
  import chalk from 'chalk';
21
28
 
22
29
  const cwd = process.cwd();
30
+ const isLocal = process.argv.includes('--local');
23
31
 
24
32
  // ─── Helpers ──────────────────────────────────────────────────────────────────
25
33
 
@@ -82,9 +90,376 @@ function ensureGitignore(entry) {
82
90
 
83
91
  console.log('');
84
92
  console.log(chalk.bold(' SmashOS Harness Installer'));
85
- console.log(chalk.dim(' Installs the Claude Code harness into your repo'));
93
+ console.log(chalk.dim(isLocal ? ' Local-only mode — no web app required' : ' Installs the Claude Code harness into your repo'));
86
94
  console.log('');
87
95
 
96
+ // ─── Local-only mode ──────────────────────────────────────────────────────────
97
+
98
+ if (isLocal) {
99
+ const onCancel = () => { console.log(chalk.yellow('\n Cancelled.')); process.exit(0); };
100
+
101
+ const baseAnswers = await prompts([
102
+ {
103
+ type: 'text',
104
+ name: 'projectName',
105
+ message: 'Project name',
106
+ initial: basename(cwd),
107
+ validate: (v) => (v.trim().length > 0 ? true : 'Required'),
108
+ },
109
+ ], { onCancel });
110
+
111
+ const { projectName } = baseAnswers;
112
+
113
+ console.log('');
114
+ console.log(chalk.bold(' Tech Stack'));
115
+ console.log(chalk.dim(' Select your tools — choose "Other" to enter a custom value'));
116
+ console.log('');
117
+
118
+ const O = '__other__';
119
+ const o = (prev) => prev === O ? 'text' : null;
120
+ const req = (v) => v.trim().length > 0 ? true : 'Required';
121
+
122
+ const stackAnswers = await prompts([
123
+ // ── Frontend Framework ─────────────────────────────────────────────────
124
+ {
125
+ type: 'select',
126
+ name: 'frontend',
127
+ message: 'Frontend framework',
128
+ choices: [
129
+ { title: 'Next.js', value: 'Next.js' },
130
+ { title: 'React Router v7', value: 'React Router v7' },
131
+ { title: 'Remix', value: 'Remix' },
132
+ { title: 'Vite + React (SPA)', value: 'Vite + React' },
133
+ { title: 'SvelteKit', value: 'SvelteKit' },
134
+ { title: 'Nuxt (Vue 3)', value: 'Nuxt' },
135
+ { title: 'Astro', value: 'Astro' },
136
+ { title: 'Angular', value: 'Angular' },
137
+ { title: 'Solid Start', value: 'Solid Start' },
138
+ { title: 'Other…', value: O },
139
+ ],
140
+ },
141
+ { type: o, name: 'frontendCustom', message: 'Specify your frontend framework', validate: req },
142
+
143
+ // ── Backend / API ──────────────────────────────────────────────────────
144
+ {
145
+ type: 'select',
146
+ name: 'backend',
147
+ message: 'Backend / API layer',
148
+ choices: [
149
+ { title: 'Fullstack (handled by framework)', value: 'Fullstack (framework handles API)' },
150
+ { title: 'Node.js + Express', value: 'Node.js + Express' },
151
+ { title: 'Node.js + Hono', value: 'Node.js + Hono' },
152
+ { title: 'Node.js + Fastify', value: 'Node.js + Fastify' },
153
+ { title: 'Node.js + NestJS', value: 'Node.js + NestJS' },
154
+ { title: 'Python + FastAPI', value: 'Python + FastAPI' },
155
+ { title: 'Python + Django / Flask', value: 'Python + Django/Flask' },
156
+ { title: 'Go (Gin / Chi)', value: 'Go' },
157
+ { title: 'Bun + Elysia', value: 'Bun + Elysia' },
158
+ { title: 'Other…', value: O },
159
+ ],
160
+ },
161
+ { type: o, name: 'backendCustom', message: 'Specify your backend/API layer', validate: req },
162
+
163
+ // ── Database ───────────────────────────────────────────────────────────
164
+ {
165
+ type: 'select',
166
+ name: 'database',
167
+ message: 'Database',
168
+ choices: [
169
+ { title: 'Supabase (Postgres)', value: 'Supabase (Postgres)' },
170
+ { title: 'Neon (Postgres)', value: 'Neon (Postgres)' },
171
+ { title: 'MongoDB Atlas', value: 'MongoDB Atlas' },
172
+ { title: 'PlanetScale / Vitess (MySQL)', value: 'PlanetScale (MySQL)' },
173
+ { title: 'Firebase Firestore', value: 'Firebase Firestore' },
174
+ { title: 'Turso (SQLite / libSQL)', value: 'Turso (SQLite)' },
175
+ { title: 'Prisma + PostgreSQL', value: 'Prisma + PostgreSQL' },
176
+ { title: 'Drizzle + D1 (SQLite)', value: 'Drizzle + Cloudflare D1' },
177
+ { title: 'Upstash Redis', value: 'Upstash Redis' },
178
+ { title: 'Other…', value: O },
179
+ ],
180
+ },
181
+ { type: o, name: 'databaseCustom', message: 'Specify your database', validate: req },
182
+
183
+ // ── Authentication ─────────────────────────────────────────────────────
184
+ {
185
+ type: 'select',
186
+ name: 'auth',
187
+ message: 'Authentication',
188
+ choices: [
189
+ { title: 'None / Custom', value: 'None' },
190
+ { title: 'Clerk', value: 'Clerk' },
191
+ { title: 'Supabase Auth', value: 'Supabase Auth' },
192
+ { title: 'Auth.js (NextAuth.js)', value: 'Auth.js (NextAuth)' },
193
+ { title: 'Better Auth', value: 'Better Auth' },
194
+ { title: 'Firebase Auth', value: 'Firebase Auth' },
195
+ { title: 'Auth0', value: 'Auth0' },
196
+ { title: 'Lucia Auth', value: 'Lucia Auth' },
197
+ { title: 'Kinde', value: 'Kinde' },
198
+ { title: 'Other…', value: O },
199
+ ],
200
+ },
201
+ { type: o, name: 'authCustom', message: 'Specify your auth provider', validate: req },
202
+
203
+ // ── Deployment ─────────────────────────────────────────────────────────
204
+ {
205
+ type: 'select',
206
+ name: 'deployment',
207
+ message: 'Deployment target',
208
+ choices: [
209
+ { title: 'Vercel', value: 'Vercel' },
210
+ { title: 'Cloudflare Workers / Pages', value: 'Cloudflare Workers/Pages' },
211
+ { title: 'Netlify', value: 'Netlify' },
212
+ { title: 'Railway', value: 'Railway' },
213
+ { title: 'Fly.io', value: 'Fly.io' },
214
+ { title: 'Render', value: 'Render' },
215
+ { title: 'AWS (Lambda / ECS)', value: 'AWS' },
216
+ { title: 'Google Cloud Run', value: 'Google Cloud Run' },
217
+ { title: 'Self-hosted / VPS', value: 'Self-hosted / VPS' },
218
+ { title: 'Other…', value: O },
219
+ ],
220
+ },
221
+ { type: o, name: 'deploymentCustom', message: 'Specify your deployment target', validate: req },
222
+
223
+ // ── Styling / UI ───────────────────────────────────────────────────────
224
+ {
225
+ type: 'select',
226
+ name: 'styling',
227
+ message: 'Styling / UI library',
228
+ choices: [
229
+ { title: 'Tailwind CSS', value: 'Tailwind CSS' },
230
+ { title: 'shadcn/ui + Tailwind', value: 'shadcn/ui + Tailwind CSS' },
231
+ { title: 'CSS Modules', value: 'CSS Modules' },
232
+ { title: 'Material UI (MUI)', value: 'Material UI (MUI)' },
233
+ { title: 'Chakra UI', value: 'Chakra UI' },
234
+ { title: 'Styled Components / Emotion', value: 'Styled Components' },
235
+ { title: 'Mantine', value: 'Mantine' },
236
+ { title: 'Radix UI + Tailwind', value: 'Radix UI + Tailwind CSS' },
237
+ { title: 'UnoCSS', value: 'UnoCSS' },
238
+ { title: 'Other…', value: O },
239
+ ],
240
+ },
241
+ { type: o, name: 'stylingCustom', message: 'Specify your styling/UI library', validate: req },
242
+ ], { onCancel });
243
+
244
+ function pick(val, custom) { return val === O ? (custom || 'Other') : val; }
245
+
246
+ const techStack = [
247
+ `- Frontend: ${pick(stackAnswers.frontend, stackAnswers.frontendCustom)}`,
248
+ `- Backend: ${pick(stackAnswers.backend, stackAnswers.backendCustom)}`,
249
+ `- Database: ${pick(stackAnswers.database, stackAnswers.databaseCustom)}`,
250
+ `- Auth: ${pick(stackAnswers.auth, stackAnswers.authCustom)}`,
251
+ `- Deployment: ${pick(stackAnswers.deployment, stackAnswers.deploymentCustom)}`,
252
+ `- Styling: ${pick(stackAnswers.styling, stackAnswers.stylingCustom)}`,
253
+ ].join('\n');
254
+
255
+ console.log('');
256
+ const today = new Date().toISOString().split('T')[0];
257
+
258
+ const claudeMd = `# ${projectName} — SmashOS Harness Active
259
+
260
+ You are operating inside the SmashOS AI Workflow Harness.
261
+ You are not a single assistant. You are a coordinated AI engineering organisation.
262
+
263
+ ## On Session Start
264
+ 1. Read \`ai/context/product.md\` (if it exists)
265
+ 2. Read \`ai/context/architecture.md\` (if it exists)
266
+ 3. Read \`ai/context/coding-standards.md\` (if it exists)
267
+ 4. Read \`ai/memory/decisions.md\` — last 20 entries (if it exists)
268
+ 5. Adopt the role: Staff Engineer
269
+
270
+ ## Cognitive Mode Rules
271
+ THINKING → Staff Engineer, Product Manager (no file writes)
272
+ EXECUTION → Senior Developer, DevOps (no specs or decisions)
273
+ VALIDATION → Security Engineer, QA Engineer (no production code)
274
+
275
+ ## Golden Rules
276
+ - Never skip architecture review
277
+ - Never write code without an approved spec
278
+ - Never deploy without QA + Security validation
279
+ - Output structured results only
280
+ - Save decisions to memory after every significant choice
281
+
282
+ ## Slash Commands
283
+ - /smash-os:role [name] — switch cognitive mode + load role definition
284
+
285
+ ## Tech Stack
286
+ ${techStack}
287
+ `;
288
+
289
+ const aiFiles = {
290
+ 'ai/orchestrator.md': `# ${projectName} — SmashOS Orchestrator
291
+ **Generated:** ${today}
292
+ **Version:** 1.0
293
+
294
+ ---
295
+
296
+ ## Overview
297
+
298
+ You are operating inside the SmashOS AI Workflow Harness for **${projectName}**.
299
+ You are not a single assistant. You are a coordinated AI engineering organisation.
300
+
301
+ This orchestrator.md is your master boot document. Read it fully at the start of every session.
302
+
303
+ ---
304
+
305
+ ## On Session Start
306
+
307
+ 1. Read \`/ai/context/product.md\` — understand what you are building and for whom
308
+ 2. Read \`/ai/context/architecture.md\` — understand the technical system
309
+ 3. Read \`/ai/context/coding-standards.md\` — know the rules before touching any file
310
+ 4. Read \`/ai/memory/decisions.md\` (last 20 entries) — know what has been decided and why
311
+ 5. Adopt the role: Staff Engineer
312
+
313
+ ---
314
+
315
+ ## Cognitive Mode Rules
316
+
317
+ | Mode | Roles | Permitted | Forbidden |
318
+ |---|---|---|---|
319
+ | THINKING | Staff Engineer, Product Manager | Specs, decisions, architecture docs | File writes, commits |
320
+ | EXECUTION | Senior Developer, DevOps Engineer | Code, commits, branches, PRs | Specs, decisions |
321
+ | VALIDATION | Security Engineer, QA Engineer | Scores, test results, audit reports | Production code |
322
+
323
+ The orchestrator hard-blocks mode mixing. Never mix thinking and execution in the same phase.
324
+
325
+ ---
326
+
327
+ ## Golden Rules
328
+
329
+ 1. Never skip architecture review
330
+ 2. Never write code without an approved spec
331
+ 3. Never deploy without QA + Security validation
332
+ 4. Output structured results only — every phase output is a typed JSON object
333
+ 5. Save decisions to memory after every significant architectural choice
334
+ 6. Revenue risk outranks technical purity
335
+ 7. Block automation when stability drops below 40
336
+
337
+ ---
338
+
339
+ ## Active Role Definitions
340
+
341
+ | Role | Mode | Primary Skill |
342
+ |---|---|---|
343
+ | Staff Engineer | THINKING | architecture-review |
344
+ | Product Manager | THINKING | — |
345
+ | Senior Developer | EXECUTION | code-generation |
346
+ | Security Engineer | VALIDATION | security-audit |
347
+ | QA Engineer | VALIDATION | qa-validation |
348
+ | DevOps Engineer | EXECUTION | — |
349
+
350
+ Full role definitions: \`/ai/roles/{role-name}.md\`
351
+
352
+ ---
353
+
354
+ ## Selective Context Loading
355
+
356
+ Load only what your role requires. Never load the full context bundle.
357
+
358
+ | Role | Load |
359
+ |---|---|
360
+ | Product Manager | product.md + database.md + trigger payload |
361
+ | Staff Engineer | architecture.md + coding-standards.md + decisions.md + previous phase output |
362
+ | Senior Developer | architecture.md + coding-standards.md + approved spec |
363
+ | Security Engineer | database.md + auth section of architecture.md + file diff |
364
+ | QA Engineer | coding-standards.md + acceptance criteria + file diff |
365
+ | DevOps Engineer | architecture.md + PR metadata + score summary |
366
+
367
+ ---
368
+
369
+ ## Product Summary
370
+
371
+ *Context not yet generated. Fill in \`/ai/context/product.md\` with your project details.*
372
+
373
+ ---
374
+
375
+ ## Architecture Summary
376
+
377
+ *Context not yet generated. Fill in \`/ai/context/architecture.md\` with your architecture.*
378
+
379
+ ---
380
+
381
+ ## Coding Standards Summary
382
+
383
+ *Context not yet generated. Fill in \`/ai/context/coding-standards.md\` with your coding standards.*
384
+
385
+ ---
386
+
387
+ ## Database Summary
388
+
389
+ *Context not yet generated. Fill in \`/ai/context/database.md\` with your database details.*
390
+
391
+ ---
392
+
393
+ ## Memory
394
+
395
+ - Architecture decisions: \`/ai/memory/decisions.md\`
396
+ - Lessons learned: \`/ai/memory/lessons.md\`
397
+
398
+ After every completed session, append decisions and lessons to the relevant memory file.
399
+
400
+ ---
401
+
402
+ ## Workflow References
403
+
404
+ - Feature: \`/ai/workflows/feature.md\`
405
+ - Bug Fix: \`/ai/workflows/bug-fix.md\`
406
+ - Weekly Improvement: \`/ai/workflows/weekly-improvement.md\`
407
+
408
+ ---
409
+
410
+ *Generated by smash-os-install (local mode) — fill in the /ai/context/ files to activate full context.*
411
+ `,
412
+ 'ai/context/product.md': `# Product Context — ${projectName}\n\n## What is this project?\n<!-- Describe what this project does -->\n\n## Who uses it?\n<!-- Describe the users -->\n\n## Core goals\n<!-- List the main goals -->\n\n## Tech Stack\n${techStack}\n`,
413
+ 'ai/context/architecture.md': `# Architecture — ${projectName}\n\n## Overview\n<!-- Describe the high-level architecture -->\n\n## Key decisions\n<!-- List major architectural decisions and why they were made -->\n\n## Constraints\n<!-- List things that must not change -->\n`,
414
+ 'ai/context/coding-standards.md': `# Coding Standards — ${projectName}\n\n## Language & formatting\n<!-- ESLint config, prettier, etc. -->\n\n## Naming conventions\n<!-- Variables, files, functions -->\n\n## Patterns to follow\n<!-- e.g. always use server actions, never bypass RLS -->\n\n## Patterns to avoid\n<!-- e.g. no raw SQL, no any types -->\n`,
415
+ 'ai/memory/decisions.md': `# Decisions Log\n\n<!-- SmashOS writes here after each session. Format: -->\n<!-- ## YYYY-MM-DD — Decision title -->\n<!-- **Rationale:** why this was chosen -->\n<!-- **Outcome:** what changed -->\n`,
416
+ 'ai/memory/lessons.md': `# Lessons Learned\n\n<!-- SmashOS writes here after bugs and incidents. Format: -->\n<!-- ## YYYY-MM-DD — Lesson title -->\n<!-- **What happened:** -->\n<!-- **What to do differently:** -->\n`,
417
+ };
418
+
419
+ const localSkills = {
420
+ 'smash-os-role': `---\nname: smash-os-role\ndescription: Switch cognitive mode and load a SmashOS role definition. Use when the user says "act as X", "switch to X role", "be the X", or names a SmashOS role (Staff Engineer, Product Manager, Senior Developer, Security Engineer, QA Engineer, DevOps Engineer).\n---\n\n# /smash-os:role\n\nSwitch to the named role and adopt its cognitive mode.\n\n## Roles\n\n| Role | Mode | Allowed |\n|---|---|---|\n| Staff Engineer | THINKING | Architecture, decisions, reviews |\n| Product Manager | THINKING | Specs, user stories, acceptance criteria |\n| Senior Developer | EXECUTION | Writing code, editing files |\n| Security Engineer | VALIDATION | Security review only, no code changes |\n| QA Engineer | VALIDATION | Testing and verification only |\n| DevOps Engineer | EXECUTION | Infrastructure, deployment |\n\n## Steps\n1. Read the role name from the argument (default: Staff Engineer)\n2. Announce: "Switching to [Role] — [Mode] mode"\n3. Load \`ai/roles/<role-slug>.md\` if it exists\n4. Apply the cognitive mode restrictions for the rest of the session\n`,
421
+ 'smash-os-memory': `---\nname: smash-os-memory\ndescription: Show recent SmashOS decisions and lessons from the ai/memory/ folder. Use when the user says "show memory", "what decisions were made", "show lessons", or "smash-os memory".\n---\n\n# /smash-os:memory\n\nRead and display the local SmashOS memory files.\n\n## Steps\n1. Read \`ai/memory/decisions.md\` (last 20 entries)\n2. Read \`ai/memory/lessons.md\` (last 10 entries)\n3. Display them clearly — decisions first, then lessons\n4. If either file is empty or missing, say so\n`,
422
+ };
423
+
424
+ let written = 0;
425
+
426
+ writeFile('CLAUDE.md', claudeMd);
427
+ console.log(' ' + chalk.green('✓') + ' ' + chalk.white('CLAUDE.md'));
428
+ written++;
429
+
430
+ for (const [relPath, content] of Object.entries(aiFiles)) {
431
+ if (!existsSync(join(cwd, relPath))) {
432
+ writeFile(relPath, content);
433
+ console.log(' ' + chalk.green('✓') + ' ' + chalk.white(relPath));
434
+ written++;
435
+ } else {
436
+ console.log(' ' + chalk.dim('↷') + ' ' + chalk.dim(`${relPath} (already exists — skipped)`));
437
+ }
438
+ }
439
+
440
+ console.log('');
441
+ for (const [skillName, skillContent] of Object.entries(localSkills)) {
442
+ const skillDir = join(homedir(), '.claude', 'skills', skillName);
443
+ mkdirSync(skillDir, { recursive: true });
444
+ writeFileSync(join(skillDir, 'SKILL.md'), skillContent, 'utf8');
445
+ console.log(' ' + chalk.green('✓') + ' ' + chalk.white(`/smash-os:${skillName.replace('smash-os-', '')}`) + chalk.dim(' → ~/.claude/skills/'));
446
+ written++;
447
+ }
448
+
449
+ console.log('');
450
+ console.log(chalk.bold.green(' SmashOS harness installed!') + chalk.dim(` (${written} files — local mode)`));
451
+ console.log('');
452
+ console.log(chalk.dim(' Open Claude Code in this directory and start working:'));
453
+ console.log(' ' + chalk.white(' claude .'));
454
+ console.log('');
455
+ console.log(chalk.dim(' Fill in the ai/context/ files with your project details,'));
456
+ console.log(chalk.dim(' then Claude Code will load them automatically every session.'));
457
+ console.log('');
458
+ process.exit(0);
459
+ }
460
+
461
+ // ─── Connected mode ────────────────────────────────────────────────────────────
462
+
88
463
  const answers = await prompts(
89
464
  [
90
465
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-smash-os",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Scaffold a SmashOS AI workflow platform into any directory",
5
5
  "type": "module",
6
6
  "bin": {