@nclamvn/vibecode-cli 1.1.0 → 2.0.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/bin/vibecode.js CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  buildCommand,
17
17
  reviewCommand,
18
18
  snapshotCommand,
19
+ configCommand,
19
20
  VERSION
20
21
  } from '../src/index.js';
21
22
 
@@ -77,6 +78,8 @@ program
77
78
  .option('-s, --start', 'Start the build process')
78
79
  .option('-c, --complete', 'Mark build as complete')
79
80
  .option('-e, --evidence', 'Capture evidence snapshot')
81
+ .option('-a, --auto', 'Auto-build with Claude Code (--dangerously-skip-permissions)')
82
+ .option('--provider <name>', 'Provider to use: claude-code', 'claude-code')
80
83
  .action(buildCommand);
81
84
 
82
85
  program
@@ -93,6 +96,17 @@ program
93
96
  .option('--major', 'Major version bump')
94
97
  .action(snapshotCommand);
95
98
 
99
+ // ─────────────────────────────────────────────────────────────────────────────
100
+ // Phase C Commands - AI Integration
101
+ // ─────────────────────────────────────────────────────────────────────────────
102
+
103
+ program
104
+ .command('config')
105
+ .description('Manage Vibecode configuration')
106
+ .option('--show', 'Show current configuration')
107
+ .option('--provider <name>', 'Set default AI provider')
108
+ .action(configCommand);
109
+
96
110
  // ─────────────────────────────────────────────────────────────────────────────
97
111
  // Parse
98
112
  // ─────────────────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nclamvn/vibecode-cli",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Build software with discipline - AI coding with guardrails",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,5 +1,6 @@
1
1
  // ═══════════════════════════════════════════════════════════════════════════════
2
2
  // VIBECODE CLI - Build Command
3
+ // "Claude/LLM là PIPELINE, là KIẾN TRÚC SƯ"
3
4
  // ═══════════════════════════════════════════════════════════════════════════════
4
5
 
5
6
  import chalk from 'chalk';
@@ -12,14 +13,21 @@ import {
12
13
  getCurrentSessionId,
13
14
  getCurrentSessionPath,
14
15
  writeSessionFile,
15
- sessionFileExists
16
+ sessionFileExists,
17
+ readSessionFile
16
18
  } from '../core/session.js';
17
19
  import { getCurrentState, transitionTo } from '../core/state-machine.js';
18
20
  import { getSpecHash } from '../core/contract.js';
19
21
  import { STATES } from '../config/constants.js';
20
22
  import { getBuildReportTemplate } from '../config/templates.js';
21
- import { ensureDir, pathExists, appendToFile } from '../utils/files.js';
23
+ import { ensureDir, pathExists, appendToFile, readMarkdown } from '../utils/files.js';
22
24
  import { printBox, printError, printSuccess, printWarning, printNextStep } from '../ui/output.js';
25
+ import {
26
+ spawnClaudeCode,
27
+ isClaudeCodeAvailable,
28
+ buildPromptWithContext,
29
+ getProviderInfo
30
+ } from '../providers/index.js';
23
31
 
24
32
  const execAsync = promisify(exec);
25
33
 
