octo-dev 0.4.0 → 0.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octo-dev",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "CLI for monorepo build orchestration, semantic versioning, and local infrastructure management",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -44,6 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "commander": "^13.1.0",
47
+ "execa": "^9.6.1",
47
48
  "ollama": "^0.5.16",
48
49
  "semver": "^7.7.2",
49
50
  "tsx": "^4.19.4",
@@ -55,7 +56,7 @@
55
56
  "@types/semver": "^7.7.0",
56
57
  "fast-check": "^4.1.1",
57
58
  "typescript": "^5.8.3",
58
- "vitest": "^3.2.4"
59
+ "vitest": "^4.1.8"
59
60
  },
60
61
  "engines": {
61
62
  "node": ">=25.0.0"
package/src/cli/index.ts CHANGED
@@ -11,7 +11,7 @@ const program = new Command();
11
11
  program
12
12
  .name('octo')
13
13
  .description('Monorepo build orchestration, versioning, and infrastructure CLI')
14
- .version('0.4.0');
14
+ .version('0.4.2');
15
15
 
16
16
  program
17
17
  .command('init')
@@ -29,7 +29,7 @@ export async function runHooks(trigger: HookTrigger, context: HookContext): Prom
29
29
  logger.info(`Running hook "${hook.name}" (${trigger})...`);
30
30
  const start = Date.now();
31
31
 
32
- const result = await run(hook.command, [], { cwd: context.workingDir });
32
+ const result = await run(hook.command, [], { cwd: context.workingDir, shell: true });
33
33
  const durationMs = Date.now() - start;
34
34
  const output = (result.stdout + result.stderr).trim() || undefined;
35
35
 
@@ -24,9 +24,10 @@ async function hasModel(model: string): Promise<boolean> {
24
24
  */
25
25
  async function install(): Promise<boolean> {
26
26
  logger.info('Instalando Ollama...');
27
- const result = await run('curl', ['-fsSL', 'https://ollama.com/install.sh', '|', 'sh'], {
27
+ const result = await run('curl -fsSL https://ollama.com/install.sh | sh', [], {
28
28
  timeout: 120_000,
29
29
  interactive: true,
30
+ shell: true,
30
31
  });
31
32
  return result.exitCode === 0;
32
33
  }
@@ -1,4 +1,4 @@
1
- import { spawn } from 'node:child_process';
1
+ import { execaCommand, execa } from 'execa';
2
2
 
3
3
  export interface RunOptions {
4
4
  cwd?: string;
@@ -6,6 +6,8 @@ export interface RunOptions {
6
6
  env?: Record<string, string>;
7
7
  /** When true, inherits stdio so the user can interact (e.g. git password prompts) */
8
8
  interactive?: boolean;
9
+ /** When true, runs command through the system shell. Use only for piped/complex commands. */
10
+ shell?: boolean;
9
11
  }
10
12
 
11
13
  export interface RunResult {
@@ -14,39 +16,31 @@ export interface RunResult {
14
16
  exitCode: number;
15
17
  }
16
18
 
17
- /** Wrapper for child_process.spawn with Promise, timeout, and stdout/stderr capture */
18
- export function run(command: string, args: string[] = [], options: RunOptions = {}): Promise<RunResult> {
19
- const { cwd, timeout = 60_000, env, interactive = false } = options;
20
-
21
- return new Promise((resolve, reject) => {
22
- const child = spawn(command, args, {
23
- cwd,
24
- env: env ? { ...process.env, ...env } : process.env,
25
- shell: true,
26
- stdio: interactive ? 'inherit' : 'pipe',
27
- });
28
-
29
- let stdout = '';
30
- let stderr = '';
31
-
32
- if (!interactive) {
33
- child.stdout!.on('data', (data: Buffer) => { stdout += data.toString(); });
34
- child.stderr!.on('data', (data: Buffer) => { stderr += data.toString(); });
35
- }
36
-
37
- const timer = setTimeout(() => {
38
- child.kill('SIGTERM');
39
- reject(new Error(`Command timed out after ${timeout}ms: ${command} ${args.join(' ')}`));
40
- }, timeout);
41
-
42
- child.on('close', (code) => {
43
- clearTimeout(timer);
44
- resolve({ stdout, stderr, exitCode: code ?? 1 });
45
- });
46
-
47
- child.on('error', (err) => {
48
- clearTimeout(timer);
49
- reject(err);
50
- });
51
- });
19
+ /**
20
+ * Executes a command with args. Powered by execa.
21
+ * Returns stdout, stderr, and exitCode without throwing on non-zero exit.
22
+ */
23
+ export async function run(command: string, args: string[] = [], options: RunOptions = {}): Promise<RunResult> {
24
+ const { cwd, timeout = 60_000, env, interactive = false, shell = false } = options;
25
+
26
+ const execOptions = {
27
+ cwd,
28
+ env: env ? { ...process.env, ...env } : undefined,
29
+ timeout,
30
+ shell,
31
+ reject: false,
32
+ stdin: interactive ? 'inherit' as const : undefined,
33
+ stdout: interactive ? 'inherit' as const : 'pipe' as const,
34
+ stderr: interactive ? 'inherit' as const : 'pipe' as const,
35
+ };
36
+
37
+ const result = shell && args.length === 0
38
+ ? await execaCommand(command, execOptions)
39
+ : await execa(command, args, execOptions);
40
+
41
+ return {
42
+ stdout: result.stdout ?? '',
43
+ stderr: result.stderr ?? '',
44
+ exitCode: result.exitCode ?? 1,
45
+ };
52
46
  }