ccraft 1.0.9 → 1.0.11

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.
@@ -64,6 +64,7 @@ program
64
64
  .option('-y, --yes', 'Accept all defaults (non-interactive)')
65
65
  .option('-n, --name <name>', 'Project name (non-interactive mode)')
66
66
  .option('--description <text>', 'Project description (non-interactive mode)')
67
+ .option('--pro', 'Developer mode — skip persona selection, show all options')
67
68
  .option('-d, --dir <path>', 'Parent directory to create the project in (default: cwd)')
68
69
  .action(runCreate);
69
70
 
@@ -72,6 +73,7 @@ program
72
73
  .description('Generate Claude Code configuration files in the current project')
73
74
  .option('-y, --yes', 'Accept all defaults (non-interactive)')
74
75
  .option(`-p, --preset <preset>`, `Apply a framework preset (${Object.keys(PRESET_ALIASES).join(', ')})`)
76
+ .option('--pro', 'Developer mode — skip persona selection, show all options')
75
77
  .option('-d, --dir <path>', 'Target directory (default: cwd)')
76
78
  .action(runInstall);
77
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccraft",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Intelligent Claude Code project configurator — role-aware agents, skills, rules, MCPs, and workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,7 @@ import { validateKey, saveConfig, loadConfig, ApiError } from '../utils/api-clie
8
8
  import * as logger from '../utils/logger.js';
9
9
 
