shoud-cli 1.0.5 → 1.0.7

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools/index.js +121 -96
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shoud-cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "SHOUD Terminal Agent: Give your computer a job.",
5
5
  "main": "bin/shoud.js",
6
6
  "bin": {
@@ -1,115 +1,140 @@
1
- const { execSync } = require('child_process');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const os = require('os');
1
+ #!/usr/bin/env node
5
2
 
6
- const MAX_OUTPUT_SIZE = 1024 * 1024;
7
- const PROJECT_ROOT = process.cwd();
3
+ const { program } = require('commander');
4
+ const chalk = require('chalk');
5
+ const { login } = require('../src/auth/deviceFlow');
6
+ const { executeTask } = require('../src/runtime/agentLoop');
8
7
 
9
- function getShell() {
10
- if (os.platform() === 'win32') {
11
- return 'powershell.exe';
12
- }
13
- return '/bin/bash';
14
- }
8
+ // ─── Original SHOUD Banner Art ──────────────────────────────
9
+ const BANNER_ART = [
10
+ " ███████╗██╗ ██╗ ██████╗ ██╗ ██╗██████╗ ",
11
+ " ██╔════╝██║ ██║██╔═══██╗██║ ██║██╔══██╗",
12
+ " ███████╗███████║██║ ██║██║ ██║██║ ██║",
13
+ " ╚════██║██╔══██║██║ ██║██║ ██║██║ ██║",
14
+ " ███████║██║ ██║╚██████╔╝╚██████╔╝██████╔╝",
15
+ " ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ "
16
+ ];
15
17
 
16
- function sanitizePath(inputPath) {
17
- const resolved = path.resolve(PROJECT_ROOT, inputPath);
18
- if (!resolved.startsWith(PROJECT_ROOT)) {
19
- throw new Error('Path is outside the project directory.');
20
- }
21
- return resolved;
18
+ // ─── Static Banner (no animation) ──────────────────────────
19
+ function printStaticBanner() {
20
+ const color = chalk.hex('#B5F96C');
21
+ console.log(color(BANNER_ART.join('\n')));
22
+ console.log(color(' ┌──────────────────────────────────────────────────────────┐'));
23
+ console.log(color(' │ Give your computer a job. │'));
24
+ console.log(color(' └──────────────────────────────────────────────────────────┘'));
22
25
  }
23
26
 
24
- /**
25
- * Execute a shell command and return plain text output.
26
- * On Windows, we use PowerShell with `Out-String` to get text.
27
- */
28
- function executeShell(commandString) {
29
- if (!commandString || commandString.trim().length === 0) {
30
- throw new Error('Empty command.');
31
- }
27
+ // ─── Animated Banner (for help) ─────────────────────────────
28
+ const SLEEP = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
32
29
 
33
- let finalCommand = commandString;
34
- if (os.platform() === 'win32') {
35
- // ✅ Force PowerShell to output plain text
36
- // Escape double quotes in the command
37
- const escaped = commandString.replace(/"/g, '`"');
38
- // Wrap in a PowerShell script block and pipe to Out-String
39
- finalCommand = `& { ${escaped} } | Out-String -Width 4096`;
30
+ async function renderAnimatedBanner() {
31
+ if (!process.stdout.isTTY) {
32
+ printStaticBanner();
33
+ return;
40
34
  }
41
35
 
42
- try {
43
- const output = execSync(finalCommand, {
44
- cwd: PROJECT_ROOT,
45
- encoding: 'utf-8',
46
- shell: getShell(),
47
- timeout: 30000,
48
- maxBuffer: MAX_OUTPUT_SIZE,
49
- stdio: 'pipe',
36
+ const colorGradients = [
37
+ ['#364B1D', '#5E8232', '#86B947', '#B5F96C'],
38
+ ['#5E8232', '#86B947', '#B5F96C', '#DDFFA8'],
39
+ ['#86B947', '#B5F96C', '#FFFFFF', '#B5F96C'],
40
+ ['#B5F96C', '#B5F96C', '#B5F96C', '#9EEB49']
41
+ ];
42
+
43
+ for (const colors of colorGradients) {
44
+ process.stdout.write('\x1B[?25l');
45
+ process.stdout.write('\r\x1B[K');
46
+ console.clear();
47
+ console.log();
48
+
49
+ BANNER_ART.forEach((line, index) => {
50
+ const color = colors[index % colors.length];
51
+ console.log(chalk.hex(color).bold(line));
50
52
  });
51
- const trimmed = output.trim();
52
- return trimmed || 'Command executed successfully with no output.';
53
- } catch (err) {
54
- // If the command fails, return the error message
55
- if (err.stderr) {
56
- return `Error: ${err.stderr.trim()}`;
57
- }
58
- if (err.stdout) {
59
- // Some commands output to stdout even on failure (e.g., `dir`)
60
- return err.stdout.trim();
61
- }
62
- throw new Error(`Command execution failed: ${err.message}`);
63
- }
64
- }
65
53
 
66
- function readFile(filePath) {
67
- const safePath = sanitizePath(filePath);
68
- try {
69
- const stats = fs.statSync(safePath);
70
- if (!stats.isFile()) throw new Error('Path is not a file.');
71
- if (stats.size > MAX_OUTPUT_SIZE) {
72
- const buffer = Buffer.alloc(MAX_OUTPUT_SIZE);
73
- const fd = fs.openSync(safePath, 'r');
74
- fs.readSync(fd, buffer, 0, MAX_OUTPUT_SIZE, 0);
75
- fs.closeSync(fd);
76
- return buffer.toString('utf-8') + '\n... (file truncated)';
77
- }
78
- return fs.readFileSync(safePath, 'utf-8');
79
- } catch (err) {
80
- throw new Error(`Failed to read file: ${err.message}`);
54
+ console.log(
55
+ chalk.hex('#3B472E')(
56
+ ' ┌──────────────────────────────────────────────────────────┐\n' +
57
+ ' │'
58
+ ) +
59
+ chalk.hex('#B5F96C').bold(' Give your computer a job. ') +
60
+ chalk.hex('#3B472E')(
61
+ '│\n' +
62
+ ' └──────────────────────────────────────────────────────────┘\n'
63
+ )
64
+ );
65
+
66
+ await SLEEP(75);
81
67
  }
68
+
69
+ process.stdout.write('\x1B[?25h');
82
70
  }
83
71
 
84
- function writeFile(filePath, content) {
85
- const safePath = sanitizePath(filePath);
86
- try {
87
- const dir = path.dirname(safePath);
88
- if (!fs.existsSync(dir)) {
89
- fs.mkdirSync(dir, { recursive: true });
90
- }
91
- fs.writeFileSync(safePath, content, 'utf-8');
92
- return `File written successfully: ${filePath}`;
93
- } catch (err) {
94
- throw new Error(`Failed to write file: ${err.message}`);
95
- }
72
+ // ─── Custom Help ──────────────────────────────────────────────
73
+ async function customHelp() {
74
+ console.log(chalk.hex('#B5F96C').bold('\n SHOUD Terminal Agent - Command Reference\n'));
75
+ console.log(chalk.gray(' Usage: shoud [command] OR shoud "[prompt]"\n'));
76
+
77
+ console.log(chalk.white.bold(' Commands:'));
78
+ console.log(` ${chalk.hex('#B5F96C')('shoud login')} Authenticate this device with your Google account.`);
79
+ console.log(` ${chalk.hex('#B5F96C')('shoud help')} Display this help menu.`);
80
+ console.log(` ${chalk.hex('#B5F96C')('shoud status')} Check your current credit balance and active plan.`);
81
+ console.log(` ${chalk.hex('#B5F96C')('shoud --version')} Show the version number.\n`);
82
+
83
+ console.log(chalk.white.bold(' Autonomous Execution:'));
84
+ console.log(chalk.gray(' Wrap your instructions in quotes to trigger the agent loop.'));
85
+ console.log(` ${chalk.hex('#B5F96C')('>')} shoud "Refactor the auth middleware to use JWTs"`);
86
+ console.log(` ${chalk.hex('#B5F96C')('>')} shoud "Run npm test and fix any failing test cases"\n`);
96
87
  }
97
88
 
98
- function executeTool(toolName, input) {
99
- try {
100
- switch (toolName) {
101
- case 'execute_shell':
102
- return executeShell(input.command);
103
- case 'read_file':
104
- return readFile(input.path);
105
- case 'write_file':
106
- return writeFile(input.path, input.content);
107
- default:
108
- throw new Error(`Tool "${toolName}" is not supported.`);
89
+ // ─── Main ──────────────────────────────────────────────────────
90
+ async function main() {
91
+ const args = process.argv.slice(2);
92
+
93
+ // Intercept help, version, and empty input
94
+ if (args.length === 0 ||
95
+ args[0] === 'help' || args[0] === '--help' || args[0] === '-h' ||
96
+ args[0] === '--version' || args[0] === '-v') {
97
+ if (args[0] === '--version' || args[0] === '-v') {
98
+ program.version('1.0.6');
99
+ program.parse(process.argv);
100
+ return;
109
101
  }
110
- } catch (error) {
111
- return `Tool execution failed: ${error.message}`;
102
+ await renderAnimatedBanner();
103
+ await customHelp();
104
+ return;
112
105
  }
106
+
107
+ program
108
+ .version('1.0.6')
109
+ .description('SHOUD Terminal Agent');
110
+
111
+ program
112
+ .command('login')
113
+ .description('Authenticate this device with your SHOUD account')
114
+ .action(login);
115
+
116
+ // Placeholder for status
117
+ program
118
+ .command('status')
119
+ .description('Check your current credit balance and active plan')
120
+ .action(() => {
121
+ console.log(chalk.yellow(' Status command not yet implemented.'));
122
+ });
123
+
124
+ program
125
+ .argument('[prompt...]', 'The task you want SHOUD to execute')
126
+ .action(async (promptArr) => {
127
+ if (!promptArr || promptArr.length === 0) {
128
+ console.log(chalk.gray(' Usage: shoud "fix the build errors"'));
129
+ return;
130
+ }
131
+ const taskPrompt = promptArr.join(' ');
132
+ await executeTask(taskPrompt);
133
+ // ✅ After task completes, show the static banner
134
+ printStaticBanner();
135
+ });
136
+
137
+ program.parse(process.argv);
113
138
  }
114
139
 
115
- module.exports = { executeTool };
140
+ main();