start-ai-cli 0.1.0 → 0.1.1
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 -21
- package/README.md +67 -19
- package/bin/start-ai-cli.js +237 -138
- package/package.json +58 -32
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026
|
|
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.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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
CHANGED
|
@@ -1,41 +1,63 @@
|
|
|
1
|
-
# start-ai-cli
|
|
1
|
+
# start-ai-cli - AI coding CLI launcher
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/start-ai-cli)
|
|
4
|
+
[](https://www.npmjs.com/package/start-ai-cli)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
`start-ai-cli` is an npm package and command line tool for launching multiple AI coding CLIs from the same project directory. It opens Codex CLI, Claude Code, and Cursor CLI in separate terminal tabs or windows so every assistant starts in the current workspace.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Use it when you want one command to start your AI coding agents:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx start-ai-cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- Launches Codex CLI, Claude Code CLI, and Cursor CLI from the current directory.
|
|
18
|
+
- Opens each available tool in its own Windows Terminal tab or macOS Terminal window.
|
|
19
|
+
- Skips missing CLIs instead of failing when at least one supported tool is available.
|
|
20
|
+
- Works as a global npm command or one-off `npx` command.
|
|
21
|
+
- Has no runtime dependencies.
|
|
13
22
|
|
|
14
23
|
## Install
|
|
15
24
|
|
|
25
|
+
Run without installing:
|
|
26
|
+
|
|
16
27
|
```bash
|
|
17
|
-
|
|
28
|
+
npx start-ai-cli
|
|
18
29
|
```
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
Install globally:
|
|
21
32
|
|
|
22
33
|
```bash
|
|
23
|
-
npm
|
|
34
|
+
npm install -g start-ai-cli
|
|
24
35
|
```
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Run the command from the project directory you want both tools to use:
|
|
37
|
+
Then run it from the project directory you want the AI tools to use:
|
|
29
38
|
|
|
30
39
|
```bash
|
|
31
40
|
start-ai-cli
|
|
32
41
|
```
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
## Supported Tools
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
| Tool | Required command | Terminal title |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| Codex CLI | `codex` | `Codex` |
|
|
48
|
+
| Claude Code | `claude` | `Claude` |
|
|
49
|
+
| Cursor CLI | `agent` | `Cursor` |
|
|
50
|
+
|
|
51
|
+
On Windows, `start-ai-cli` uses Windows Terminal (`wt.exe`) with PowerShell. On macOS, it uses Terminal.app through `osascript`.
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- Windows or macOS
|
|
56
|
+
- Node.js 18 or newer
|
|
57
|
+
- Windows Terminal available as `wt.exe` on Windows
|
|
58
|
+
- PowerShell 7 available as `pwsh.exe`, or Windows PowerShell, on Windows
|
|
59
|
+
- Terminal.app and `osascript` available on macOS
|
|
60
|
+
- At least one supported AI coding CLI available in `PATH`
|
|
39
61
|
|
|
40
62
|
## Options
|
|
41
63
|
|
|
@@ -44,6 +66,30 @@ start-ai-cli --help
|
|
|
44
66
|
start-ai-cli --version
|
|
45
67
|
```
|
|
46
68
|
|
|
69
|
+
## Troubleshooting
|
|
70
|
+
|
|
71
|
+
### `start-ai-cli cannot start: no CLI tools found in PATH`
|
|
72
|
+
|
|
73
|
+
Install or expose at least one supported command in your shell:
|
|
74
|
+
|
|
75
|
+
- `codex` for Codex CLI
|
|
76
|
+
- `claude` for Claude Code
|
|
77
|
+
- `agent` for Cursor CLI
|
|
78
|
+
|
|
79
|
+
### Windows Terminal was not found
|
|
80
|
+
|
|
81
|
+
Install Windows Terminal and make sure `wt.exe` is available in `PATH`.
|
|
82
|
+
|
|
83
|
+
### PowerShell was not found
|
|
84
|
+
|
|
85
|
+
Install PowerShell 7 (`pwsh.exe`) or make sure Windows PowerShell (`powershell.exe`) is available.
|
|
86
|
+
|
|
87
|
+
## Package Links
|
|
88
|
+
|
|
89
|
+
- npm package: https://www.npmjs.com/package/start-ai-cli
|
|
90
|
+
- GitHub repository: https://github.com/guoxiao0521/open-ai-cli
|
|
91
|
+
- Issues: https://github.com/guoxiao0521/open-ai-cli/issues
|
|
92
|
+
|
|
47
93
|
## Development
|
|
48
94
|
|
|
49
95
|
```bash
|
|
@@ -54,6 +100,8 @@ npm run publish:dry-run
|
|
|
54
100
|
|
|
55
101
|
## Publish
|
|
56
102
|
|
|
103
|
+
`README.md` and npm metadata changes appear on npm after publishing a new package version.
|
|
104
|
+
|
|
57
105
|
```bash
|
|
58
106
|
npm login --registry=https://registry.npmjs.org/
|
|
59
107
|
npm publish
|
package/bin/start-ai-cli.js
CHANGED
|
@@ -1,170 +1,269 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { spawn, spawnSync } from 'node:child_process';
|
|
4
|
-
import { readFileSync, realpathSync } from 'node:fs';
|
|
5
|
-
import { dirname, resolve } from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
-
|
|
8
|
-
const COMMAND = 'start-ai-cli';
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
{ command: 'wt.exe', label: 'Windows Terminal (wt.exe)' },
|
|
12
|
-
{ command: '
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
|
|
3
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
4
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
5
|
+
import { dirname, resolve, win32 as pathWin32 } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const COMMAND = 'start-ai-cli';
|
|
9
|
+
|
|
10
|
+
const HARD_REQUIREMENTS_BY_PLATFORM = {
|
|
11
|
+
win32: [{ command: 'wt.exe', label: 'Windows Terminal (wt.exe)' }],
|
|
12
|
+
darwin: [{ command: 'osascript', label: 'AppleScript runner (osascript)' }]
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const CLI_COMMANDS = [
|
|
16
|
+
{ command: 'codex', label: 'Codex CLI (codex)', title: 'Codex' },
|
|
17
|
+
{ command: 'claude', label: 'Claude Code CLI (claude)', title: 'Claude' },
|
|
18
|
+
{ command: 'agent', label: 'Cursor CLI (agent)', title: 'Cursor' }
|
|
15
19
|
];
|
|
16
20
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
${COMMAND}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
21
|
+
const DEFAULT_WINDOWS_SHELL_COMMAND = 'pwsh.exe';
|
|
22
|
+
|
|
23
|
+
const HELP_TEXT = `Usage:
|
|
24
|
+
${COMMAND}
|
|
25
|
+
${COMMAND} --help
|
|
26
|
+
${COMMAND} --version
|
|
27
|
+
|
|
28
|
+
Opens terminal tabs/windows in the current directory:
|
|
29
|
+
- Codex: runs "codex"
|
|
30
|
+
- Claude: runs "claude"
|
|
31
|
+
- Cursor: runs "agent"
|
|
32
|
+
|
|
27
33
|
Requirements:
|
|
28
|
-
- Windows
|
|
29
|
-
- Windows Terminal (wt.exe)
|
|
34
|
+
- Windows with Windows Terminal (wt.exe) and PowerShell, or macOS with Terminal.app
|
|
30
35
|
- Codex CLI available as "codex"
|
|
31
36
|
- Claude Code CLI available as "claude"
|
|
32
37
|
- Cursor CLI available as "agent"
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
export function parseArgs(args) {
|
|
36
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
37
|
-
return { action: 'help' };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (args.includes('--version') || args.includes('-v')) {
|
|
41
|
-
return { action: 'version' };
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
export function parseArgs(args) {
|
|
41
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
42
|
+
return { action: 'help' };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
46
|
+
return { action: 'version' };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const unknown = args.filter((arg) => arg.startsWith('-'));
|
|
50
|
+
if (unknown.length > 0) {
|
|
51
|
+
return { action: 'error', message: `Unknown option: ${unknown.join(', ')}` };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { action: 'open' };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getPackageVersion() {
|
|
58
|
+
const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
59
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
60
|
+
return packageJson.version;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function buildWtArgs({ cwd, tabs, shellCommand = DEFAULT_WINDOWS_SHELL_COMMAND }) {
|
|
64
|
+
const result = [];
|
|
65
|
+
for (let i = 0; i < tabs.length; i++) {
|
|
66
|
+
if (i > 0) result.push(';');
|
|
67
|
+
result.push(
|
|
68
|
+
'new-tab',
|
|
69
|
+
'--title',
|
|
70
|
+
tabs[i].title,
|
|
71
|
+
'-d',
|
|
72
|
+
cwd,
|
|
73
|
+
shellCommand,
|
|
74
|
+
'-NoExit',
|
|
75
|
+
'-ExecutionPolicy',
|
|
76
|
+
'Bypass',
|
|
77
|
+
'-Command',
|
|
78
|
+
tabs[i].command
|
|
79
|
+
);
|
|
42
80
|
}
|
|
43
|
-
|
|
44
|
-
const unknown = args.filter((arg) => arg.startsWith('-'));
|
|
45
|
-
if (unknown.length > 0) {
|
|
46
|
-
return { action: 'error', message: `Unknown option: ${unknown.join(', ')}` };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return { action: 'open' };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function getPackageVersion() {
|
|
53
|
-
const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
54
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
55
|
-
return packageJson.version;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function buildWtArgs({ cwd, codexCommand = 'codex', claudeCommand = 'claude', cursorCommand = 'agent' }) {
|
|
59
|
-
return [
|
|
60
|
-
'new-tab',
|
|
61
|
-
'--title',
|
|
62
|
-
'Codex',
|
|
63
|
-
'-d',
|
|
64
|
-
cwd,
|
|
65
|
-
'powershell.exe',
|
|
66
|
-
'-NoExit',
|
|
67
|
-
'-Command',
|
|
68
|
-
codexCommand,
|
|
69
|
-
';',
|
|
70
|
-
'new-tab',
|
|
71
|
-
'--title',
|
|
72
|
-
'Claude',
|
|
73
|
-
'-d',
|
|
74
|
-
cwd,
|
|
75
|
-
'powershell.exe',
|
|
76
|
-
'-NoExit',
|
|
77
|
-
'-Command',
|
|
78
|
-
claudeCommand,
|
|
79
|
-
';',
|
|
80
|
-
'new-tab',
|
|
81
|
-
'--title',
|
|
82
|
-
'Cursor',
|
|
83
|
-
'-d',
|
|
84
|
-
cwd,
|
|
85
|
-
'powershell.exe',
|
|
86
|
-
'-NoExit',
|
|
87
|
-
'-Command',
|
|
88
|
-
cursorCommand
|
|
89
|
-
];
|
|
81
|
+
return result;
|
|
90
82
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
|
|
84
|
+
function shellQuote(value) {
|
|
85
|
+
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function appleScriptQuote(value) {
|
|
89
|
+
return JSON.stringify(String(value));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function buildMacTerminalScript({ cwd, tabs }) {
|
|
93
|
+
const commands = tabs.map(({ command }) => `cd ${shellQuote(cwd)} && ${command}`);
|
|
94
|
+
const lines = [
|
|
95
|
+
'tell application "Terminal"',
|
|
96
|
+
' activate'
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
for (const command of commands) {
|
|
100
|
+
lines.push(` do script ${appleScriptQuote(command)}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
lines.push('end tell');
|
|
104
|
+
return lines.join('\n');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function buildMacTerminalArgs({ cwd, tabs }) {
|
|
108
|
+
return ['-e', buildMacTerminalScript({ cwd, tabs })];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function commandExists(command, { env = process.env, platform = process.platform } = {}) {
|
|
112
|
+
const executable = platform === 'win32' ? 'where.exe' : 'sh';
|
|
113
|
+
const args = platform === 'win32'
|
|
114
|
+
? [command]
|
|
115
|
+
: ['-lc', `command -v ${shellQuote(command)}`];
|
|
116
|
+
|
|
117
|
+
const result = spawnSync(executable, args, {
|
|
118
|
+
env,
|
|
119
|
+
stdio: 'ignore',
|
|
120
|
+
windowsHide: true
|
|
121
|
+
});
|
|
98
122
|
|
|
99
123
|
return result.status === 0;
|
|
100
124
|
}
|
|
101
125
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
126
|
+
function getSystemWindowsPowerShellPath(env = process.env) {
|
|
127
|
+
const systemRoot = env.SystemRoot ?? env.SYSTEMROOT ?? env.windir ?? env.WINDIR;
|
|
128
|
+
if (!systemRoot) {
|
|
129
|
+
return null;
|
|
105
130
|
}
|
|
106
131
|
|
|
107
|
-
return
|
|
108
|
-
.filter(({ command }) => !commandExists(command, { env }))
|
|
109
|
-
.map(({ label }) => `${label} was not found in PATH.`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function launchTerminals({ cwd = process.cwd(), env = process.env } = {}) {
|
|
113
|
-
const child = spawn('wt.exe', buildWtArgs({ cwd }), {
|
|
114
|
-
cwd,
|
|
115
|
-
env,
|
|
116
|
-
detached: true,
|
|
117
|
-
stdio: 'ignore',
|
|
118
|
-
windowsHide: false
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
child.unref();
|
|
132
|
+
return pathWin32.join(systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
122
133
|
}
|
|
123
134
|
|
|
124
|
-
export function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
135
|
+
export function getWindowsShellCommand({
|
|
136
|
+
env = process.env,
|
|
137
|
+
platform = process.platform,
|
|
138
|
+
commandExistsFn = commandExists,
|
|
139
|
+
fileExistsFn = existsSync
|
|
140
|
+
} = {}) {
|
|
141
|
+
if (platform !== 'win32') {
|
|
142
|
+
return null;
|
|
130
143
|
}
|
|
131
144
|
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
return 0;
|
|
145
|
+
if (commandExistsFn('pwsh.exe', { env, platform })) {
|
|
146
|
+
return 'pwsh.exe';
|
|
135
147
|
}
|
|
136
148
|
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
console.error(`Run "${COMMAND} --help" for usage.`);
|
|
140
|
-
return 1;
|
|
149
|
+
if (commandExistsFn('powershell.exe', { env, platform })) {
|
|
150
|
+
return 'powershell.exe';
|
|
141
151
|
}
|
|
142
152
|
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
for (const message of missing) {
|
|
147
|
-
console.error(`- ${message}`);
|
|
148
|
-
}
|
|
149
|
-
return 1;
|
|
153
|
+
const systemPowerShellPath = getSystemWindowsPowerShellPath(env);
|
|
154
|
+
if (systemPowerShellPath && fileExistsFn(systemPowerShellPath)) {
|
|
155
|
+
return systemPowerShellPath;
|
|
150
156
|
}
|
|
151
157
|
|
|
152
|
-
|
|
153
|
-
console.log('Opened Codex CLI, Claude Code, and Cursor CLI in Windows Terminal.');
|
|
154
|
-
return 0;
|
|
158
|
+
return null;
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
export function
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
export function getMissingRequirements({ platform = process.platform, env = process.env } = {}) {
|
|
162
|
+
const requirements = HARD_REQUIREMENTS_BY_PLATFORM[platform];
|
|
163
|
+
if (!requirements) {
|
|
164
|
+
return ['Windows or macOS is required.'];
|
|
160
165
|
}
|
|
161
166
|
|
|
162
|
-
const
|
|
163
|
-
|
|
167
|
+
const missing = requirements
|
|
168
|
+
.filter(({ command }) => !commandExists(command, { env, platform }))
|
|
169
|
+
.map(({ label }) => `${label} was not found in PATH.`);
|
|
164
170
|
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
if (platform === 'win32' && !getWindowsShellCommand({ env, platform })) {
|
|
172
|
+
missing.push('PowerShell (pwsh.exe or powershell.exe) was not found.');
|
|
173
|
+
}
|
|
167
174
|
|
|
168
|
-
|
|
169
|
-
process.exitCode = main();
|
|
175
|
+
return missing;
|
|
170
176
|
}
|
|
177
|
+
|
|
178
|
+
export function getAvailableCliTabs({ env = process.env, platform = process.platform } = {}) {
|
|
179
|
+
return CLI_COMMANDS.filter(({ command }) => commandExists(command, { env, platform }));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function getMissingCliLabels({ env = process.env, platform = process.platform } = {}) {
|
|
183
|
+
return CLI_COMMANDS
|
|
184
|
+
.filter(({ command }) => !commandExists(command, { env, platform }))
|
|
185
|
+
.map(({ label }) => label);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function launchTerminals({ cwd = process.cwd(), env = process.env, platform = process.platform, tabs = CLI_COMMANDS } = {}) {
|
|
189
|
+
const executable = platform === 'win32' ? 'wt.exe' : 'osascript';
|
|
190
|
+
const shellCommand = platform === 'win32' ? getWindowsShellCommand({ env, platform }) : undefined;
|
|
191
|
+
if (platform === 'win32' && !shellCommand) {
|
|
192
|
+
throw new Error('PowerShell (pwsh.exe or powershell.exe) was not found.');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const args = platform === 'win32'
|
|
196
|
+
? buildWtArgs({ cwd, tabs, shellCommand })
|
|
197
|
+
: buildMacTerminalArgs({ cwd, tabs });
|
|
198
|
+
|
|
199
|
+
const child = spawn(executable, args, {
|
|
200
|
+
cwd,
|
|
201
|
+
env,
|
|
202
|
+
detached: true,
|
|
203
|
+
stdio: 'ignore',
|
|
204
|
+
windowsHide: false
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
child.unref();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function main(args = process.argv.slice(2), options = {}) {
|
|
211
|
+
const parsed = parseArgs(args);
|
|
212
|
+
|
|
213
|
+
if (parsed.action === 'help') {
|
|
214
|
+
console.log(HELP_TEXT.trimEnd());
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (parsed.action === 'version') {
|
|
219
|
+
console.log(getPackageVersion());
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (parsed.action === 'error') {
|
|
224
|
+
console.error(parsed.message);
|
|
225
|
+
console.error(`Run "${COMMAND} --help" for usage.`);
|
|
226
|
+
return 1;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const missing = getMissingRequirements(options);
|
|
230
|
+
if (missing.length > 0) {
|
|
231
|
+
console.error(`${COMMAND} cannot start:`);
|
|
232
|
+
for (const message of missing) {
|
|
233
|
+
console.error(`- ${message}`);
|
|
234
|
+
}
|
|
235
|
+
return 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const missingClis = getMissingCliLabels(options);
|
|
239
|
+
for (const label of missingClis) {
|
|
240
|
+
console.warn(`Warning: ${label} was not found in PATH, skipping.`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const availableTabs = getAvailableCliTabs(options);
|
|
244
|
+
if (availableTabs.length === 0) {
|
|
245
|
+
console.error(`${COMMAND} cannot start: no CLI tools found in PATH.`);
|
|
246
|
+
return 1;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
launchTerminals({ ...options, tabs: availableTabs });
|
|
250
|
+
const launched = availableTabs.map(({ title }) => title).join(', ');
|
|
251
|
+
const terminalName = (options.platform ?? process.platform) === 'win32' ? 'Windows Terminal' : 'Terminal.app';
|
|
252
|
+
console.log(`Opened ${launched} in ${terminalName}.`);
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function isEntrypoint(argvPath = process.argv[1], moduleUrl = import.meta.url) {
|
|
257
|
+
if (!argvPath) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const argvRealPath = realpathSync(resolve(argvPath));
|
|
262
|
+
const moduleRealPath = realpathSync(fileURLToPath(moduleUrl));
|
|
263
|
+
|
|
264
|
+
return argvRealPath === moduleRealPath;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (isEntrypoint()) {
|
|
268
|
+
process.exitCode = main();
|
|
269
|
+
}
|
package/package.json
CHANGED
|
@@ -1,45 +1,71 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
2
|
"name": "start-ai-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Launch Codex CLI, Claude Code, and Cursor CLI together from one project directory in Windows Terminal or macOS Terminal.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "bin/start-ai-cli.js",
|
|
7
|
+
"exports": "./bin/start-ai-cli.js",
|
|
6
8
|
"bin": {
|
|
7
9
|
"start-ai-cli": "bin/start-ai-cli.js"
|
|
8
10
|
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "node --test",
|
|
11
|
-
"pack:dry-run": "npm pack --dry-run",
|
|
12
|
-
"publish:dry-run": "npm publish --dry-run"
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"bin/",
|
|
16
|
-
"README.md",
|
|
17
|
-
"LICENSE"
|
|
18
|
-
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "node --test",
|
|
13
|
+
"pack:dry-run": "npm pack --dry-run",
|
|
14
|
+
"publish:dry-run": "npm publish --dry-run"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"bin/",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
19
21
|
"keywords": [
|
|
22
|
+
"start-ai-cli",
|
|
23
|
+
"open-ai-cli",
|
|
24
|
+
"ai-cli",
|
|
25
|
+
"ai-agent",
|
|
26
|
+
"ai-coding",
|
|
27
|
+
"ai-coding-agent",
|
|
28
|
+
"coding-agent",
|
|
20
29
|
"codex",
|
|
30
|
+
"codex-cli",
|
|
31
|
+
"openai-codex",
|
|
21
32
|
"claude",
|
|
33
|
+
"claude-code",
|
|
34
|
+
"claude-code-cli",
|
|
22
35
|
"cursor",
|
|
36
|
+
"cursor-cli",
|
|
37
|
+
"cursor-agent",
|
|
23
38
|
"cli",
|
|
39
|
+
"cli-launcher",
|
|
40
|
+
"developer-tools",
|
|
24
41
|
"terminal",
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"url": "git+https://github.com/guoxiao0521/open-ai-cli.git"
|
|
31
|
-
},
|
|
32
|
-
"bugs": {
|
|
33
|
-
"url": "https://github.com/guoxiao0521/open-ai-cli/issues"
|
|
34
|
-
},
|
|
35
|
-
"homepage": "https://github.com/guoxiao0521/open-ai-cli#readme",
|
|
36
|
-
"os": [
|
|
37
|
-
"win32"
|
|
42
|
+
"terminal-tabs",
|
|
43
|
+
"windows-terminal",
|
|
44
|
+
"macos",
|
|
45
|
+
"macos-terminal",
|
|
46
|
+
"productivity"
|
|
38
47
|
],
|
|
39
|
-
"
|
|
40
|
-
"
|
|
48
|
+
"author": {
|
|
49
|
+
"name": "guoxiao0521",
|
|
50
|
+
"url": "https://github.com/guoxiao0521"
|
|
41
51
|
},
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/guoxiao0521/open-ai-cli.git"
|
|
56
|
+
},
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/guoxiao0521/open-ai-cli/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/guoxiao0521/open-ai-cli#readme",
|
|
61
|
+
"os": [
|
|
62
|
+
"win32",
|
|
63
|
+
"darwin"
|
|
64
|
+
],
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18"
|
|
67
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"registry": "https://registry.npmjs.org/"
|
|
70
|
+
}
|
|
71
|
+
}
|