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.
- package/bin/claude-craft.js +2 -0
- package/package.json +1 -1
- package/src/commands/auth.js +1 -1
- package/src/commands/create.js +37 -10
- package/src/commands/install.js +22 -4
- package/src/constants.js +23 -0
- package/src/prompts/gather.js +17 -0
- package/src/ui/cards.js +41 -19
- package/src/utils/api-client.js +1 -1
- package/src/utils/preflight.js +1 -1
package/bin/claude-craft.js
CHANGED
|
@@ -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
package/src/commands/auth.js
CHANGED
|
@@ -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
|
|
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_'));
|
package/src/commands/create.js
CHANGED
|
@@ -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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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(
|
|
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(
|
|
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();
|
package/src/commands/install.js
CHANGED
|
@@ -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
|
-
// ──
|
|
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 = [
|
package/src/prompts/gather.js
CHANGED
|
@@ -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 (
|
|
127
|
+
if (isVibe) {
|
|
128
|
+
lines.push(chalk.bold.green(' Claude is ready to build your app!'));
|
|
131
129
|
lines.push('');
|
|
132
|
-
lines.push(chalk.
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
package/src/utils/api-client.js
CHANGED
|
@@ -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
|
|
13
|
+
return 'https://api.claude-craft.cc';
|
|
14
14
|
}
|
|
15
15
|
const TIMEOUT_MS = 30_000;
|
|
16
16
|
|
package/src/utils/preflight.js
CHANGED
|
@@ -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
|
|
79
|
+
const serverUrl = 'https://api.claude-craft.cc';
|
|
80
80
|
|
|
81
81
|
try {
|
|
82
82
|
const valid = await validateKey(apiConfig.apiKey, serverUrl);
|