@nclamvn/vibecode-cli 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Lรขm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # Vibecode CLI
2
+
3
+ > Build software with discipline - AI coding with guardrails
4
+
5
+ [![npm version](https://badge.fury.io/js/vibecode-cli.svg)](https://badge.fury.io/js/vibecode-cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## What is Vibecode?
9
+
10
+ Vibecode CLI transforms AI coding tools into disciplined builders. It provides:
11
+
12
+ - ๐ŸŽฏ **Guided workflow** - Step-by-step from idea to production
13
+ - ๐Ÿ”’ **Quality gates** - No shipping without checks
14
+ - ๐Ÿ“‹ **Contract-first** - Clear scope before building
15
+ - ๐Ÿ“Š **State tracking** - Always know where you are
16
+ - ๐Ÿ“ **Audit trail** - Every decision documented
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install -g vibecode-cli
22
+ ```
23
+
24
+ Requires Node.js 18 or higher.
25
+
26
+ ## Quick Start
27
+
28
+ ```bash
29
+ # 1. Initialize workspace
30
+ cd your-project
31
+ vibecode init
32
+
33
+ # 2. Start guided session
34
+ vibecode start
35
+
36
+ # 3. Check status anytime
37
+ vibecode status
38
+
39
+ # 4. Lock contract when ready
40
+ vibecode lock
41
+
42
+ # 5. Health check
43
+ vibecode doctor
44
+ ```
45
+
46
+ ## Commands
47
+
48
+ | Command | Description |
49
+ |---------|-------------|
50
+ | `vibecode init` | Initialize .vibecode/ workspace |
51
+ | `vibecode start` | Start guided session |
52
+ | `vibecode status` | Show current state and progress |
53
+ | `vibecode lock` | Validate and lock contract |
54
+ | `vibecode doctor` | Check installation health |
55
+
56
+ ## Workflow
57
+
58
+ ```
59
+ INIT โ†’ INTAKE โ†’ BLUEPRINT โ†’ CONTRACT โ†’ LOCK โ†’ BUILD โ†’ REVIEW โ†’ SHIP
60
+ ```
61
+
62
+ ## Files Created
63
+
64
+ ```
65
+ .vibecode/
66
+ โ”œโ”€โ”€ vibecode.yaml # Configuration
67
+ โ”œโ”€โ”€ state.json # Current state
68
+ โ”œโ”€โ”€ sessions/ # Session files
69
+ โ”‚ โ””โ”€โ”€ [timestamp]/
70
+ โ”‚ โ”œโ”€โ”€ intake.md
71
+ โ”‚ โ”œโ”€โ”€ blueprint.md
72
+ โ”‚ โ””โ”€โ”€ contract.md
73
+ โ””โ”€โ”€ logs/ # Audit trail
74
+ ```
75
+
76
+ ## Philosophy
77
+
78
+ Vibecode is built on the "AI as Pipeline" philosophy:
79
+
80
+ - **AI proposes** based on patterns and best practices
81
+ - **Human decides** based on context and goals
82
+ - **Contract locks** scope before building
83
+ - **Evidence proves** work was done correctly
84
+
85
+ ## License
86
+
87
+ MIT ยฉ Lรขm
88
+
89
+ ---
90
+
91
+ Built with โค๏ธ using Vibecode Kit v4.0
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
4
+ // VIBECODE CLI - Entry Point
5
+ // Spec Hash: 0fe43335f5a325e3279a079ce616c052
6
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
7
+
8
+ import { Command } from 'commander';
9
+ import {
10
+ initCommand,
11
+ startCommand,
12
+ statusCommand,
13
+ lockCommand,
14
+ doctorCommand,
15
+ VERSION
16
+ } from '../src/index.js';
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name('vibecode')
22
+ .description('Build software with discipline - AI coding with guardrails')
23
+ .version(VERSION);
24
+
25
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
26
+ // Commands
27
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
28
+
29
+ program
30
+ .command('init')
31
+ .description('Initialize Vibecode workspace in current directory')
32
+ .option('-f, --force', 'Overwrite existing workspace')
33
+ .option('-q, --quiet', 'Minimal output')
34
+ .action(initCommand);
35
+
36
+ program
37
+ .command('start')
38
+ .description('Start guided Vibecode session')
39
+ .option('-r, --resume', 'Resume existing session')
40
+ .action(startCommand);
41
+
42
+ program
43
+ .command('status')
44
+ .description('Display current state and progress')
45
+ .option('--json', 'Output as JSON')
46
+ .option('-v, --verbose', 'Show detailed info')
47
+ .action(statusCommand);
48
+
49
+ program
50
+ .command('lock')
51
+ .description('Lock contract and generate spec hash')
52
+ .option('-d, --dry-run', 'Validate without locking')
53
+ .option('-f, --force', 'Lock even with warnings')
54
+ .action(lockCommand);
55
+
56
+ program
57
+ .command('doctor')
58
+ .description('Check configuration and diagnose issues')
59
+ .action(doctorCommand);
60
+
61
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
62
+ // Parse
63
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
64
+
65
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@nclamvn/vibecode-cli",
3
+ "version": "1.0.0",
4
+ "description": "Build software with discipline - AI coding with guardrails",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "vibecode": "./bin/vibecode.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node bin/vibecode.js",
12
+ "lint": "eslint src/",
13
+ "test": "echo \"Tests coming in Phase B\" && exit 0"
14
+ },
15
+ "keywords": [
16
+ "cli",
17
+ "ai",
18
+ "coding",
19
+ "claude",
20
+ "vibecode",
21
+ "development",
22
+ "workflow"
23
+ ],
24
+ "author": "Lรขm",
25
+ "license": "MIT",
26
+ "engines": {
27
+ "node": ">=18.0.0"
28
+ },
29
+ "dependencies": {
30
+ "boxen": "^7.1.1",
31
+ "chalk": "^5.3.0",
32
+ "commander": "^12.0.0",
33
+ "fs-extra": "^11.2.0",
34
+ "inquirer": "^9.2.12",
35
+ "ora": "^8.0.1",
36
+ "yaml": "^2.3.4"
37
+ },
38
+ "devDependencies": {
39
+ "eslint": "^8.56.0"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/user/vibecode-cli"
44
+ }
45
+ }
@@ -0,0 +1,130 @@
1
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
2
+ // VIBECODE CLI - Doctor Command
3
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
4
+
5
+ import chalk from 'chalk';
6
+ import ora from 'ora';
7
+ import { VERSION, SPEC_HASH } from '../config/constants.js';
8
+ import {
9
+ workspaceExists,
10
+ loadConfig,
11
+ loadState
12
+ } from '../core/workspace.js';
13
+ import { getCurrentSessionId, getSessionFilesStatus } from '../core/session.js';
14
+ import { getSpecHash } from '../core/contract.js';
15
+ import { printBox } from '../ui/output.js';
16
+
17
+ export async function doctorCommand() {
18
+ console.log();
19
+ console.log(chalk.cyan('๐Ÿ” Vibecode Doctor'));
20
+ console.log(chalk.gray(' Checking installation and workspace health...\n'));
21
+
22
+ const checks = [];
23
+
24
+ // Check Node version
25
+ const nodeVersion = process.version;
26
+ const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0]);
27
+ checks.push({
28
+ name: 'Node.js version',
29
+ status: nodeMajor >= 18 ? 'ok' : 'error',
30
+ message: `${nodeVersion} ${nodeMajor >= 18 ? '(OK)' : '(Need 18+)'}`
31
+ });
32
+
33
+ // Check CLI version
34
+ checks.push({
35
+ name: 'Vibecode CLI',
36
+ status: 'ok',
37
+ message: `v${VERSION}`
38
+ });
39
+
40
+ // Check workspace
41
+ const hasWorkspace = await workspaceExists();
42
+ checks.push({
43
+ name: 'Workspace',
44
+ status: hasWorkspace ? 'ok' : 'warning',
45
+ message: hasWorkspace ? '.vibecode/ found' : 'Not initialized (run vibecode init)'
46
+ });
47
+
48
+ if (hasWorkspace) {
49
+ try {
50
+ // Check config
51
+ const config = await loadConfig();
52
+ checks.push({
53
+ name: 'Config',
54
+ status: 'ok',
55
+ message: 'vibecode.yaml valid'
56
+ });
57
+
58
+ // Check state
59
+ const state = await loadState();
60
+ checks.push({
61
+ name: 'State',
62
+ status: 'ok',
63
+ message: `state.json valid (${state.current_state})`
64
+ });
65
+
66
+ // Check session
67
+ const sessionId = await getCurrentSessionId();
68
+ checks.push({
69
+ name: 'Session',
70
+ status: sessionId ? 'ok' : 'info',
71
+ message: sessionId ? '1 active session' : 'No active session'
72
+ });
73
+
74
+ // Check contract
75
+ const specHash = await getSpecHash();
76
+ checks.push({
77
+ name: 'Contract',
78
+ status: specHash ? 'ok' : 'warning',
79
+ message: specHash ? `Locked (${specHash.substring(0, 8)}...)` : 'Not locked'
80
+ });
81
+
82
+ } catch (error) {
83
+ checks.push({
84
+ name: 'Workspace files',
85
+ status: 'error',
86
+ message: error.message
87
+ });
88
+ }
89
+ }
90
+
91
+ // Print results
92
+ let hasErrors = false;
93
+ let hasWarnings = false;
94
+
95
+ checks.forEach(check => {
96
+ let icon, color;
97
+ switch (check.status) {
98
+ case 'ok':
99
+ icon = 'โœ…';
100
+ color = chalk.green;
101
+ break;
102
+ case 'warning':
103
+ icon = 'โš ๏ธ ';
104
+ color = chalk.yellow;
105
+ hasWarnings = true;
106
+ break;
107
+ case 'error':
108
+ icon = 'โŒ';
109
+ color = chalk.red;
110
+ hasErrors = true;
111
+ break;
112
+ default:
113
+ icon = 'โ„น๏ธ ';
114
+ color = chalk.blue;
115
+ }
116
+
117
+ console.log(` ${icon} ${check.name}: ${color(check.message)}`);
118
+ });
119
+
120
+ // Summary
121
+ console.log();
122
+ if (hasErrors) {
123
+ console.log(chalk.red(' Some checks failed. Please fix the errors above.'));
124
+ } else if (hasWarnings) {
125
+ console.log(chalk.yellow(' All checks passed! (with warnings)'));
126
+ } else {
127
+ console.log(chalk.green(' All checks passed!'));
128
+ }
129
+ console.log();
130
+ }
@@ -0,0 +1,62 @@
1
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
2
+ // VIBECODE CLI - Init Command
3
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
4
+
5
+ import ora from 'ora';
6
+ import { workspaceExists, createWorkspace, getWorkspacePath } from '../core/workspace.js';
7
+ import { printBox, printSuccess, printError, printNextStep } from '../ui/output.js';
8
+ import { confirmAction } from '../ui/prompts.js';
9
+
10
+ export async function initCommand(options = {}) {
11
+ const spinner = ora('Checking workspace...').start();
12
+
13
+ try {
14
+ // Check if already exists
15
+ if (await workspaceExists()) {
16
+ spinner.stop();
17
+
18
+ if (!options.force) {
19
+ printError('Workspace already exists!');
20
+ console.log();
21
+ console.log(' Use --force to reinitialize (will backup existing)');
22
+ console.log(' Or run `vibecode start` to continue');
23
+ process.exit(1);
24
+ }
25
+
26
+ const confirm = await confirmAction('This will overwrite existing workspace. Continue?');
27
+ if (!confirm) {
28
+ console.log('Cancelled.');
29
+ process.exit(0);
30
+ }
31
+
32
+ spinner.start('Reinitializing workspace...');
33
+ }
34
+
35
+ // Create workspace
36
+ spinner.text = 'Creating workspace...';
37
+ await createWorkspace();
38
+
39
+ spinner.succeed('Workspace initialized!');
40
+
41
+ // Print success box
42
+ const content = `โœ… Vibecode workspace initialized!
43
+
44
+ Created:
45
+ .vibecode/
46
+ โ”œโ”€โ”€ vibecode.yaml
47
+ โ”œโ”€โ”€ state.json
48
+ โ”œโ”€โ”€ sessions/
49
+ โ”œโ”€โ”€ library/
50
+ โ””โ”€โ”€ logs/
51
+
52
+ Spec Hash Reference: 0fe43335f5a325e3279a079ce616c052`;
53
+
54
+ printBox(content, { borderColor: 'green' });
55
+ printNextStep('Run `vibecode start` to begin your project');
56
+
57
+ } catch (error) {
58
+ spinner.fail('Failed to initialize workspace');
59
+ printError(error.message);
60
+ process.exit(1);
61
+ }
62
+ }
@@ -0,0 +1,105 @@
1
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
2
+ // VIBECODE CLI - Lock Command
3
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
4
+
5
+ import chalk from 'chalk';
6
+ import ora from 'ora';
7
+ import { workspaceExists, loadState } from '../core/workspace.js';
8
+ import { getCurrentSessionId, sessionFileExists } from '../core/session.js';
9
+ import { lockContract, validateContract } from '../core/contract.js';
10
+ import { readSessionFile } from '../core/session.js';
11
+ import { transitionTo, getCurrentState } from '../core/state-machine.js';
12
+ import { STATES } from '../config/constants.js';
13
+ import { printBox, printError, printSuccess, printWarning, printNextStep } from '../ui/output.js';
14
+
15
+ export async function lockCommand(options = {}) {
16
+ const spinner = ora('Validating contract...').start();
17
+
18
+ try {
19
+ // Check workspace
20
+ if (!await workspaceExists()) {
21
+ spinner.fail();
22
+ printError('No Vibecode workspace found. Run `vibecode init` first.');
23
+ process.exit(1);
24
+ }
25
+
26
+ // Check state
27
+ const currentState = await getCurrentState();
28
+ if (currentState !== STATES.CONTRACT_DRAFTED) {
29
+ spinner.fail();
30
+ printError(`Cannot lock contract in state: ${currentState}`);
31
+ console.log('Contract must be in CONTRACT_DRAFTED state.');
32
+ console.log(`Current state: ${currentState}`);
33
+ process.exit(1);
34
+ }
35
+
36
+ // Check contract file exists
37
+ if (!await sessionFileExists('contract.md')) {
38
+ spinner.fail();
39
+ printError('Contract file not found.');
40
+ console.log('Create a contract first using `vibecode start`.');
41
+ process.exit(1);
42
+ }
43
+
44
+ // Dry run mode
45
+ if (options.dryRun) {
46
+ const content = await readSessionFile('contract.md');
47
+ const validation = validateContract(content);
48
+
49
+ spinner.stop();
50
+
51
+ if (validation.valid) {
52
+ printSuccess('Contract validation passed!');
53
+ if (validation.warnings.length > 0) {
54
+ validation.warnings.forEach(w => printWarning(w));
55
+ }
56
+ console.log('\nRun without --dry-run to lock.');
57
+ } else {
58
+ printError('Contract validation failed:');
59
+ validation.errors.forEach(e => console.log(chalk.red(` โ€ข ${e}`)));
60
+ }
61
+ return;
62
+ }
63
+
64
+ // Lock contract
65
+ spinner.text = 'Locking contract...';
66
+ const result = await lockContract();
67
+
68
+ if (!result.success) {
69
+ spinner.fail('Contract validation failed');
70
+ console.log();
71
+ result.errors.forEach(e => console.log(chalk.red(` โŒ ${e}`)));
72
+ console.log();
73
+ console.log('Please edit contract.md and try again.');
74
+ process.exit(1);
75
+ }
76
+
77
+ // Transition state
78
+ await transitionTo(STATES.CONTRACT_LOCKED, 'contract_locked');
79
+
80
+ spinner.succeed('Contract locked!');
81
+
82
+ // Success output
83
+ const content = `๐Ÿ”’ CONTRACT LOCKED
84
+
85
+ Spec Hash: ${result.specHash}
86
+ Locked at: ${result.timestamp}
87
+
88
+ Contract is now immutable.
89
+ All builds must reference this spec_hash.`;
90
+
91
+ printBox(content, { borderColor: 'green' });
92
+
93
+ if (result.warnings.length > 0) {
94
+ console.log();
95
+ result.warnings.forEach(w => printWarning(w));
96
+ }
97
+
98
+ printNextStep('Ready for build! Transfer to Claude Code with contract.');
99
+
100
+ } catch (error) {
101
+ spinner.fail('Failed to lock contract');
102
+ printError(error.message);
103
+ process.exit(1);
104
+ }
105
+ }