@insien/cortex-cli 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Aditya Raj
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 ADDED
@@ -0,0 +1,292 @@
1
+ # Cortex
2
+
3
+ ```
4
+ ██████╗ ██████╗ ██████╗ ████████╗███████╗██╗ ██╗
5
+ ██╔════╝██╔═══██╗██╔══██╗╚══██╔══╝██╔════╝╚██╗██╔╝
6
+ ██║ ██║ ██║██████╔╝ ██║ █████╗ ╚███╔╝
7
+ ██║ ██║ ██║██╔══██╗ ██║ ██╔══╝ ██╔██╗
8
+ ╚██████╗╚██████╔╝██║ ██║ ██║ ███████╗██╔╝ ██╗
9
+ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
10
+
11
+ ⚡ AI Agent Orchestrator ⚡
12
+ ```
13
+
14
+ Cortex is a powerful CLI tool that orchestrates AI agent workflows defined in YAML. Run multiple AI agents in parallel, chain their outputs, and automate complex tasks.
15
+
16
+ ## Features
17
+
18
+ - 🚀 **Parallel Execution** - Run independent tasks concurrently
19
+ - 🔗 **Task Dependencies** - Chain tasks with `needs` and pass outputs via templates
20
+ - 🤖 **Multi-Agent Support** - Use Claude Code, OpenCode, or other AI CLIs
21
+ - 📊 **Session Tracking** - View and manage past run sessions
22
+ - 🔔 **Webhooks** - Get notified on task completion/failure
23
+ - ⚙️ **Global Config** - Set defaults in `~/.cortex/config.yml`
24
+
25
+ ## Installation
26
+
27
+ ### npm (Recommended)
28
+
29
+ ```bash
30
+ npm install -g @insien/cortex-cli
31
+ ```
32
+
33
+ ### Quick Install (Shell)
34
+
35
+ ```bash
36
+ curl -fsSL https://raw.githubusercontent.com/obliviious/cortex/main/install.sh | bash
37
+ ```
38
+
39
+ ### Homebrew (macOS/Linux)
40
+
41
+ ```bash
42
+ brew tap obliviious/tap
43
+ brew install cortex
44
+ ```
45
+
46
+ ### Go Install
47
+
48
+ ```bash
49
+ go install github.com/obliviious/cortex/cmd/agentflow@latest
50
+ ```
51
+
52
+ ### From Source
53
+
54
+ ```bash
55
+ git clone https://github.com/obliviious/cortex.git
56
+ cd cortex
57
+ make install
58
+ ```
59
+
60
+ ### Manual Download
61
+
62
+ Download the latest release for your platform from [GitHub Releases](https://github.com/obliviious/cortex/releases).
63
+
64
+ ## Quick Start
65
+
66
+ ### 1. Create a Cortexfile
67
+
68
+ Create `Cortexfile.yml` in your project:
69
+
70
+ ```yaml
71
+ agents:
72
+ architect:
73
+ tool: claude-code
74
+ model: sonnet
75
+
76
+ reviewer:
77
+ tool: claude-code
78
+ model: sonnet
79
+
80
+ tasks:
81
+ analyze:
82
+ agent: architect
83
+ prompt: |
84
+ Analyze the codebase structure and identify areas for improvement.
85
+ Be concise and focus on actionable insights.
86
+
87
+ review:
88
+ agent: reviewer
89
+ prompt: |
90
+ Review the code for security issues and best practices.
91
+
92
+ implement:
93
+ agent: architect
94
+ needs: [analyze, review]
95
+ write: true
96
+ prompt: |
97
+ Based on the analysis and review:
98
+
99
+ ## Analysis:
100
+ {{outputs.analyze}}
101
+
102
+ ## Review:
103
+ {{outputs.review}}
104
+
105
+ Implement the top priority improvement.
106
+ ```
107
+
108
+ ### 2. Run the Workflow
109
+
110
+ ```bash
111
+ cortex run
112
+ ```
113
+
114
+ ### 3. View Past Sessions
115
+
116
+ ```bash
117
+ cortex sessions
118
+ ```
119
+
120
+ ## Commands
121
+
122
+ | Command | Description |
123
+ |---------|-------------|
124
+ | `cortex run` | Execute the Cortexfile workflow |
125
+ | `cortex validate` | Validate configuration without running |
126
+ | `cortex sessions` | List previous run sessions |
127
+
128
+ ### Run Options
129
+
130
+ ```bash
131
+ cortex run [flags]
132
+
133
+ Flags:
134
+ -f, --file string Path to Cortexfile (default: auto-detect)
135
+ -v, --verbose Verbose output
136
+ -s, --stream Stream real-time logs from agents
137
+ --parallel Enable parallel execution (default: on)
138
+ --sequential Force sequential execution
139
+ --max-parallel int Max concurrent tasks (0 = CPU cores)
140
+ --no-color Disable colored output
141
+ --compact Minimal output (no banner)
142
+ ```
143
+
144
+ ### Sessions Options
145
+
146
+ ```bash
147
+ cortex sessions [flags]
148
+
149
+ Flags:
150
+ --project string Filter by project name
151
+ --limit int Max sessions to show (default: 10)
152
+ --failed Show only failed sessions
153
+ ```
154
+
155
+ ## Configuration
156
+
157
+ ### Cortexfile.yml
158
+
159
+ ```yaml
160
+ # Agents define the AI tools to use
161
+ agents:
162
+ my-agent:
163
+ tool: claude-code # or "opencode"
164
+ model: sonnet # optional: model override
165
+
166
+ # Tasks define the workflow
167
+ tasks:
168
+ task-name:
169
+ agent: my-agent # Reference to agent
170
+ prompt: | # Inline prompt
171
+ Your prompt here
172
+ # OR
173
+ prompt_file: prompts/task.md # External file
174
+
175
+ needs: [other-task] # Dependencies (optional)
176
+ write: true # Allow file writes (default: false)
177
+
178
+ # Local settings (optional)
179
+ settings:
180
+ parallel: true
181
+ max_parallel: 4
182
+ ```
183
+
184
+ ### Global Config (~/.cortex/config.yml)
185
+
186
+ ```yaml
187
+ # Default agent settings
188
+ defaults:
189
+ model: sonnet
190
+ tool: claude-code
191
+
192
+ # Execution settings
193
+ settings:
194
+ parallel: true
195
+ max_parallel: 4
196
+ verbose: false
197
+ stream: false
198
+
199
+ # Webhook notifications
200
+ webhooks:
201
+ - url: https://hooks.slack.com/services/xxx
202
+ events: [run_complete, task_failed]
203
+ headers:
204
+ Authorization: "Bearer token"
205
+ ```
206
+
207
+ ## Template Variables
208
+
209
+ Pass outputs between tasks using template variables:
210
+
211
+ ```yaml
212
+ tasks:
213
+ analyze:
214
+ agent: architect
215
+ prompt: Analyze the code...
216
+
217
+ implement:
218
+ agent: coder
219
+ needs: [analyze] # Must declare dependency
220
+ prompt: |
221
+ Based on this analysis:
222
+ {{outputs.analyze}}
223
+
224
+ Implement the changes.
225
+ ```
226
+
227
+ ## Webhooks
228
+
229
+ Configure webhooks to receive notifications:
230
+
231
+ ```yaml
232
+ # In ~/.cortex/config.yml
233
+ webhooks:
234
+ - url: https://your-webhook.com/endpoint
235
+ events:
236
+ - run_start
237
+ - run_complete
238
+ - task_start
239
+ - task_complete
240
+ - task_failed
241
+ headers:
242
+ Authorization: "Bearer your-token"
243
+ ```
244
+
245
+ ### Webhook Payload
246
+
247
+ ```json
248
+ {
249
+ "event": "task_complete",
250
+ "timestamp": "2024-01-04T20:00:00Z",
251
+ "run_id": "20240104-200000",
252
+ "project": "my-project",
253
+ "task": {
254
+ "name": "analyze",
255
+ "agent": "architect",
256
+ "tool": "claude-code",
257
+ "duration": "12.3s",
258
+ "success": true
259
+ }
260
+ }
261
+ ```
262
+
263
+ ## Session Storage
264
+
265
+ Run results are stored in `~/.cortex/sessions/<project>/run-<timestamp>/`:
266
+
267
+ ```
268
+ ~/.cortex/
269
+ ├── config.yml # Global config
270
+ └── sessions/
271
+ └── my-project/
272
+ └── run-20240104-200000/
273
+ ├── run.json # Run summary
274
+ ├── analyze.json # Task results
275
+ └── review.json
276
+ ```
277
+
278
+ ## Supported Tools
279
+
280
+ | Tool | CLI Command | Description |
281
+ |------|-------------|-------------|
282
+ | `claude-code` | `claude` | Anthropic's Claude Code CLI |
283
+ | `opencode` | `opencode` | OpenCode CLI |
284
+
285
+ ## Requirements
286
+
287
+ - One of the supported AI CLI tools installed
288
+ - Go 1.21+ (for building from source)
289
+
290
+ ## License
291
+
292
+ MIT License - see [LICENSE](LICENSE)
package/bin/cortex ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ const BINARY_NAME = 'cortex';
8
+ const ext = process.platform === 'win32' ? '.exe' : '';
9
+ const binaryPath = path.join(__dirname, BINARY_NAME + ext);
10
+
11
+ if (!fs.existsSync(binaryPath)) {
12
+ console.error('Cortex binary not found. Try reinstalling:');
13
+ console.error(' npm uninstall -g cortex-ai && npm install -g cortex-ai');
14
+ process.exit(1);
15
+ }
16
+
17
+ const child = spawn(binaryPath, process.argv.slice(2), {
18
+ stdio: 'inherit',
19
+ env: process.env,
20
+ });
21
+
22
+ child.on('error', (err) => {
23
+ console.error('Failed to start Cortex:', err.message);
24
+ process.exit(1);
25
+ });
26
+
27
+ child.on('close', (code) => {
28
+ process.exit(code || 0);
29
+ });
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@insien/cortex-cli",
3
+ "version": "0.2.0",
4
+ "description": "AI Agent Orchestrator - Run AI workflows defined in YAML",
5
+ "keywords": [
6
+ "ai",
7
+ "agent",
8
+ "orchestrator",
9
+ "claude",
10
+ "workflow",
11
+ "automation",
12
+ "cli"
13
+ ],
14
+ "author": "Aditya Raj",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/obliviious/cortex.git"
19
+ },
20
+ "homepage": "https://github.com/obliviious/cortex",
21
+ "bugs": {
22
+ "url": "https://github.com/obliviious/cortex/issues"
23
+ },
24
+ "bin": {
25
+ "cortex": "./bin/cortex"
26
+ },
27
+ "scripts": {
28
+ "postinstall": "node scripts/postinstall.js",
29
+ "preuninstall": "node scripts/preuninstall.js"
30
+ },
31
+ "files": [
32
+ "bin",
33
+ "scripts",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "engines": {
38
+ "node": ">=14.0.0"
39
+ },
40
+ "os": [
41
+ "darwin",
42
+ "linux",
43
+ "win32"
44
+ ],
45
+ "cpu": [
46
+ "x64",
47
+ "arm64"
48
+ ]
49
+ }
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+
3
+ const https = require('https');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { execSync } = require('child_process');
7
+ const zlib = require('zlib');
8
+
9
+ const REPO = 'obliviious/cortex';
10
+ const BINARY_NAME = 'cortex';
11
+ const VERSION = require('../package.json').version;
12
+
13
+ // Platform mapping
14
+ const PLATFORM_MAP = {
15
+ darwin: 'darwin',
16
+ linux: 'linux',
17
+ win32: 'windows',
18
+ };
19
+
20
+ const ARCH_MAP = {
21
+ x64: 'amd64',
22
+ arm64: 'arm64',
23
+ };
24
+
25
+ function getPlatform() {
26
+ const platform = PLATFORM_MAP[process.platform];
27
+ const arch = ARCH_MAP[process.arch];
28
+
29
+ if (!platform) {
30
+ throw new Error(`Unsupported platform: ${process.platform}`);
31
+ }
32
+ if (!arch) {
33
+ throw new Error(`Unsupported architecture: ${process.arch}`);
34
+ }
35
+
36
+ return { platform, arch };
37
+ }
38
+
39
+ function getBinaryPath() {
40
+ const binDir = path.join(__dirname, '..', 'bin');
41
+ const ext = process.platform === 'win32' ? '.exe' : '';
42
+ return path.join(binDir, BINARY_NAME + ext);
43
+ }
44
+
45
+ function downloadFile(url) {
46
+ return new Promise((resolve, reject) => {
47
+ const request = (url) => {
48
+ https.get(url, { headers: { 'User-Agent': 'cortex-npm' } }, (res) => {
49
+ // Handle redirects
50
+ if (res.statusCode === 301 || res.statusCode === 302) {
51
+ request(res.headers.location);
52
+ return;
53
+ }
54
+
55
+ if (res.statusCode !== 200) {
56
+ reject(new Error(`Failed to download: ${res.statusCode}`));
57
+ return;
58
+ }
59
+
60
+ const chunks = [];
61
+ res.on('data', (chunk) => chunks.push(chunk));
62
+ res.on('end', () => resolve(Buffer.concat(chunks)));
63
+ res.on('error', reject);
64
+ }).on('error', reject);
65
+ };
66
+ request(url);
67
+ });
68
+ }
69
+
70
+ async function extractTarGz(buffer, destPath) {
71
+ const gunzip = zlib.createGunzip();
72
+ const tempDir = path.join(__dirname, '..', '.temp');
73
+
74
+ // Create temp directory
75
+ if (!fs.existsSync(tempDir)) {
76
+ fs.mkdirSync(tempDir, { recursive: true });
77
+ }
78
+
79
+ const tarPath = path.join(tempDir, 'archive.tar');
80
+
81
+ // Decompress gzip
82
+ await new Promise((resolve, reject) => {
83
+ const input = require('stream').Readable.from(buffer);
84
+ const output = fs.createWriteStream(tarPath);
85
+ input.pipe(gunzip).pipe(output);
86
+ output.on('finish', resolve);
87
+ output.on('error', reject);
88
+ });
89
+
90
+ // Extract tar using system tar (more reliable than JS implementations)
91
+ try {
92
+ execSync(`tar -xf "${tarPath}" -C "${tempDir}"`, { stdio: 'pipe' });
93
+ } catch (e) {
94
+ throw new Error('Failed to extract archive. Make sure tar is installed.');
95
+ }
96
+
97
+ // Find and move the binary
98
+ const files = fs.readdirSync(tempDir);
99
+ const binaryFile = files.find(f => f.startsWith(BINARY_NAME) && !f.endsWith('.tar'));
100
+
101
+ if (!binaryFile) {
102
+ throw new Error('Binary not found in archive');
103
+ }
104
+
105
+ const srcPath = path.join(tempDir, binaryFile);
106
+ fs.copyFileSync(srcPath, destPath);
107
+ fs.chmodSync(destPath, 0o755);
108
+
109
+ // Cleanup
110
+ fs.rmSync(tempDir, { recursive: true, force: true });
111
+ }
112
+
113
+ async function extractZip(buffer, destPath) {
114
+ const AdmZip = require('adm-zip');
115
+ const zip = new AdmZip(buffer);
116
+ const entries = zip.getEntries();
117
+
118
+ const binaryEntry = entries.find(e => e.entryName.includes(BINARY_NAME));
119
+ if (!binaryEntry) {
120
+ throw new Error('Binary not found in archive');
121
+ }
122
+
123
+ const binDir = path.dirname(destPath);
124
+ zip.extractEntryTo(binaryEntry, binDir, false, true);
125
+
126
+ // Rename if needed
127
+ const extractedPath = path.join(binDir, binaryEntry.entryName);
128
+ if (extractedPath !== destPath) {
129
+ fs.renameSync(extractedPath, destPath);
130
+ }
131
+ }
132
+
133
+ async function install() {
134
+ console.log('Installing Cortex...');
135
+
136
+ const { platform, arch } = getPlatform();
137
+ const binaryPath = getBinaryPath();
138
+ const binDir = path.dirname(binaryPath);
139
+
140
+ // Ensure bin directory exists
141
+ if (!fs.existsSync(binDir)) {
142
+ fs.mkdirSync(binDir, { recursive: true });
143
+ }
144
+
145
+ // Determine download URL
146
+ const ext = platform === 'windows' ? 'zip' : 'tar.gz';
147
+ const downloadUrl = `https://github.com/${REPO}/releases/download/v${VERSION}/${BINARY_NAME}-${platform}-${arch}.${ext}`;
148
+
149
+ console.log(`Downloading from: ${downloadUrl}`);
150
+
151
+ try {
152
+ const buffer = await downloadFile(downloadUrl);
153
+ console.log('Download complete. Extracting...');
154
+
155
+ if (ext === 'zip') {
156
+ await extractZip(buffer, binaryPath);
157
+ } else {
158
+ await extractTarGz(buffer, binaryPath);
159
+ }
160
+
161
+ console.log(`Cortex installed successfully!`);
162
+ console.log(`Binary location: ${binaryPath}`);
163
+
164
+ // Verify installation
165
+ try {
166
+ const version = execSync(`"${binaryPath}" --version`, { encoding: 'utf8' }).trim();
167
+ console.log(`Version: ${version}`);
168
+ } catch (e) {
169
+ // Ignore verification errors
170
+ }
171
+ } catch (error) {
172
+ console.error('Failed to install Cortex:', error.message);
173
+ console.error('');
174
+ console.error('You can install manually:');
175
+ console.error(' curl -fsSL https://raw.githubusercontent.com/adityaraj/cortex/main/install.sh | bash');
176
+ process.exit(1);
177
+ }
178
+ }
179
+
180
+ install();
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const BINARY_NAME = 'cortex';
7
+
8
+ function getBinaryPath() {
9
+ const binDir = path.join(__dirname, '..', 'bin');
10
+ const ext = process.platform === 'win32' ? '.exe' : '';
11
+ return path.join(binDir, BINARY_NAME + ext);
12
+ }
13
+
14
+ try {
15
+ const binaryPath = getBinaryPath();
16
+ if (fs.existsSync(binaryPath)) {
17
+ fs.unlinkSync(binaryPath);
18
+ console.log('Cortex uninstalled successfully');
19
+ }
20
+ } catch (error) {
21
+ // Ignore errors during uninstall
22
+ }