vibecodingmachine-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/.allnightai/REQUIREMENTS.md +11 -0
- package/.allnightai/temp/auto-status.json +6 -0
- package/.env +7 -0
- package/.eslintrc.js +16 -0
- package/README.md +85 -0
- package/bin/vibecodingmachine.js +274 -0
- package/jest.config.js +8 -0
- package/logs/audit/2025-11-07.jsonl +2 -0
- package/package.json +64 -0
- package/scripts/README.md +128 -0
- package/scripts/auto-start-wrapper.sh +92 -0
- package/scripts/postinstall.js +81 -0
- package/src/commands/auth.js +96 -0
- package/src/commands/auto-direct.js +1748 -0
- package/src/commands/auto.js +4692 -0
- package/src/commands/auto.js.bak +710 -0
- package/src/commands/ide.js +70 -0
- package/src/commands/repo.js +159 -0
- package/src/commands/requirements.js +161 -0
- package/src/commands/setup.js +91 -0
- package/src/commands/status.js +88 -0
- package/src/components/RequirementPage.js +0 -0
- package/src/file.js +0 -0
- package/src/index.js +5 -0
- package/src/main.js +0 -0
- package/src/ui/requirements-page.js +0 -0
- package/src/utils/auth.js +548 -0
- package/src/utils/auto-mode-ansi-ui.js +238 -0
- package/src/utils/auto-mode-simple-ui.js +161 -0
- package/src/utils/auto-mode-ui.js.bak.blessed +207 -0
- package/src/utils/auto-mode.js +65 -0
- package/src/utils/config.js +64 -0
- package/src/utils/interactive.js +3616 -0
- package/src/utils/keyboard-handler.js +152 -0
- package/src/utils/logger.js +4 -0
- package/src/utils/persistent-header.js +116 -0
- package/src/utils/provider-registry.js +128 -0
- package/src/utils/requirementUtils.js +0 -0
- package/src/utils/status-card.js +120 -0
- package/src/utils/status-manager.js +0 -0
- package/src/utils/status.js +0 -0
- package/src/utils/stdout-interceptor.js +127 -0
- package/tests/auto-mode.test.js +37 -0
- package/tests/config.test.js +34 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const { AppleScriptManager } = require('@vibecodingmachine/core');
|
|
5
|
+
const { getRepoPath } = require('../utils/config');
|
|
6
|
+
|
|
7
|
+
const IDES = ['cursor', 'vscode', 'windsurf', 'antigravity', 'cline'];
|
|
8
|
+
|
|
9
|
+
async function list() {
|
|
10
|
+
console.log(chalk.bold('\nAvailable IDEs'));
|
|
11
|
+
for (const ide of IDES) console.log('-', chalk.cyan(ide));
|
|
12
|
+
console.log();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function open(ide) {
|
|
16
|
+
const repoPath = await getRepoPath();
|
|
17
|
+
if (!repoPath) {
|
|
18
|
+
console.log(chalk.yellow('No repository path configured'));
|
|
19
|
+
console.log(chalk.gray('Use'), chalk.cyan('allnightai repo:init'), chalk.gray('or'), chalk.cyan('allnightai repo:set <path>'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const ideKey = String(ide || '').toLowerCase();
|
|
23
|
+
if (!IDES.includes(ideKey)) {
|
|
24
|
+
console.log(chalk.red('Unknown IDE:'), ide);
|
|
25
|
+
await list();
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const spinner = ora(`Opening ${ideKey}...`).start();
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const appleScriptManager = new AppleScriptManager();
|
|
33
|
+
|
|
34
|
+
// Use AppleScriptManager to open IDE with repo path
|
|
35
|
+
await appleScriptManager.openIDE(ideKey, repoPath);
|
|
36
|
+
|
|
37
|
+
spinner.succeed(chalk.green(`Opened ${ideKey} at ${path.basename(repoPath)}`));
|
|
38
|
+
} catch (error) {
|
|
39
|
+
spinner.fail(chalk.red('Failed to open IDE'));
|
|
40
|
+
console.error(chalk.red('Error:'), error.message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function send(message, options) {
|
|
46
|
+
const ide = (options && options.ide) || 'cline';
|
|
47
|
+
const spinner = ora(`Sending message to ${ide}...`).start();
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const appleScriptManager = new AppleScriptManager();
|
|
51
|
+
|
|
52
|
+
// Use AppleScriptManager to send text to IDE AI chat
|
|
53
|
+
await appleScriptManager.sendTextToIDE(ide, message);
|
|
54
|
+
|
|
55
|
+
spinner.succeed(chalk.green(`Sent message to ${ide} AI chat`));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
spinner.fail(chalk.red('Failed to send message'));
|
|
58
|
+
console.error(chalk.red('Error:'), error.message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = {
|
|
64
|
+
list,
|
|
65
|
+
open,
|
|
66
|
+
send
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const { getRepoPath, setRepoPath } = require('../utils/config');
|
|
6
|
+
const {
|
|
7
|
+
checkVibeCodingMachineExists,
|
|
8
|
+
getHostname,
|
|
9
|
+
getRequirementsFilename,
|
|
10
|
+
isComputerNameEnabled
|
|
11
|
+
} = require('@vibecodingmachine/core');
|
|
12
|
+
|
|
13
|
+
async function setRepo(repoPath) {
|
|
14
|
+
try {
|
|
15
|
+
const absolutePath = path.resolve(repoPath);
|
|
16
|
+
|
|
17
|
+
if (!await fs.pathExists(absolutePath)) {
|
|
18
|
+
console.error(chalk.red(`Error: Directory does not exist: ${absolutePath}`));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await setRepoPath(absolutePath);
|
|
23
|
+
console.log(chalk.green('✓'), `Repository path set to: ${chalk.cyan(absolutePath)}`);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error(chalk.red('Error setting repository path:'), error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function getRepo() {
|
|
31
|
+
try {
|
|
32
|
+
const repoPath = await getRepoPath();
|
|
33
|
+
|
|
34
|
+
if (!repoPath) {
|
|
35
|
+
console.log(chalk.yellow('No repository path configured'));
|
|
36
|
+
console.log(chalk.gray('Use'), chalk.cyan('allnightai repo:set <path>'), chalk.gray('to set repository path'));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(chalk.cyan(repoPath));
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error(chalk.red('Error getting repository path:'), error.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function initRepo() {
|
|
48
|
+
try {
|
|
49
|
+
const cwd = process.cwd();
|
|
50
|
+
const repoName = path.basename(cwd);
|
|
51
|
+
const insideDir = path.join(cwd, '.vibecodingmachine');
|
|
52
|
+
const siblingDir = path.join(path.dirname(cwd), `.vibecodingmachine-${repoName}`);
|
|
53
|
+
|
|
54
|
+
// Ask user where to create the directory
|
|
55
|
+
const { location } = await inquirer.prompt([
|
|
56
|
+
{
|
|
57
|
+
type: 'list',
|
|
58
|
+
name: 'location',
|
|
59
|
+
message: 'Where would you like to create the VibeCodingMachine directory?',
|
|
60
|
+
choices: [
|
|
61
|
+
{
|
|
62
|
+
name: `Inside this repository (${chalk.cyan('.vibecodingmachine')}) - suggested`,
|
|
63
|
+
value: 'inside',
|
|
64
|
+
short: 'Inside repository'
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: `As a sibling directory (${chalk.cyan(`../.vibecodingmachine-${repoName}`)}) - keeps configs separate from code`,
|
|
68
|
+
value: 'sibling',
|
|
69
|
+
short: 'Sibling directory'
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
default: 'inside'
|
|
73
|
+
}
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const allnightDir = location === 'inside' ? insideDir : siblingDir;
|
|
77
|
+
|
|
78
|
+
console.log(chalk.blue('\nInitializing VibeCodingMachine in:'), chalk.cyan(allnightDir));
|
|
79
|
+
|
|
80
|
+
await fs.ensureDir(allnightDir);
|
|
81
|
+
await fs.ensureDir(path.join(allnightDir, 'temp'));
|
|
82
|
+
await fs.ensureDir(path.join(allnightDir, 'temp', 'screenshots'));
|
|
83
|
+
|
|
84
|
+
// Check if computer name is enabled and use appropriate filename
|
|
85
|
+
const useHostname = await isComputerNameEnabled();
|
|
86
|
+
const hostname = getHostname();
|
|
87
|
+
const requirementsFilename = await getRequirementsFilename();
|
|
88
|
+
const requirementsPath = path.join(allnightDir, requirementsFilename);
|
|
89
|
+
|
|
90
|
+
if (!await fs.pathExists(requirementsPath)) {
|
|
91
|
+
const titleSuffix = useHostname ? ` (${hostname})` : '';
|
|
92
|
+
const requirementsTemplate = `# VibeCodingMachine Requirements${titleSuffix}
|
|
93
|
+
|
|
94
|
+
## 🚦 Current Status
|
|
95
|
+
PREPARE
|
|
96
|
+
|
|
97
|
+
## ⏳ Requirements not yet completed
|
|
98
|
+
|
|
99
|
+
### Example Requirement
|
|
100
|
+
Add your requirements here...
|
|
101
|
+
|
|
102
|
+
## ✅ Verified by AI
|
|
103
|
+
|
|
104
|
+
## ⚠️ Issues Found
|
|
105
|
+
`;
|
|
106
|
+
await fs.writeFile(requirementsPath, requirementsTemplate);
|
|
107
|
+
console.log(chalk.green('✓'), `Created ${requirementsFilename}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const instructionsPath = path.join(allnightDir, 'INSTRUCTIONS.md');
|
|
111
|
+
if (!await fs.pathExists(instructionsPath)) {
|
|
112
|
+
const fileNote = useHostname
|
|
113
|
+
? `**ALWAYS** use the hostname-specific requirements file:
|
|
114
|
+
- File: \`${requirementsFilename}\`
|
|
115
|
+
- This ensures each computer has its own requirements and progress tracking`
|
|
116
|
+
: `**Requirements file:** \`REQUIREMENTS.md\``;
|
|
117
|
+
|
|
118
|
+
const instructionsTemplate = `# Development Instructions
|
|
119
|
+
|
|
120
|
+
## Important: Requirements File
|
|
121
|
+
|
|
122
|
+
${fileNote}
|
|
123
|
+
|
|
124
|
+
## Guidelines
|
|
125
|
+
- Follow the status progression: PREPARE → ACT → CLEAN UP → VERIFY → DONE
|
|
126
|
+
- Update status in ${requirementsFilename} at each stage
|
|
127
|
+
- Take screenshots during VERIFY phase
|
|
128
|
+
- Mark requirement as DONE when complete
|
|
129
|
+
|
|
130
|
+
## Code Standards
|
|
131
|
+
- Keep files under 250 lines
|
|
132
|
+
- Follow DRY principles
|
|
133
|
+
- Keep it simple (KISS)
|
|
134
|
+
- No placeholder code
|
|
135
|
+
- Prefix temporary files with TEMP_
|
|
136
|
+
`;
|
|
137
|
+
await fs.writeFile(instructionsPath, instructionsTemplate);
|
|
138
|
+
console.log(chalk.green('✓'), 'Created INSTRUCTIONS.md');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await setRepoPath(cwd);
|
|
142
|
+
console.log(chalk.green('✓'), 'Set repository path to current directory');
|
|
143
|
+
|
|
144
|
+
console.log(chalk.green('\n✓ VibeCodingMachine initialized successfully!'));
|
|
145
|
+
console.log(chalk.gray('\nNext steps:'));
|
|
146
|
+
const configPath = location === 'inside' ? `.vibecodingmachine/${requirementsFilename}` : `../.vibecodingmachine-${repoName}/${requirementsFilename}`;
|
|
147
|
+
console.log(chalk.gray(' 1. Edit'), chalk.cyan(configPath), chalk.gray('with your requirements'));
|
|
148
|
+
console.log(chalk.gray(' 2. Run'), chalk.cyan('allnightai auto:start'), chalk.gray('to begin autonomous development'));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(chalk.red('Error initializing repository:'), error.message);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = {
|
|
156
|
+
setRepo,
|
|
157
|
+
getRepo,
|
|
158
|
+
initRepo
|
|
159
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const chokidar = require('chokidar');
|
|
5
|
+
const { getRepoPath } = require('../utils/config');
|
|
6
|
+
const { getRequirementsPath } = require('@vibecodingmachine/core');
|
|
7
|
+
|
|
8
|
+
async function getReqPathOrExit() {
|
|
9
|
+
const repoPath = await getRepoPath();
|
|
10
|
+
if (!repoPath) {
|
|
11
|
+
console.log(chalk.yellow('No repository path configured'));
|
|
12
|
+
console.log(chalk.gray('Use'), chalk.cyan('vcm repo:init'), chalk.gray('or'), chalk.cyan('vcm repo:set <path>'));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
// Use getRequirementsPath which handles hostname-specific files
|
|
16
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
17
|
+
return { repoPath, reqPath };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function list(options) {
|
|
21
|
+
try {
|
|
22
|
+
const { reqPath } = await getReqPathOrExit();
|
|
23
|
+
if (!await fs.pathExists(reqPath)) {
|
|
24
|
+
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
28
|
+
const lines = content.split('\n');
|
|
29
|
+
const filter = options && options.status ? String(options.status).toLowerCase() : null;
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
if (line.startsWith('### ') || line.startsWith('## ')) {
|
|
32
|
+
if (!filter || line.toLowerCase().includes(filter)) {
|
|
33
|
+
console.log(line);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(chalk.red('Error listing requirements:'), error.message);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function add(name) {
|
|
44
|
+
try {
|
|
45
|
+
const { reqPath } = await getReqPathOrExit();
|
|
46
|
+
await fs.ensureFile(reqPath);
|
|
47
|
+
let content = await fs.readFile(reqPath, 'utf8').catch(() => '');
|
|
48
|
+
|
|
49
|
+
// Find the TODO section
|
|
50
|
+
const todoSectionHeader = '## ⏳ Requirements not yet completed';
|
|
51
|
+
if (!content.includes(todoSectionHeader)) {
|
|
52
|
+
content += '\n\n' + todoSectionHeader + '\n';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Insert at the top of the TODO section (right after the header)
|
|
56
|
+
const lines = content.split('\n');
|
|
57
|
+
const newLines = [];
|
|
58
|
+
let inserted = false;
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < lines.length; i++) {
|
|
61
|
+
newLines.push(lines[i]);
|
|
62
|
+
|
|
63
|
+
// Insert right after the TODO section header (new header format)
|
|
64
|
+
if (!inserted && lines[i].includes(todoSectionHeader)) {
|
|
65
|
+
// Add requirement header
|
|
66
|
+
newLines.push(`### ${name}`);
|
|
67
|
+
// Add blank line after requirement
|
|
68
|
+
newLines.push('');
|
|
69
|
+
inserted = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
content = newLines.join('\n');
|
|
74
|
+
await fs.writeFile(reqPath, content);
|
|
75
|
+
console.log(chalk.green('✓ Requirement added'));
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(chalk.red('Error adding requirement:'), error.message);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function current() {
|
|
83
|
+
try {
|
|
84
|
+
const { reqPath } = await getReqPathOrExit();
|
|
85
|
+
if (!await fs.pathExists(reqPath)) {
|
|
86
|
+
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
90
|
+
const match = content.match(/## \uD83D\uDEA6 Current Status\n([A-Z_]+)/);
|
|
91
|
+
console.log(chalk.cyan(match ? match[1] : 'UNKNOWN'));
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(chalk.red('Error reading current status:'), error.message);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function next() {
|
|
99
|
+
try {
|
|
100
|
+
const { reqPath } = await getReqPathOrExit();
|
|
101
|
+
if (!await fs.pathExists(reqPath)) {
|
|
102
|
+
console.log(chalk.yellow('No REQUIREMENTS.md found.'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
106
|
+
const order = ['PREPARE', 'ACT', 'CLEAN UP', 'VERIFY', 'DONE'];
|
|
107
|
+
const currentMatch = content.match(/## \uD83D\uDEA6 Current Status\n([A-Z_ ]+)/);
|
|
108
|
+
const current = currentMatch ? currentMatch[1] : 'PREPARE';
|
|
109
|
+
const idx = Math.min(order.indexOf(current) + 1, order.length - 1);
|
|
110
|
+
const nextStatus = order[idx];
|
|
111
|
+
const updated = content.replace(/(## \uD83D\uDEA6 Current Status\n)([A-Z_ ]+)/, `$1${nextStatus}`);
|
|
112
|
+
await fs.writeFile(reqPath, updated);
|
|
113
|
+
console.log(chalk.green('✓ Moved to next status:'), chalk.cyan(nextStatus));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error(chalk.red('Error updating status:'), error.message);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function edit() {
|
|
121
|
+
try {
|
|
122
|
+
const { reqPath } = await getReqPathOrExit();
|
|
123
|
+
const opener = process.env.EDITOR ? null : 'open';
|
|
124
|
+
if (process.env.EDITOR) {
|
|
125
|
+
const { spawn } = require('child_process');
|
|
126
|
+
const child = spawn(process.env.EDITOR, [reqPath], { stdio: 'inherit' });
|
|
127
|
+
child.on('exit', (code) => process.exit(code || 0));
|
|
128
|
+
} else {
|
|
129
|
+
const { execSync } = require('child_process');
|
|
130
|
+
execSync(`${opener} ${JSON.stringify(reqPath)}`);
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(chalk.red('Error opening file:'), error.message);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function watch() {
|
|
139
|
+
try {
|
|
140
|
+
const { reqPath } = await getReqPathOrExit();
|
|
141
|
+
console.log(chalk.gray('Watching:'), chalk.cyan(reqPath));
|
|
142
|
+
chokidar.watch(reqPath, { ignoreInitial: true })
|
|
143
|
+
.on('change', () => console.log(chalk.green('✓ REQUIREMENTS.md changed')))
|
|
144
|
+
.on('error', (err) => console.error(chalk.red('Watcher error:'), err.message));
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error(chalk.red('Error watching requirements:'), error.message);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
list,
|
|
153
|
+
add,
|
|
154
|
+
current,
|
|
155
|
+
next,
|
|
156
|
+
edit,
|
|
157
|
+
watch
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const inquirer = require('inquirer');
|
|
6
|
+
|
|
7
|
+
async function setupAlias() {
|
|
8
|
+
console.log(chalk.bold.cyan('\n🛠️ Setup Vibe Coding Machine Alias\n'));
|
|
9
|
+
|
|
10
|
+
// Detect shell
|
|
11
|
+
const shell = process.env.SHELL;
|
|
12
|
+
let configFile = '';
|
|
13
|
+
let shellName = '';
|
|
14
|
+
|
|
15
|
+
if (shell.includes('zsh')) {
|
|
16
|
+
shellName = 'zsh';
|
|
17
|
+
configFile = path.join(os.homedir(), '.zshrc');
|
|
18
|
+
} else if (shell.includes('bash')) {
|
|
19
|
+
shellName = 'bash';
|
|
20
|
+
configFile = path.join(os.homedir(), '.bashrc');
|
|
21
|
+
// On Mac, .bash_profile is often used instead of .bashrc for login shells
|
|
22
|
+
if (process.platform === 'darwin' && !fs.existsSync(configFile)) {
|
|
23
|
+
const profile = path.join(os.homedir(), '.bash_profile');
|
|
24
|
+
if (fs.existsSync(profile)) {
|
|
25
|
+
configFile = profile;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} else if (shell.includes('fish')) {
|
|
29
|
+
shellName = 'fish';
|
|
30
|
+
configFile = path.join(os.homedir(), '.config', 'fish', 'config.fish');
|
|
31
|
+
} else {
|
|
32
|
+
console.log(chalk.yellow(`⚠️ Could not detect supported shell (zsh, bash, fish). Current shell: ${shell}`));
|
|
33
|
+
console.log(chalk.gray('You can manually add the alias:\n'));
|
|
34
|
+
console.log(chalk.cyan(' alias vcm="vibecodingmachine"'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(chalk.gray(`Detected shell: ${chalk.cyan(shellName)}`));
|
|
39
|
+
console.log(chalk.gray(`Config file: ${chalk.cyan(configFile)}`));
|
|
40
|
+
|
|
41
|
+
// Check if config file exists
|
|
42
|
+
if (!fs.existsSync(configFile)) {
|
|
43
|
+
// For fish, ensure directory exists
|
|
44
|
+
if (shellName === 'fish') {
|
|
45
|
+
const configDir = path.dirname(configFile);
|
|
46
|
+
if (!fs.existsSync(configDir)) {
|
|
47
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Create empty file if it doesn't exist
|
|
51
|
+
fs.writeFileSync(configFile, '', 'utf8');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Read config file
|
|
55
|
+
let content = fs.readFileSync(configFile, 'utf8');
|
|
56
|
+
|
|
57
|
+
// Check if alias already exists
|
|
58
|
+
if (content.includes('alias vcm="vibecodingmachine"') || content.includes("alias vcm='vibecodingmachine'")) {
|
|
59
|
+
console.log(chalk.green('\n✓ "vcm" alias is already configured!'));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Confirm with user
|
|
64
|
+
const { confirm } = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'confirm',
|
|
67
|
+
name: 'confirm',
|
|
68
|
+
message: `Add 'vcm' alias to ${path.basename(configFile)}?`,
|
|
69
|
+
default: true
|
|
70
|
+
}
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
if (!confirm) {
|
|
74
|
+
console.log(chalk.yellow('\nCancelled.'));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Add alias
|
|
79
|
+
const aliasCommand = '\n# Vibe Coding Machine Alias\nalias vcm="vibecodingmachine"\n';
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
fs.appendFileSync(configFile, aliasCommand, 'utf8');
|
|
83
|
+
console.log(chalk.green(`\n✓ Added alias to ${path.basename(configFile)}`));
|
|
84
|
+
console.log(chalk.gray('\nYou may need to restart your terminal or run:'));
|
|
85
|
+
console.log(chalk.cyan(` source ${configFile}`));
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.log(chalk.red(`\n✗ Error writing to config file: ${error.message}`));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = { setupAlias };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const { getRepoPath } = require('../utils/config');
|
|
6
|
+
const { checkAutoModeStatus } = require('../utils/auto-mode');
|
|
7
|
+
const { getRequirementsPath } = require('@vibecodingmachine/core');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format IDE name for display
|
|
11
|
+
* @param {string} ide - Internal IDE identifier
|
|
12
|
+
* @returns {string} Display name for IDE
|
|
13
|
+
*/
|
|
14
|
+
function formatIDEName(ide) {
|
|
15
|
+
const ideNames = {
|
|
16
|
+
'cline': 'Cline IDE',
|
|
17
|
+
'cursor': 'Cursor',
|
|
18
|
+
'vscode': 'VS Code',
|
|
19
|
+
'windsurf': 'Windsurf'
|
|
20
|
+
};
|
|
21
|
+
return ideNames[ide] || ide;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function formatPath(fullPath) {
|
|
25
|
+
if (!fullPath) return 'not set';
|
|
26
|
+
const homeDir = os.homedir();
|
|
27
|
+
if (fullPath.startsWith(homeDir)) {
|
|
28
|
+
return fullPath.replace(homeDir, '~');
|
|
29
|
+
}
|
|
30
|
+
return fullPath;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function show() {
|
|
34
|
+
const repoPath = await getRepoPath();
|
|
35
|
+
console.log(chalk.bold('\nVibe Coding Machine Status'));
|
|
36
|
+
console.log(chalk.gray('Repository:'), chalk.cyan(formatPath(repoPath)));
|
|
37
|
+
const status = await checkAutoModeStatus();
|
|
38
|
+
console.log(chalk.gray('Auto mode:'), status.running ? chalk.green('running') : chalk.yellow('stopped'));
|
|
39
|
+
if (status.running) {
|
|
40
|
+
console.log(chalk.gray('IDE:'), chalk.cyan(formatIDEName(status.ide || 'cline')));
|
|
41
|
+
console.log(chalk.gray('Chats:'), chalk.cyan(status.chatCount || 0));
|
|
42
|
+
}
|
|
43
|
+
console.log();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function progress() {
|
|
47
|
+
const repoPath = await getRepoPath();
|
|
48
|
+
if (!repoPath) {
|
|
49
|
+
console.log(chalk.yellow('No repository path configured'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Use getRequirementsPath to get the hostname-specific file
|
|
53
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
54
|
+
if (!reqPath || !await fs.pathExists(reqPath)) {
|
|
55
|
+
console.log(chalk.yellow('No REQUIREMENTS file found.'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
59
|
+
const current = content.match(/## \uD83D\uDEA6 Current Status\n([A-Z_ ]+)/);
|
|
60
|
+
console.log(chalk.bold('\nProgress'));
|
|
61
|
+
console.log(chalk.gray('Current Status:'), chalk.cyan(current ? current[1] : 'UNKNOWN'));
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function logs(cmd) {
|
|
66
|
+
const lines = parseInt((cmd && cmd.lines) || '50', 10);
|
|
67
|
+
const repoPath = await getRepoPath();
|
|
68
|
+
const defaultLog = path.join(process.cwd(), 'logs', 'security', 'security-2025-10-29.log');
|
|
69
|
+
const logPath = await fs.pathExists(defaultLog) ? defaultLog : null;
|
|
70
|
+
console.log(chalk.bold('\nRecent Logs'));
|
|
71
|
+
if (!logPath) {
|
|
72
|
+
console.log(chalk.gray('No logs available.'));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const content = await fs.readFile(logPath, 'utf8');
|
|
76
|
+
const out = content.trim().split('\n').slice(-lines).join('\n');
|
|
77
|
+
console.log(out);
|
|
78
|
+
console.log();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
show,
|
|
83
|
+
progress,
|
|
84
|
+
logs
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
File without changes
|
package/src/file.js
ADDED
|
File without changes
|
package/src/index.js
ADDED
package/src/main.js
ADDED
|
File without changes
|
|
File without changes
|