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.
Files changed (44) hide show
  1. package/.allnightai/REQUIREMENTS.md +11 -0
  2. package/.allnightai/temp/auto-status.json +6 -0
  3. package/.env +7 -0
  4. package/.eslintrc.js +16 -0
  5. package/README.md +85 -0
  6. package/bin/vibecodingmachine.js +274 -0
  7. package/jest.config.js +8 -0
  8. package/logs/audit/2025-11-07.jsonl +2 -0
  9. package/package.json +64 -0
  10. package/scripts/README.md +128 -0
  11. package/scripts/auto-start-wrapper.sh +92 -0
  12. package/scripts/postinstall.js +81 -0
  13. package/src/commands/auth.js +96 -0
  14. package/src/commands/auto-direct.js +1748 -0
  15. package/src/commands/auto.js +4692 -0
  16. package/src/commands/auto.js.bak +710 -0
  17. package/src/commands/ide.js +70 -0
  18. package/src/commands/repo.js +159 -0
  19. package/src/commands/requirements.js +161 -0
  20. package/src/commands/setup.js +91 -0
  21. package/src/commands/status.js +88 -0
  22. package/src/components/RequirementPage.js +0 -0
  23. package/src/file.js +0 -0
  24. package/src/index.js +5 -0
  25. package/src/main.js +0 -0
  26. package/src/ui/requirements-page.js +0 -0
  27. package/src/utils/auth.js +548 -0
  28. package/src/utils/auto-mode-ansi-ui.js +238 -0
  29. package/src/utils/auto-mode-simple-ui.js +161 -0
  30. package/src/utils/auto-mode-ui.js.bak.blessed +207 -0
  31. package/src/utils/auto-mode.js +65 -0
  32. package/src/utils/config.js +64 -0
  33. package/src/utils/interactive.js +3616 -0
  34. package/src/utils/keyboard-handler.js +152 -0
  35. package/src/utils/logger.js +4 -0
  36. package/src/utils/persistent-header.js +116 -0
  37. package/src/utils/provider-registry.js +128 -0
  38. package/src/utils/requirementUtils.js +0 -0
  39. package/src/utils/status-card.js +120 -0
  40. package/src/utils/status-manager.js +0 -0
  41. package/src/utils/status.js +0 -0
  42. package/src/utils/stdout-interceptor.js +127 -0
  43. package/tests/auto-mode.test.js +37 -0
  44. 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
@@ -0,0 +1,5 @@
1
+ // Entry point placeholder to satisfy package.json main
2
+ module.exports = {};
3
+
4
+
5
+
package/src/main.js ADDED
File without changes
File without changes