@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 +21 -0
- package/README.md +91 -0
- package/bin/vibecode.js +65 -0
- package/package.json +45 -0
- package/src/commands/doctor.js +130 -0
- package/src/commands/init.js +62 -0
- package/src/commands/lock.js +105 -0
- package/src/commands/start.js +222 -0
- package/src/commands/status.js +80 -0
- package/src/config/constants.js +89 -0
- package/src/config/templates.js +194 -0
- package/src/core/contract.js +103 -0
- package/src/core/session.js +115 -0
- package/src/core/state-machine.js +95 -0
- package/src/core/workspace.js +152 -0
- package/src/index.js +12 -0
- package/src/ui/branding.js +36 -0
- package/src/ui/output.js +96 -0
- package/src/ui/prompts.js +120 -0
- package/src/utils/files.js +84 -0
- package/src/utils/hash.js +34 -0
- package/templates/blueprint.md +61 -0
- package/templates/contract.md +61 -0
- package/templates/intake.md +37 -0
- package/templates/vibecode.yaml +15 -0
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
|
+
[](https://badge.fury.io/js/vibecode-cli)
|
|
6
|
+
[](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
|
package/bin/vibecode.js
ADDED
|
@@ -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
|
+
}
|