10
10
  export async function runAuth(key, options = {}) {
11
- const serverUrl = 'https://claude-craft-server.up.railway.app';
11
+ const serverUrl = 'https://api.claude-craft.cc';
12
12
 
13
13
  if (!key.startsWith('ck_live_')) {
14
14
  logger.error('Invalid key format. API keys must start with ' + chalk.bold('ck_live_'));
@@ -3,7 +3,7 @@ import { mkdirSync, existsSync, writeFileSync, readdirSync } from 'fs';
3
3
  import { execFileSync } from 'child_process';
4
4
  import chalk from 'chalk';
5
5
  import ora from 'ora';
6
- import { gatherCreateProfile, gatherMcpConfig, confirmInstallation } from '../prompts/gather.js';
6
+ import { gatherCreateProfile, gatherPersona, gatherUserProfile, gatherMcpConfig, confirmInstallation } from '../prompts/gather.js';
7
7
  import { themedInput } from '../ui/prompts.js';
8
8
  import { callGenerate, ApiError } from '../utils/api-client.js';
9
9
  import { runPreflight } from '../utils/preflight.js';
@@ -25,7 +25,7 @@ import {
25
25
  promoteCache,
26
26
  cleanupAnalysisCache,
27
27
  } from '../utils/analysis-cache.js';
28
- import { VERSION } from '../constants.js';
28
+ import { VERSION, VIBE_DEFAULTS } from '../constants.js';
29
29
  import * as logger from '../utils/logger.js';
30
30
 
31
31
  // UI modules
@@ -170,12 +170,34 @@ export async function runCreate(options = {}) {
170
170
  : `Created ${chalk.bold(name)}/ (git init skipped — git not available).`);
171
171
  }
172
172
 
173
+ // ── Persona selection ────────────────────────────────────────────
174
+ let persona;
175
+ if (options.yes) {
176
+ persona = 'developer'; // non-interactive: always developer defaults
177
+ } else if (options.pro) {
178
+ persona = 'developer'; // interactive developer mode: skip persona prompt
179
+ } else {
180
+ persona = await gatherPersona();
181
+ }
182
+
183
+ const isVibe = persona === 'vibe';
184
+
173
185
  // Build user profile
174
- const userProfile = {
175
- intents: ['implementing', 'debugging', 'refactoring', 'testing', 'reviewing'],
176
- sourceControl: 'github',
177
- documentTools: [],
178
- };
186
+ let userProfile;
187
+ if (options.yes) {
188
+ userProfile = {
189
+ intents: ['implementing', 'debugging', 'refactoring', 'testing', 'reviewing'],
190
+ sourceControl: 'github',
191
+ documentTools: [],
192
+ };
193
+ logger.info('Non-interactive mode — using default profile.');
194
+ } else if (isVibe) {
195
+ userProfile = { ...VIBE_DEFAULTS };
196
+ logger.info('Vibe mode — using streamlined defaults.');
197
+ } else {
198
+ userProfile = await gatherUserProfile();
199
+ }
200
+ userProfile.persona = persona;
179
201
 
180
202
  // ================================================================
181
203
  // STEP 3: Configuration & Install
@@ -275,7 +297,7 @@ export async function runCreate(options = {}) {
275
297
  let mcpKeys = {};
276
298
  const securityConfig = { addSecurityGitignore: true };
277
299
 
278
- if (options.yes) {
300
+ if (options.yes || isVibe) {
279
301
  selectedMcps = mcpConfigs.filter(
280
302
  (m) => m.tier === 'core' || m.tier === 'role' || m.tier === 'stack' || m.tier === 'auto' || m.recommended,
281
303
  );
@@ -478,14 +500,19 @@ export async function runCreate(options = {}) {
478
500
  totalItems,
479
501
  mcpCount: selectedMcps.length,
480
502
  mcpsNeedingKeys,
503
+ persona,
481
504
  });
482
505
 
483
506
  console.log();
484
507
  if (bootstrapSucceeded) {
485
- logger.success(`Project ${chalk.bold(name)} created and bootstrapped!`);
508
+ logger.success(isVibe
509
+ ? `Project ${chalk.bold(name)} is ready! Start Claude and describe what you want.`
510
+ : `Project ${chalk.bold(name)} created and bootstrapped!`);
486
511
  console.log(chalk.dim(` cd ${name} && claude`));
487
512
  } else {
488
- logger.success(`Project ${chalk.bold(name)} created with Claude configuration.`);
513
+ logger.success(isVibe
514
+ ? `Project ${chalk.bold(name)} is ready! Run Claude to finish setup.`
515
+ : `Project ${chalk.bold(name)} created with Claude configuration.`);
489
516
  console.log(chalk.dim(` cd ${name} && claude -p "/bootstrap:auto ${description}"`));
490
517
  }
491
518
  console.log();
@@ -11,6 +11,7 @@ import {
11
11
  } from '../utils/existing-setup.js';
12
12
  import {
13
13
  gatherProjectPath,
14
+ gatherPersona,
14
15
  gatherUserProfile,
15
16
  gatherMcpConfig,
16
17
  gatherSecurityConfig,
@@ -30,7 +31,7 @@ import {
30
31
  promoteCache,
31
32
  cleanupAnalysisCache,
32
33
  } from '../utils/analysis-cache.js';
33
- import { PRESET_ALIASES, VERSION } from '../constants.js';
34
+ import { PRESET_ALIASES, VERSION, VIBE_DEFAULTS } from '../constants.js';
34
35
  import * as logger from '../utils/logger.js';
35
36
 
36
37
  // UI modules
@@ -72,16 +73,32 @@ export async function runInstall(options = {}) {
72
73
  targetDir = resolve(options.dir || process.cwd());
73
74
  }
74
75
 
75
- // ── User profile ───────────────────────────────────────────────
76
+ // ── Persona selection ────────────────────────────────────────────
76
77
  renderPhaseHeader(1);
77
78
 
79
+ let persona;
80
+ if (options.yes) {
81
+ persona = 'developer'; // non-interactive: always developer defaults
82
+ } else if (options.pro) {
83
+ persona = 'developer'; // interactive developer mode: skip persona prompt
84
+ } else {
85
+ persona = await gatherPersona();
86
+ }
87
+
88
+ const isVibe = persona === 'vibe';
89
+
90
+ // ── User profile ───────────────────────────────────────────────
78
91
  let userProfile;
79
92
  if (options.yes) {
80
93
  userProfile = { intents: ['implementing', 'debugging', 'refactoring', 'testing', 'reviewing'], sourceControl: 'github', documentTools: [] };
81
94
  logger.info('Non-interactive mode — using default profile (web, all intents, github).');
95
+ } else if (isVibe) {
96
+ userProfile = { ...VIBE_DEFAULTS };
97
+ logger.info('Vibe mode — using streamlined defaults.');
82
98
  } else {
83
99
  userProfile = await gatherUserProfile();
84
100
  }
101
+ userProfile.persona = persona;
85
102
 
86
103
  // ================================================================
87
104
  // PHASE 2: Project Discovery
@@ -264,7 +281,7 @@ export async function runInstall(options = {}) {
264
281
  let mcpKeys = {};
265
282
  let securityConfig;
266
283
 
267
- if (options.yes) {
284
+ if (options.yes || isVibe) {
268
285
  selectedMcps = mcpConfigs.filter(
269
286
  (m) => m.tier === 'core' || m.tier === 'role' || m.tier === 'stack' || m.tier === 'auto' || m.recommended
270
287
  );
@@ -424,10 +441,11 @@ export async function runInstall(options = {}) {
424
441
  totalItems,
425
442
  mcpCount: selectedMcps.length,
426
443
  mcpsNeedingKeys,
444
+ persona,
427
445
  });
428
446
 
429
447
  console.log();
430
- logger.success('Done! Claude Code is ready.');
448
+ logger.success(isVibe ? 'Done! Start Claude Code and describe what you want to build.' : 'Done! Claude Code is ready.');
431
449
  console.log();
432
450
  } catch (err) {
433
451
  if (
package/src/constants.js CHANGED
@@ -20,6 +20,29 @@ export const INTENTS = [
20
20
  { name: 'Code review', value: 'reviewing', description: 'Review PRs, suggest improvements, catch issues' },
21
21
  ];
22
22
 
23
+ // ── Personas ────────────────────────────────────────────────────────
24
+
25
+ export const PERSONAS = [
26
+ {
27
+ name: 'I\'m building something new — describe what I want, Claude handles the rest',
28
+ value: 'vibe',
29
+ description: 'Streamlined setup with smart defaults',
30
+ },
31
+ {
32
+ name: 'I\'m a developer — full control over agents, skills, MCPs, and workflows',
33
+ value: 'developer',
34
+ description: 'All configuration options available',
35
+ },
36
+ ];
37
+
38
+ export const VIBE_DEFAULTS = {
39
+ // Intentionally narrowed to core intents for non-technical users.
40
+ // Full intent list is available in the developer path via gatherUserProfile().
41
+ intents: ['implementing', 'debugging'],
42
+ sourceControl: 'github',
43
+ documentTools: [],
44
+ };
45
+
23
46
  // ── Source control platforms ─────────────────────────────────────────
24
47
 
25
48
  export const SOURCE_CONTROLS = [
@@ -7,12 +7,29 @@ import {
7
7
  PROJECT_TYPES,
8
8
  SOURCE_CONTROLS,
9
9
  DOCUMENT_TOOLS,
10
+ PERSONAS,
10
11
  } from '../constants.js';
11
12
  import { validateApiKeyFormat } from '../utils/mcp-setup.js';
12
13
  import { themedSelect, themedCheckbox, themedConfirm, themedPassword, themedInput } from '../ui/prompts.js';
13
14
  import { renderSummaryCard, renderWarningCard } from '../ui/cards.js';
14
15
  import { colors } from '../ui/theme.js';
15
16
 
17
+ // ── Persona selection ───────────────────────────────────────────────────
18
+
19
+ export async function gatherPersona() {
20
+ console.log();
21
+ const persona = await themedSelect({
22
+ message: 'How do you want to use Claude Code?',
23
+ choices: PERSONAS.map((p) => ({
24
+ name: p.name,
25
+ value: p.value,
26
+ description: p.description,
27
+ })),
28
+ });
29
+
30
+ return persona;
31
+ }
32
+
16
33
  // ── Project path ────────────────────────────────────────────────────────
17
34
 
18
35
  export async function gatherProjectPath() {
package/src/ui/cards.js CHANGED
@@ -120,33 +120,55 @@ export function renderSummaryCard(summary) {
120
120
  /**
121
121
  * Render the final success card.
122
122
  */
123
- export function renderSuccessCard({ totalItems, mcpCount, mcpsNeedingKeys }) {
123
+ export function renderSuccessCard({ totalItems, mcpCount, mcpsNeedingKeys, persona }) {
124
+ const isVibe = persona === 'vibe';
124
125
  const lines = [];
125
- lines.push(chalk.bold.green(' Installation complete!'));
126
- lines.push('');
127
- lines.push(` ${chalk.dim('Settings installed'.padEnd(22))}${colors.success(String(totalItems))} ${chalk.dim('(guaranteed + selected)')}`);
128
- lines.push(` ${chalk.dim('MCP servers'.padEnd(22))}${colors.success(String(mcpCount))}`);
129
126
 
130
- if (mcpsNeedingKeys.length > 0) {
127
+ if (isVibe) {
128
+ lines.push(chalk.bold.green(' Claude is ready to build your app!'));
131
129
  lines.push('');
132
- lines.push(chalk.yellow(' MCP servers needing API keys:'));
133
- for (const r of mcpsNeedingKeys) {
134
- lines.push(chalk.yellow(` ${chalk.dim('•')} ${r.id}: set ${r.apiKey.keyName}`));
130
+ lines.push(` ${chalk.dim('Components installed'.padEnd(22))}${colors.success(String(totalItems))}`);
131
+ lines.push(` ${chalk.dim('MCP servers'.padEnd(22))}${colors.success(String(mcpCount))}`);
132
+
133
+ if (mcpsNeedingKeys.length > 0) {
134
+ lines.push('');
135
+ lines.push(chalk.yellow(' Some MCP servers need an API key:'));
136
+ for (const r of mcpsNeedingKeys) {
137
+ lines.push(chalk.yellow(` ${chalk.dim('•')} ${r.id}: set ${r.apiKey.keyName}`));
138
+ }
135
139
  }
136
- }
137
140
 
138
- lines.push('');
139
- lines.push(chalk.dim(' Next steps:'));
140
- lines.push(chalk.dim(` 1. Read ${chalk.underline('USER_GUIDE.md')} for a full feature overview`));
141
+ lines.push('');
142
+ lines.push(chalk.dim(' Next steps:'));
143
+ lines.push(chalk.dim(` 1. Start Claude Code and describe what you want to build`));
144
+ lines.push(chalk.dim(` 2. Claude will handle the rest!`));
145
+ } else {
146
+ lines.push(chalk.bold.green(' Installation complete!'));
147
+ lines.push('');
148
+ lines.push(` ${chalk.dim('Settings installed'.padEnd(22))}${colors.success(String(totalItems))} ${chalk.dim('(guaranteed + selected)')}`);
149
+ lines.push(` ${chalk.dim('MCP servers'.padEnd(22))}${colors.success(String(mcpCount))}`);
150
+
151
+ if (mcpsNeedingKeys.length > 0) {
152
+ lines.push('');
153
+ lines.push(chalk.yellow(' MCP servers needing API keys:'));
154
+ for (const r of mcpsNeedingKeys) {
155
+ lines.push(chalk.yellow(` ${chalk.dim('•')} ${r.id}: set ${r.apiKey.keyName}`));
156
+ }
157
+ }
158
+
159
+ lines.push('');
160
+ lines.push(chalk.dim(' Next steps:'));
161
+ lines.push(chalk.dim(` 1. Read ${chalk.underline('USER_GUIDE.md')} for a full feature overview`));
141
162
 
142
- let step = 2;
143
- if (mcpsNeedingKeys.length > 0) {
144
- lines.push(chalk.dim(` ${step}. Set missing API keys for MCP servers (see above)`));
163
+ let step = 2;
164
+ if (mcpsNeedingKeys.length > 0) {
165
+ lines.push(chalk.dim(` ${step}. Set missing API keys for MCP servers (see above)`));
166
+ step++;
167
+ }
168
+ lines.push(chalk.dim(` ${step}. Start Claude Code and try out some commands!`));
145
169
  step++;
170
+ lines.push(chalk.dim(` ${step}. Customize .claude/ to your needs`));
146
171
  }
147
- lines.push(chalk.dim(` ${step}. Start Claude Code and try out some commands!`));
148
- step++;
149
- lines.push(chalk.dim(` ${step}. Customize .claude/ to your needs`));
150
172
 
151
173
  const content = lines.join('\n');
152
174
 
@@ -10,7 +10,7 @@ import { VERSION } from '../constants.js';
10
10
  const CONFIG_DIR = join(homedir(), '.claude-craft');
11
11
  const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
12
12
  function getDefaultServerUrl() {
13
- return 'https://claude-craft-server.up.railway.app';
13
+ return 'https://api.claude-craft.cc';
14
14
  }
15
15
  const TIMEOUT_MS = 30_000;
16
16
 
@@ -76,7 +76,7 @@ export async function runPreflight(options = {}) {
76
76
 
77
77
  // ── 4. API key validation (also proves server is reachable) ─────────
78
78
  if (apiConfig?.apiKey) {
79
- const serverUrl = 'https://claude-craft-server.up.railway.app';
79
+ const serverUrl = 'https://api.claude-craft.cc';
80
80
 
81
81
  try {
82
82
  const valid = await validateKey(apiConfig.apiKey, serverUrl);