@@ -38,7 +46,9 @@ export async function buildCommand(options = {}) {
38
46
  const specHash = await getSpecHash();
39
47
 
40
48
  // Handle different build modes
41
- if (options.start) {
49
+ if (options.auto) {
50
+ await handleAutoBuild(currentState, projectName, sessionId, sessionPath, specHash, options);
51
+ } else if (options.start) {
42
52
  await handleBuildStart(currentState, projectName, sessionId, sessionPath, specHash);
43
53
  } else if (options.complete) {
44
54
  await handleBuildComplete(currentState, projectName, sessionId, sessionPath, specHash);
@@ -54,6 +64,158 @@ export async function buildCommand(options = {}) {
54
64
  }
55
65
  }
56
66
 
67
+ /**
68
+ * Handle --auto mode: Spawn Claude Code with optimal config
69
+ * "Contract LOCKED = License to build"
70
+ */
71
+ async function handleAutoBuild(currentState, projectName, sessionId, sessionPath, specHash, options) {
72
+ // Check state - must be PLAN_CREATED or BUILD_IN_PROGRESS or REVIEW_FAILED
73
+ const validStates = [STATES.PLAN_CREATED, STATES.BUILD_IN_PROGRESS, STATES.REVIEW_FAILED];
74
+ if (!validStates.includes(currentState)) {
75
+ printError(`Cannot auto-build in state: ${currentState}`);
76
+ console.log('Run `vibecode plan` first to create execution plan.');
77
+ process.exit(1);
78
+ }
79
+
80
+ // Check if Claude Code is available
81
+ const available = await isClaudeCodeAvailable();
82
+ if (!available) {
83
+ printError('Claude Code CLI not found.');
84
+ console.log(chalk.gray('Install with: npm install -g @anthropic-ai/claude-code'));
85
+ console.log(chalk.gray('Or use manual build: vibecode build --start'));
86
+ process.exit(1);
87
+ }
88
+
89
+ // Check coder_pack.md exists
90
+ if (!await sessionFileExists('coder_pack.md')) {
91
+ printError('coder_pack.md not found. Run `vibecode plan` first.');
92
+ process.exit(1);
93
+ }
94
+
95
+ // Setup evidence directory
96
+ const evidencePath = path.join(sessionPath, 'evidence');
97
+ await ensureDir(evidencePath);
98
+ await ensureDir(path.join(evidencePath, 'screenshots'));
99
+ const logPath = path.join(evidencePath, 'build.log');
100
+
101
+ // Save build start time
102
+ const stateData = await loadState();
103
+ const startTime = new Date().toISOString();
104
+ stateData.build_started = startTime;
105
+ stateData.build_provider = 'claude-code';
106
+ stateData.build_mode = 'auto';
107
+ await saveState(stateData);
108
+
109
+ // Transition to BUILD_IN_PROGRESS if not already
110
+ if (currentState !== STATES.BUILD_IN_PROGRESS) {
111
+ await transitionTo(STATES.BUILD_IN_PROGRESS, 'auto_build_started');
112
+ }
113
+
114
+ // Load coder pack
115
+ const coderPackContent = await readSessionFile('coder_pack.md');
116
+
117
+ // Build full prompt with CLAUDE.md injection if exists
118
+ const fullPrompt = await buildPromptWithContext(coderPackContent, process.cwd());
119
+
120
+ // Log start
121
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
122
+ await appendToFile(logPath, `AUTO BUILD STARTED: ${startTime}\n`);
123
+ await appendToFile(logPath, `Provider: Claude Code (--dangerously-skip-permissions)\n`);
124
+ await appendToFile(logPath, `${'='.repeat(60)}\n\n`);
125
+
126
+ // Show starting message
127
+ const providerInfo = getProviderInfo();
128
+
129
+ const content = `🤖 AUTO BUILD
130
+
131
+ Project: ${projectName}
132
+ Session: ${sessionId}
133
+ Spec Hash: ${specHash}
134
+
135
+ Provider: ${providerInfo.name}
136
+ Mode: ${providerInfo.mode}
137
+
138
+ Starting AI build session...
139
+ Contract LOCKED = License to build`;
140
+
141
+ console.log();
142
+ printBox(content, { borderColor: 'magenta' });
143
+ console.log();
144
+ console.log(chalk.magenta('─'.repeat(60)));
145
+ console.log(chalk.magenta('│ CLAUDE CODE OUTPUT'));
146
+ console.log(chalk.magenta('─'.repeat(60)));
147
+ console.log();
148
+
149
+ // Spawn Claude Code
150
+ try {
151
+ const result = await spawnClaudeCode(fullPrompt, {
152
+ cwd: process.cwd(),
153
+ logPath: logPath
154
+ });
155
+
156
+ console.log();
157
+ console.log(chalk.magenta('─'.repeat(60)));
158
+ console.log();
159
+
160
+ // Capture evidence
161
+ await captureGitDiff(evidencePath);
162
+
163
+ const endTime = new Date().toISOString();
164
+ await appendToFile(logPath, `\n${'='.repeat(60)}\n`);
165
+ await appendToFile(logPath, `AUTO BUILD COMPLETED: ${endTime}\n`);
166
+ await appendToFile(logPath, `Exit code: ${result.code}\n`);
167
+ await appendToFile(logPath, `${'='.repeat(60)}\n`);
168
+
169
+ // Check evidence
170
+ const evidence = await checkEvidence(evidencePath);
171
+
172
+ // Generate build report
173
+ const reportContent = getBuildReportTemplate(
174
+ projectName,
175
+ sessionId,
176
+ specHash,
177
+ startTime,
178
+ endTime,
179
+ evidence
180
+ );
181
+ await writeSessionFile('build_report.md', reportContent);
182
+
183
+ // Update state
184
+ stateData.build_completed = endTime;
185
+ await saveState(stateData);
186
+
187
+ if (result.success) {
188
+ await transitionTo(STATES.BUILD_DONE, 'auto_build_completed');
189
+
190
+ const duration = Math.round((new Date(endTime) - new Date(startTime)) / 1000 / 60);
191
+
192
+ const successContent = `✅ AUTO BUILD COMPLETED
193
+
194
+ Project: ${projectName}
195
+ Duration: ${duration} minutes
196
+ Provider: Claude Code
197
+
198
+ Evidence:
199
+ ${evidence.hasDiff ? ' ✅ changes.diff' : ' ⬜ changes.diff'}
200
+ ${evidence.hasLog ? ' ✅ build.log' : ' ⬜ build.log'}
201
+ ${evidence.screenshots > 0 ? ` ✅ ${evidence.screenshots} screenshots` : ' ⬜ No screenshots'}`;
202
+
203
+ printBox(successContent, { borderColor: 'green' });
204
+ printNextStep('Run `vibecode review` to validate your build');
205
+
206
+ } else {
207
+ printWarning(`Claude Code exited with code: ${result.code}`);
208
+ console.log(chalk.gray('Check build.log for details.'));
209
+ console.log(chalk.gray('Run `vibecode build --auto` to retry.'));
210
+ }
211
+
212
+ } catch (error) {
213
+ await appendToFile(logPath, `\nERROR: ${error.message}\n`);
214
+ printError(`Auto build failed: ${error.message}`);
215
+ process.exit(1);
216
+ }
217
+ }
218
+
57
219
  async function handleBuildStart(currentState, projectName, sessionId, sessionPath, specHash) {
58
220
  const spinner = ora('Starting build...').start();
59
221
 
@@ -0,0 +1,149 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Config Command
3
+ // ═══════════════════════════════════════════════════════════════════════════════
4
+
5
+ import chalk from 'chalk';
6
+ import path from 'path';
7
+ import { workspaceExists, getWorkspacePath } from '../core/workspace.js';
8
+ import { pathExists, readJson, writeJson } from '../utils/files.js';
9
+ import { PROVIDERS, getDefaultProvider } from '../providers/index.js';
10
+ import { printBox, printError, printSuccess, printInfo } from '../ui/output.js';
11
+
12
+ const CONFIG_FILE = 'config.json';
13
+
14
+ /**
15
+ * Get config file path
16
+ */
17
+ function getConfigPath() {
18
+ return path.join(getWorkspacePath(), CONFIG_FILE);
19
+ }
20
+
21
+ /**
22
+ * Load config
23
+ */
24
+ async function loadConfig() {
25
+ const configPath = getConfigPath();
26
+ if (await pathExists(configPath)) {
27
+ return await readJson(configPath);
28
+ }
29
+ return getDefaultConfig();
30
+ }
31
+
32
+ /**
33
+ * Save config
34
+ */
35
+ async function saveConfig(config) {
36
+ const configPath = getConfigPath();
37
+ await writeJson(configPath, config);
38
+ }
39
+
40
+ /**
41
+ * Get default config
42
+ */
43
+ function getDefaultConfig() {
44
+ return {
45
+ provider: 'claude-code',
46
+ claudeCode: {
47
+ flags: ['--dangerously-skip-permissions'],
48
+ timeout: 30 // minutes
49
+ },
50
+ autoEvidence: true,
51
+ verbose: false
52
+ };
53
+ }
54
+
55
+ export async function configCommand(options = {}) {
56
+ try {
57
+ // Check workspace for set operations
58
+ if (options.provider || options.set) {
59
+ if (!await workspaceExists()) {
60
+ printError('No Vibecode workspace found. Run `vibecode init` first.');
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ // Show current config
66
+ if (options.show || (!options.provider && !options.set)) {
67
+ await showConfig();
68
+ return;
69
+ }
70
+
71
+ // Set provider
72
+ if (options.provider) {
73
+ await setProvider(options.provider);
74
+ return;
75
+ }
76
+
77
+ } catch (error) {
78
+ printError(error.message);
79
+ process.exit(1);
80
+ }
81
+ }
82
+
83
+ async function showConfig() {
84
+ console.log();
85
+
86
+ // Check if workspace exists
87
+ if (!await workspaceExists()) {
88
+ // Show default config
89
+ console.log(chalk.cyan('Default Configuration (no workspace):'));
90
+ console.log();
91
+ const defaultConfig = getDefaultConfig();
92
+ console.log(chalk.gray('Provider: ') + chalk.white(defaultConfig.provider));
93
+ console.log(chalk.gray('Flags: ') + chalk.white(defaultConfig.claudeCode.flags.join(', ')));
94
+ console.log(chalk.gray('Timeout: ') + chalk.white(`${defaultConfig.claudeCode.timeout} minutes`));
95
+ console.log();
96
+ console.log(chalk.gray('Run `vibecode init` to create a workspace.'));
97
+ return;
98
+ }
99
+
100
+ const config = await loadConfig();
101
+
102
+ const content = `Current Configuration
103
+
104
+ Provider: ${config.provider}
105
+ Flags: ${config.claudeCode?.flags?.join(', ') || 'none'}
106
+ Timeout: ${config.claudeCode?.timeout || 30} minutes
107
+ Auto Evidence: ${config.autoEvidence ? 'enabled' : 'disabled'}
108
+ Verbose: ${config.verbose ? 'enabled' : 'disabled'}`;
109
+
110
+ printBox(content, { borderColor: 'cyan' });
111
+
112
+ // Show available providers
113
+ console.log();
114
+ console.log(chalk.cyan('Available Providers:'));
115
+ Object.entries(PROVIDERS).forEach(([key, provider]) => {
116
+ const status = provider.available ? chalk.green('available') : chalk.gray('coming soon');
117
+ const current = key === config.provider ? chalk.yellow(' (current)') : '';
118
+ console.log(chalk.gray(` • ${key}: ${provider.description} [${status}]${current}`));
119
+ });
120
+
121
+ console.log();
122
+ console.log(chalk.gray('To change: vibecode config --provider <name>'));
123
+ }
124
+
125
+ async function setProvider(providerName) {
126
+ // Validate provider
127
+ if (!PROVIDERS[providerName]) {
128
+ printError(`Unknown provider: ${providerName}`);
129
+ console.log();
130
+ console.log(chalk.cyan('Available providers:'));
131
+ Object.keys(PROVIDERS).forEach(key => {
132
+ console.log(chalk.gray(` • ${key}`));
133
+ });
134
+ process.exit(1);
135
+ }
136
+
137
+ if (!PROVIDERS[providerName].available) {
138
+ printError(`Provider "${providerName}" is not yet available.`);
139
+ process.exit(1);
140
+ }
141
+
142
+ // Load and update config
143
+ const config = await loadConfig();
144
+ config.provider = providerName;
145
+ await saveConfig(config);
146
+
147
+ printSuccess(`Provider set to: ${providerName}`);
148
+ console.log(chalk.gray(`Config saved to: ${getConfigPath()}`));
149
+ }
@@ -3,7 +3,7 @@
3
3
  // Spec Hash: 0fe43335f5a325e3279a079ce616c052
4
4
  // ═══════════════════════════════════════════════════════════════════════════════
5
5
 
6
- export const VERSION = '1.1.0';
6
+ export const VERSION = '2.0.0';
7
7
  export const SPEC_HASH = '0fe43335f5a325e3279a079ce616c052';
8
8
 
9
9
  // ─────────────────────────────────────────────────────────────────────────────
package/src/index.js CHANGED
@@ -16,4 +16,11 @@ export { buildCommand } from './commands/build.js';
16
16
  export { reviewCommand } from './commands/review.js';
17
17
  export { snapshotCommand } from './commands/snapshot.js';
18
18
 
19
+ // Phase C Commands
20
+ export { configCommand } from './commands/config.js';
21
+
22
+ // Constants
19
23
  export { VERSION, SPEC_HASH, STATES } from './config/constants.js';
24
+
25
+ // Providers
26
+ export { PROVIDERS, getProvider, getDefaultProvider } from './providers/index.js';
@@ -0,0 +1,159 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Claude Code Provider
3
+ // "Claude/LLM là PIPELINE, là KIẾN TRÚC SƯ"
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ import { spawn } from 'child_process';
7
+ import path from 'path';
8
+ import { pathExists, appendToFile } from '../utils/files.js';
9
+
10
+ /**
11
+ * Claude Code optimal configuration
12
+ * Contract LOCKED = License to build (không cần hỏi thêm)
13
+ */
14
+ export const CLAUDE_CODE_CONFIG = {
15
+ command: 'claude',
16
+ flags: [
17
+ '--dangerously-skip-permissions', // Trust the AI - Contract đã locked
18
+ ],
19
+ timeout: 30 * 60 * 1000, // 30 minutes max
20
+ };
21
+
22
+ /**
23
+ * Check if Claude Code CLI is available
24
+ */
25
+ export async function isClaudeCodeAvailable() {
26
+ return new Promise((resolve) => {
27
+ const proc = spawn('which', ['claude'], { shell: true });
28
+ proc.on('close', (code) => {
29
+ resolve(code === 0);
30
+ });
31
+ proc.on('error', () => {
32
+ resolve(false);
33
+ });
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Spawn Claude Code with optimal settings
39
+ *
40
+ * @param {string} prompt - The coder pack / prompt to send
41
+ * @param {object} options - Configuration options
42
+ * @param {string} options.cwd - Working directory
43
+ * @param {string} options.logPath - Path to write build logs
44
+ * @param {function} options.onOutput - Callback for output
45
+ * @returns {Promise<{success: boolean, code: number}>}
46
+ */
47
+ export async function spawnClaudeCode(prompt, options = {}) {
48
+ const { cwd, logPath, onOutput } = options;
49
+ const fs = await import('fs-extra');
50
+ const os = await import('os');
51
+
52
+ // Check if Claude Code is available
53
+ const available = await isClaudeCodeAvailable();
54
+ if (!available) {
55
+ throw new Error('Claude Code CLI not found. Install with: npm install -g @anthropic/claude-code');
56
+ }
57
+
58
+ // Write prompt to temp file to avoid shell escaping issues
59
+ const tempDir = os.default.tmpdir();
60
+ const promptFile = path.join(tempDir, `vibecode-prompt-${Date.now()}.md`);
61
+ await fs.default.writeFile(promptFile, prompt, 'utf-8');
62
+
63
+ return new Promise((resolve, reject) => {
64
+ // Use cat to pipe the prompt file content to claude
65
+ const command = `cat "${promptFile}" | claude ${CLAUDE_CODE_CONFIG.flags.join(' ')}`;
66
+
67
+ // Log the command being run
68
+ if (logPath) {
69
+ appendToFile(logPath, `\n[${new Date().toISOString()}] Running: claude with prompt from ${promptFile}\n`);
70
+ }
71
+
72
+ const proc = spawn(command, [], {
73
+ cwd: cwd || process.cwd(),
74
+ stdio: 'inherit', // Stream directly to terminal
75
+ shell: true,
76
+ });
77
+
78
+ let timeoutId = setTimeout(() => {
79
+ proc.kill();
80
+ // Cleanup temp file
81
+ fs.default.remove(promptFile).catch(() => {});
82
+ reject(new Error('Claude Code process timed out'));
83
+ }, CLAUDE_CODE_CONFIG.timeout);
84
+
85
+ proc.on('close', async (code) => {
86
+ clearTimeout(timeoutId);
87
+
88
+ // Cleanup temp file
89
+ await fs.default.remove(promptFile).catch(() => {});
90
+
91
+ const result = {
92
+ success: code === 0,
93
+ code: code || 0,
94
+ timestamp: new Date().toISOString()
95
+ };
96
+
97
+ if (logPath) {
98
+ const status = result.success ? 'SUCCESS' : 'FAILED';
99
+ appendToFile(logPath, `\n[${result.timestamp}] Claude Code ${status} (exit code: ${code})\n`);
100
+ }
101
+
102
+ resolve(result);
103
+ });
104
+
105
+ proc.on('error', async (error) => {
106
+ clearTimeout(timeoutId);
107
+ // Cleanup temp file
108
+ await fs.default.remove(promptFile).catch(() => {});
109
+
110
+ if (logPath) {
111
+ appendToFile(logPath, `\n[${new Date().toISOString()}] ERROR: ${error.message}\n`);
112
+ }
113
+ reject(error);
114
+ });
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Build prompt with optional CLAUDE.md injection
120
+ *
121
+ * @param {string} coderPackContent - Content of coder_pack.md
122
+ * @param {string} projectRoot - Project root directory
123
+ * @returns {Promise<string>} - Final prompt
124
+ */
125
+ export async function buildPromptWithContext(coderPackContent, projectRoot) {
126
+ let fullPrompt = coderPackContent;
127
+
128
+ // Check for CLAUDE.md in project root
129
+ const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
130
+ if (await pathExists(claudeMdPath)) {
131
+ const fs = await import('fs-extra');
132
+ const claudeMd = await fs.default.readFile(claudeMdPath, 'utf-8');
133
+
134
+ // Inject CLAUDE.md rules before coder pack
135
+ fullPrompt = `# PROJECT RULES (from CLAUDE.md)
136
+
137
+ ${claudeMd}
138
+
139
+ ---
140
+
141
+ # BUILD INSTRUCTIONS
142
+
143
+ ${coderPackContent}`;
144
+ }
145
+
146
+ return fullPrompt;
147
+ }
148
+
149
+ /**
150
+ * Get provider info for status display
151
+ */
152
+ export function getProviderInfo() {
153
+ return {
154
+ name: 'Claude Code',
155
+ command: CLAUDE_CODE_CONFIG.command,
156
+ mode: '--dangerously-skip-permissions',
157
+ description: 'AI coding with guardrails disabled (contract-approved)'
158
+ };
159
+ }
@@ -0,0 +1,45 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - AI Provider Manager
3
+ // ═══════════════════════════════════════════════════════════════════════════════
4
+
5
+ export {
6
+ spawnClaudeCode,
7
+ isClaudeCodeAvailable,
8
+ buildPromptWithContext,
9
+ getProviderInfo,
10
+ CLAUDE_CODE_CONFIG
11
+ } from './claude-code.js';
12
+
13
+ // Future providers:
14
+ // export { callAnthropicAPI } from './anthropic-api.js';
15
+ // export { spawnCursor } from './cursor.js';
16
+
17
+ /**
18
+ * Available providers
19
+ */
20
+ export const PROVIDERS = {
21
+ 'claude-code': {
22
+ name: 'Claude Code',
23
+ description: 'Official Claude CLI for coding',
24
+ available: true
25
+ },
26
+ 'anthropic-api': {
27
+ name: 'Anthropic API',
28
+ description: 'Direct API calls (coming soon)',
29
+ available: false
30
+ }
31
+ };
32
+
33
+ /**
34
+ * Get provider by name
35
+ */
36
+ export function getProvider(name) {
37
+ return PROVIDERS[name] || null;
38
+ }
39
+
40
+ /**
41
+ * Get default provider
42
+ */
43
+ export function getDefaultProvider() {
44
+ return 'claude-code';
45
+ }