agentxchain 0.1.1 → 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/README.md +27 -0
- package/bin/agentxchain.js +38 -4
- package/package.json +1 -1
- package/src/adapters/cursor.js +134 -44
- package/src/commands/claim.js +117 -0
- package/src/commands/config.js +149 -0
- package/src/commands/init.js +123 -71
- package/src/commands/status.js +54 -16
- package/src/commands/stop.js +43 -59
- package/src/commands/update.js +42 -0
- package/src/commands/watch.js +210 -0
- package/src/lib/notify.js +24 -0
- package/src/lib/repo.js +37 -0
- package/src/lib/seed-prompt.js +49 -24
- package/src/templates/api-builder.json +29 -0
- package/src/templates/bug-squad.json +25 -0
- package/src/templates/landing-page.json +36 -0
- package/src/templates/refactor-team.json +25 -0
- package/src/templates/saas-mvp.json +29 -0
package/src/commands/init.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
-
import { join, resolve } from 'path';
|
|
1
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
2
|
+
import { join, resolve, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
3
4
|
import chalk from 'chalk';
|
|
4
5
|
import inquirer from 'inquirer';
|
|
5
6
|
import { CONFIG_FILE, LOCK_FILE, STATE_FILE } from '../lib/config.js';
|
|
6
7
|
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const TEMPLATES_DIR = join(__dirname, '../templates');
|
|
10
|
+
|
|
7
11
|
const DEFAULT_AGENTS = {
|
|
8
12
|
pm: {
|
|
9
13
|
name: 'Product Manager',
|
|
@@ -24,54 +28,93 @@ const DEFAULT_AGENTS = {
|
|
|
24
28
|
};
|
|
25
29
|
|
|
26
30
|
function slugify(name) {
|
|
27
|
-
return name
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function loadTemplates() {
|
|
35
|
+
const templates = [];
|
|
36
|
+
try {
|
|
37
|
+
const files = readdirSync(TEMPLATES_DIR).filter(f => f.endsWith('.json'));
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
const raw = readFileSync(join(TEMPLATES_DIR, file), 'utf8');
|
|
40
|
+
const tmpl = JSON.parse(raw);
|
|
41
|
+
templates.push({ id: file.replace('.json', ''), ...tmpl });
|
|
42
|
+
}
|
|
43
|
+
} catch {}
|
|
44
|
+
return templates;
|
|
31
45
|
}
|
|
32
46
|
|
|
33
47
|
export async function initCommand(opts) {
|
|
34
|
-
let project, agents, folderName;
|
|
48
|
+
let project, agents, folderName, rules;
|
|
35
49
|
|
|
36
50
|
if (opts.yes) {
|
|
37
51
|
project = 'My AgentXchain project';
|
|
38
52
|
agents = DEFAULT_AGENTS;
|
|
39
53
|
folderName = slugify(project);
|
|
54
|
+
rules = { max_consecutive_claims: 2, require_message: true, compress_after_words: 5000 };
|
|
40
55
|
} else {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
folderName = folderAnswer.folder;
|
|
59
|
-
|
|
60
|
-
const agentAnswer = await inquirer.prompt([{
|
|
61
|
-
type: 'confirm',
|
|
62
|
-
name: 'useDefaults',
|
|
63
|
-
message: 'Use default agents (pm, dev, qa, ux)?',
|
|
64
|
-
default: true
|
|
56
|
+
const templates = loadTemplates();
|
|
57
|
+
|
|
58
|
+
// Template selection
|
|
59
|
+
const templateChoices = [
|
|
60
|
+
{ name: `${chalk.cyan('Custom')} — define your own agents`, value: 'custom' },
|
|
61
|
+
{ name: `${chalk.cyan('Default')} — PM, Dev, QA, UX (4 agents)`, value: 'default' },
|
|
62
|
+
...templates.map(t => ({
|
|
63
|
+
name: `${chalk.cyan(t.label)} — ${t.description} (${Object.keys(t.agents).length} agents)`,
|
|
64
|
+
value: t.id
|
|
65
|
+
}))
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const { template } = await inquirer.prompt([{
|
|
69
|
+
type: 'list',
|
|
70
|
+
name: 'template',
|
|
71
|
+
message: 'Choose a team template:',
|
|
72
|
+
choices: templateChoices
|
|
65
73
|
}]);
|
|
66
74
|
|
|
67
|
-
if (
|
|
75
|
+
if (template !== 'custom' && template !== 'default') {
|
|
76
|
+
const tmpl = templates.find(t => t.id === template);
|
|
77
|
+
agents = tmpl.agents;
|
|
78
|
+
rules = tmpl.rules || {};
|
|
79
|
+
const { projectName } = await inquirer.prompt([{
|
|
80
|
+
type: 'input',
|
|
81
|
+
name: 'projectName',
|
|
82
|
+
message: 'Project name:',
|
|
83
|
+
default: tmpl.project
|
|
84
|
+
}]);
|
|
85
|
+
project = projectName;
|
|
86
|
+
} else if (template === 'default') {
|
|
68
87
|
agents = DEFAULT_AGENTS;
|
|
88
|
+
rules = { max_consecutive_claims: 2, require_message: true, compress_after_words: 5000 };
|
|
89
|
+
const { projectName } = await inquirer.prompt([{
|
|
90
|
+
type: 'input',
|
|
91
|
+
name: 'projectName',
|
|
92
|
+
message: 'Project name:',
|
|
93
|
+
default: 'My AgentXchain project'
|
|
94
|
+
}]);
|
|
95
|
+
project = projectName;
|
|
69
96
|
} else {
|
|
97
|
+
const { projectName } = await inquirer.prompt([{
|
|
98
|
+
type: 'input',
|
|
99
|
+
name: 'projectName',
|
|
100
|
+
message: 'Project name:',
|
|
101
|
+
default: 'My AgentXchain project'
|
|
102
|
+
}]);
|
|
103
|
+
project = projectName;
|
|
70
104
|
agents = {};
|
|
105
|
+
rules = { max_consecutive_claims: 2, require_message: true, compress_after_words: 5000 };
|
|
71
106
|
let adding = true;
|
|
72
107
|
while (adding) {
|
|
73
108
|
const agent = await inquirer.prompt([
|
|
74
|
-
{
|
|
109
|
+
{
|
|
110
|
+
type: 'input', name: 'id', message: 'Agent ID (lowercase, no spaces):',
|
|
111
|
+
validate: v => {
|
|
112
|
+
if (!v.match(/^[a-z0-9-]+$/)) return 'Lowercase letters, numbers, hyphens only.';
|
|
113
|
+
if (v === 'human' || v === 'system') return `"${v}" is reserved.`;
|
|
114
|
+
if (agents[v]) return `"${v}" already added.`;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
75
118
|
{ type: 'input', name: 'name', message: 'Display name:' },
|
|
76
119
|
{ type: 'input', name: 'mandate', message: 'Mandate (what this agent does):' },
|
|
77
120
|
{ type: 'confirm', name: 'more', message: 'Add another agent?', default: true }
|
|
@@ -80,72 +123,81 @@ export async function initCommand(opts) {
|
|
|
80
123
|
adding = agent.more;
|
|
81
124
|
}
|
|
82
125
|
}
|
|
126
|
+
|
|
127
|
+
folderName = slugify(project);
|
|
128
|
+
const { folder } = await inquirer.prompt([{
|
|
129
|
+
type: 'input',
|
|
130
|
+
name: 'folder',
|
|
131
|
+
message: 'Folder name:',
|
|
132
|
+
default: folderName
|
|
133
|
+
}]);
|
|
134
|
+
folderName = folder;
|
|
83
135
|
}
|
|
84
136
|
|
|
85
137
|
const dir = resolve(process.cwd(), folderName);
|
|
86
138
|
|
|
87
|
-
if (existsSync(dir)) {
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
139
|
+
if (existsSync(dir) && existsSync(join(dir, CONFIG_FILE))) {
|
|
140
|
+
if (!opts.yes) {
|
|
141
|
+
const { overwrite } = await inquirer.prompt([{
|
|
142
|
+
type: 'confirm',
|
|
143
|
+
name: 'overwrite',
|
|
144
|
+
message: `${folderName}/ already has agentxchain.json. Overwrite?`,
|
|
145
|
+
default: false
|
|
146
|
+
}]);
|
|
147
|
+
if (!overwrite) {
|
|
148
|
+
console.log(chalk.yellow(' Aborted.'));
|
|
149
|
+
return;
|
|
100
150
|
}
|
|
101
151
|
}
|
|
102
|
-
} else {
|
|
103
|
-
mkdirSync(dir, { recursive: true });
|
|
104
152
|
}
|
|
105
153
|
|
|
154
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
155
|
+
|
|
106
156
|
const config = {
|
|
107
157
|
version: 3,
|
|
108
158
|
project,
|
|
109
159
|
agents,
|
|
110
160
|
log: 'log.md',
|
|
161
|
+
state_file: 'state.md',
|
|
162
|
+
history_file: 'history.jsonl',
|
|
111
163
|
rules: {
|
|
112
|
-
max_consecutive_claims: 2,
|
|
113
|
-
require_message:
|
|
114
|
-
compress_after_words: 5000
|
|
164
|
+
max_consecutive_claims: rules.max_consecutive_claims || 2,
|
|
165
|
+
require_message: rules.require_message !== false,
|
|
166
|
+
compress_after_words: rules.compress_after_words || 5000,
|
|
167
|
+
ttl_minutes: rules.ttl_minutes || 10,
|
|
168
|
+
watch_interval_ms: rules.watch_interval_ms || 5000,
|
|
169
|
+
...(rules.verify_command ? { verify_command: rules.verify_command } : {})
|
|
115
170
|
}
|
|
116
171
|
};
|
|
117
172
|
|
|
118
|
-
const lock = {
|
|
119
|
-
|
|
120
|
-
last_released_by: null,
|
|
121
|
-
turn_number: 0,
|
|
122
|
-
claimed_at: null
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const state = {
|
|
126
|
-
phase: 'discovery',
|
|
127
|
-
blocked: false,
|
|
128
|
-
blocked_on: null,
|
|
129
|
-
project
|
|
130
|
-
};
|
|
173
|
+
const lock = { holder: null, last_released_by: null, turn_number: 0, claimed_at: null };
|
|
174
|
+
const state = { phase: 'discovery', blocked: false, blocked_on: null, project };
|
|
131
175
|
|
|
132
176
|
writeFileSync(join(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + '\n');
|
|
133
177
|
writeFileSync(join(dir, LOCK_FILE), JSON.stringify(lock, null, 2) + '\n');
|
|
134
|
-
writeFileSync(join(dir,
|
|
135
|
-
writeFileSync(join(dir,
|
|
178
|
+
writeFileSync(join(dir, 'state.json'), JSON.stringify(state, null, 2) + '\n');
|
|
179
|
+
writeFileSync(join(dir, 'state.md'), `# ${project} — Current State\n\n## Architecture\n\n(Agents update this each turn with current decisions.)\n\n## Active Work\n\n(What's in progress right now.)\n\n## Open Issues\n\n(Bugs, blockers, risks.)\n\n## Next Steps\n\n(What should happen next.)\n`);
|
|
180
|
+
writeFileSync(join(dir, 'history.jsonl'), '');
|
|
181
|
+
writeFileSync(join(dir, 'log.md'), `# ${project} — Agent Log\n\n## COMPRESSED CONTEXT\n\n(No compressed context yet.)\n\n## MESSAGE LOG\n\n(Agents append messages below this line.)\n`);
|
|
136
182
|
writeFileSync(join(dir, 'HUMAN_TASKS.md'), '# Human Tasks\n\n(Agents append tasks here when they need human action.)\n');
|
|
137
183
|
|
|
184
|
+
const agentCount = Object.keys(agents).length;
|
|
138
185
|
console.log('');
|
|
139
186
|
console.log(chalk.green(` ✓ Created ${chalk.bold(folderName)}/`));
|
|
140
187
|
console.log('');
|
|
141
|
-
console.log(` ${chalk.dim('├──')} ${
|
|
142
|
-
console.log(` ${chalk.dim('├──')}
|
|
143
|
-
console.log(` ${chalk.dim('├──')}
|
|
144
|
-
console.log(` ${chalk.dim('├──')}
|
|
188
|
+
console.log(` ${chalk.dim('├──')} agentxchain.json ${chalk.dim(`(${agentCount} agents)`)}`);
|
|
189
|
+
console.log(` ${chalk.dim('├──')} lock.json`);
|
|
190
|
+
console.log(` ${chalk.dim('├──')} state.json`);
|
|
191
|
+
console.log(` ${chalk.dim('├──')} state.md`);
|
|
192
|
+
console.log(` ${chalk.dim('├──')} history.jsonl`);
|
|
193
|
+
console.log(` ${chalk.dim('├──')} log.md`);
|
|
145
194
|
console.log(` ${chalk.dim('└──')} HUMAN_TASKS.md`);
|
|
146
195
|
console.log('');
|
|
147
196
|
console.log(` ${chalk.dim('Agents:')} ${Object.keys(agents).join(', ')}`);
|
|
148
197
|
console.log('');
|
|
149
|
-
console.log(` ${chalk.cyan('Next:')}
|
|
198
|
+
console.log(` ${chalk.cyan('Next:')}`);
|
|
199
|
+
console.log(` ${chalk.bold(`cd ${folderName}`)}`);
|
|
200
|
+
console.log(` ${chalk.bold('agentxchain watch')} ${chalk.dim('# start the referee')}`);
|
|
201
|
+
console.log(` ${chalk.bold('agentxchain start')} ${chalk.dim('# launch agents in your IDE')}`);
|
|
150
202
|
console.log('');
|
|
151
203
|
}
|
package/src/commands/status.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { loadConfig, loadLock, loadState } from '../lib/config.js';
|
|
3
|
+
import { getAgentStatus, loadSession } from '../adapters/cursor.js';
|
|
3
4
|
|
|
4
5
|
export async function statusCommand(opts) {
|
|
5
6
|
const result = loadConfig();
|
|
@@ -19,10 +20,9 @@ export async function statusCommand(opts) {
|
|
|
19
20
|
|
|
20
21
|
console.log('');
|
|
21
22
|
console.log(chalk.bold(' AgentXchain Status'));
|
|
22
|
-
console.log(chalk.dim(' ' + '─'.repeat(
|
|
23
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
23
24
|
console.log('');
|
|
24
25
|
|
|
25
|
-
// Project
|
|
26
26
|
console.log(` ${chalk.dim('Project:')} ${config.project}`);
|
|
27
27
|
console.log(` ${chalk.dim('Phase:')} ${state ? formatPhase(state.phase) : chalk.dim('unknown')}`);
|
|
28
28
|
if (state?.blocked) {
|
|
@@ -32,45 +32,83 @@ export async function statusCommand(opts) {
|
|
|
32
32
|
|
|
33
33
|
// Lock
|
|
34
34
|
if (lock) {
|
|
35
|
-
if (lock.holder) {
|
|
35
|
+
if (lock.holder === 'human') {
|
|
36
|
+
console.log(` ${chalk.dim('Lock:')} ${chalk.magenta('HUMAN')} — you hold the lock`);
|
|
37
|
+
} else if (lock.holder) {
|
|
36
38
|
const agentName = config.agents[lock.holder]?.name || lock.holder;
|
|
37
39
|
console.log(` ${chalk.dim('Lock:')} ${chalk.yellow('CLAIMED')} by ${chalk.bold(lock.holder)} (${agentName})`);
|
|
38
40
|
if (lock.claimed_at) {
|
|
39
|
-
|
|
40
|
-
console.log(` ${chalk.dim('Claimed:')} ${ago} ago`);
|
|
41
|
+
console.log(` ${chalk.dim('Claimed:')} ${timeSince(lock.claimed_at)} ago`);
|
|
41
42
|
}
|
|
42
43
|
} else {
|
|
43
44
|
console.log(` ${chalk.dim('Lock:')} ${chalk.green('FREE')} — any agent can claim`);
|
|
44
45
|
}
|
|
45
46
|
console.log(` ${chalk.dim('Turn:')} ${lock.turn_number}`);
|
|
46
47
|
if (lock.last_released_by) {
|
|
47
|
-
|
|
48
|
-
console.log(` ${chalk.dim('Last:')} ${lock.last_released_by} (${lastName})`);
|
|
48
|
+
console.log(` ${chalk.dim('Last:')} ${lock.last_released_by}`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
console.log('');
|
|
52
52
|
|
|
53
|
+
// Cursor session info
|
|
54
|
+
const session = loadSession(root);
|
|
55
|
+
const apiKey = process.env.CURSOR_API_KEY;
|
|
56
|
+
const hasCursor = session?.ide === 'cursor' && session?.launched?.length > 0;
|
|
57
|
+
|
|
58
|
+
if (hasCursor) {
|
|
59
|
+
console.log(` ${chalk.dim('Cursor:')} ${chalk.cyan('Active session')} (${session.launched.length} agents)`);
|
|
60
|
+
console.log(` ${chalk.dim('Started:')} ${session.started_at}`);
|
|
61
|
+
if (session.repo) console.log(` ${chalk.dim('Repo:')} ${session.repo}`);
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
// Agents
|
|
54
66
|
console.log(` ${chalk.dim('Agents:')} ${Object.keys(config.agents).length}`);
|
|
67
|
+
|
|
55
68
|
for (const [id, agent] of Object.entries(config.agents)) {
|
|
56
69
|
const isHolder = lock?.holder === id;
|
|
57
70
|
const marker = isHolder ? chalk.yellow('●') : chalk.dim('○');
|
|
58
71
|
const label = isHolder ? chalk.bold(id) : id;
|
|
59
|
-
|
|
72
|
+
|
|
73
|
+
let cursorStatus = '';
|
|
74
|
+
if (hasCursor && apiKey) {
|
|
75
|
+
const cloudAgent = session.launched.find(a => a.id === id);
|
|
76
|
+
if (cloudAgent) {
|
|
77
|
+
try {
|
|
78
|
+
const statusData = await getAgentStatus(apiKey, cloudAgent.cloudId);
|
|
79
|
+
if (statusData?.status) {
|
|
80
|
+
cursorStatus = ` ${formatCursorStatus(statusData.status)}`;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
cursorStatus = chalk.dim(' [API error]');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(` ${marker} ${label} — ${agent.name}${cursorStatus}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (lock?.holder === 'human') {
|
|
92
|
+
console.log(` ${chalk.magenta('●')} ${chalk.bold('human')} — You`);
|
|
60
93
|
}
|
|
94
|
+
|
|
61
95
|
console.log('');
|
|
62
96
|
}
|
|
63
97
|
|
|
64
98
|
function formatPhase(phase) {
|
|
65
|
-
const colors = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
99
|
+
const colors = { discovery: chalk.blue, build: chalk.green, qa: chalk.yellow, deploy: chalk.magenta, blocked: chalk.red };
|
|
100
|
+
return (colors[phase] || chalk.white)(phase);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function formatCursorStatus(status) {
|
|
104
|
+
const map = {
|
|
105
|
+
CREATING: chalk.dim('[creating]'),
|
|
106
|
+
RUNNING: chalk.cyan('[running]'),
|
|
107
|
+
FINISHED: chalk.green('[finished]'),
|
|
108
|
+
STOPPED: chalk.yellow('[stopped]'),
|
|
109
|
+
ERRORED: chalk.red('[errored]'),
|
|
71
110
|
};
|
|
72
|
-
|
|
73
|
-
return fn(phase);
|
|
111
|
+
return map[status] || chalk.dim(`[${status}]`);
|
|
74
112
|
}
|
|
75
113
|
|
|
76
114
|
function timeSince(iso) {
|
package/src/commands/stop.js
CHANGED
|
@@ -2,85 +2,69 @@ import { readFileSync, existsSync, unlinkSync } from 'fs';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { loadConfig } from '../lib/config.js';
|
|
5
|
+
import { deleteAgent, stopAgent, loadSession } from '../adapters/cursor.js';
|
|
5
6
|
|
|
6
7
|
const SESSION_FILE = '.agentxchain-session.json';
|
|
7
8
|
|
|
8
9
|
export async function stopCommand() {
|
|
9
10
|
const result = loadConfig();
|
|
10
|
-
if (!result) {
|
|
11
|
-
console.log(chalk.red('No agentxchain.json found.'));
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
11
|
+
if (!result) { console.log(chalk.red(' No agentxchain.json found.')); process.exit(1); }
|
|
14
12
|
|
|
15
13
|
const { root } = result;
|
|
16
|
-
const
|
|
14
|
+
const session = loadSession(root);
|
|
17
15
|
|
|
18
|
-
if (!
|
|
19
|
-
console.log(chalk.yellow(' No active session found
|
|
20
|
-
console.log(chalk.dim(' If agents are running, stop them manually
|
|
16
|
+
if (!session) {
|
|
17
|
+
console.log(chalk.yellow(' No active session found.'));
|
|
18
|
+
console.log(chalk.dim(' If agents are running, stop them manually.'));
|
|
21
19
|
return;
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
const session = JSON.parse(readFileSync(sessionPath, 'utf8'));
|
|
25
22
|
console.log('');
|
|
26
23
|
console.log(chalk.bold(` Stopping ${session.launched.length} agents (${session.ide})`));
|
|
27
24
|
console.log('');
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function stopCursorAgents(session) {
|
|
48
|
-
const apiKey = process.env.CURSOR_API_KEY;
|
|
49
|
-
|
|
50
|
-
for (const agent of session.launched) {
|
|
51
|
-
if (agent.cloudId && apiKey) {
|
|
52
|
-
try {
|
|
53
|
-
const res = await fetch(`https://api.cursor.com/v0/agents/${agent.cloudId}`, {
|
|
54
|
-
method: 'DELETE',
|
|
55
|
-
headers: { 'Authorization': `Basic ${btoa(apiKey + ':')}` }
|
|
56
|
-
});
|
|
57
|
-
if (res.ok) {
|
|
58
|
-
console.log(chalk.green(` ✓ Stopped ${agent.id} (cloud ID: ${agent.cloudId})`));
|
|
59
|
-
} else {
|
|
60
|
-
console.log(chalk.yellow(` ⚠ Could not stop ${agent.id}: ${res.status}`));
|
|
26
|
+
if (session.ide === 'cursor') {
|
|
27
|
+
const apiKey = process.env.CURSOR_API_KEY;
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
console.log(chalk.yellow(' CURSOR_API_KEY not set. Cannot stop agents via API.'));
|
|
30
|
+
console.log(chalk.dim(' Stop them manually at cursor.com/agents'));
|
|
31
|
+
} else {
|
|
32
|
+
for (const agent of session.launched) {
|
|
33
|
+
try {
|
|
34
|
+
const deleted = await deleteAgent(apiKey, agent.cloudId);
|
|
35
|
+
if (deleted) {
|
|
36
|
+
console.log(chalk.green(` ✓ Deleted ${chalk.bold(agent.id)} (${agent.cloudId})`));
|
|
37
|
+
} else {
|
|
38
|
+
console.log(chalk.yellow(` ⚠ Could not delete ${agent.id} — may already be gone`));
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.log(chalk.red(` ✗ ${agent.id}: ${err.message}`));
|
|
61
42
|
}
|
|
62
|
-
} catch (err) {
|
|
63
|
-
console.log(chalk.red(` ✗ Error stopping ${agent.id}: ${err.message}`));
|
|
64
43
|
}
|
|
65
|
-
} else {
|
|
66
|
-
console.log(chalk.dim(` ${agent.id} — no cloud ID or API key; stop manually in Cursor.`));
|
|
67
44
|
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
} else {
|
|
81
|
-
console.log(chalk.red(` ✗ Error stopping ${agent.id}: ${err.message}`));
|
|
45
|
+
} else if (session.ide === 'claude-code') {
|
|
46
|
+
for (const agent of session.launched) {
|
|
47
|
+
if (agent.pid) {
|
|
48
|
+
try {
|
|
49
|
+
process.kill(agent.pid, 'SIGTERM');
|
|
50
|
+
console.log(chalk.green(` ✓ Sent SIGTERM to ${agent.id} (PID: ${agent.pid})`));
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err.code === 'ESRCH') {
|
|
53
|
+
console.log(chalk.dim(` ${agent.id} (PID: ${agent.pid}) — already stopped`));
|
|
54
|
+
} else {
|
|
55
|
+
console.log(chalk.red(` ✗ ${agent.id}: ${err.message}`));
|
|
56
|
+
}
|
|
82
57
|
}
|
|
83
58
|
}
|
|
84
59
|
}
|
|
85
60
|
}
|
|
61
|
+
|
|
62
|
+
// Remove session file
|
|
63
|
+
const sessionPath = join(root, SESSION_FILE);
|
|
64
|
+
if (existsSync(sessionPath)) unlinkSync(sessionPath);
|
|
65
|
+
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log(chalk.dim(' Session file removed.'));
|
|
68
|
+
console.log(chalk.green(' All agents stopped.'));
|
|
69
|
+
console.log('');
|
|
86
70
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
export async function updateCommand() {
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
|
|
10
|
+
const currentVersion = pkg.version;
|
|
11
|
+
|
|
12
|
+
console.log('');
|
|
13
|
+
console.log(` ${chalk.dim('Current version:')} ${currentVersion}`);
|
|
14
|
+
console.log(` ${chalk.dim('Checking npm for latest...')}`);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const latest = execSync('npm view agentxchain version', { encoding: 'utf8' }).trim();
|
|
18
|
+
|
|
19
|
+
if (latest === currentVersion) {
|
|
20
|
+
console.log(chalk.green(` ✓ Already on the latest version (${currentVersion}).`));
|
|
21
|
+
console.log('');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(` ${chalk.dim('Latest version:')} ${chalk.cyan(latest)}`);
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(` Updating...`);
|
|
28
|
+
|
|
29
|
+
execSync('npm install -g agentxchain@latest', { stdio: 'inherit' });
|
|
30
|
+
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(chalk.green(` ✓ Updated to ${latest}`));
|
|
33
|
+
console.log('');
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log(chalk.yellow(' Could not auto-update. Run manually:'));
|
|
37
|
+
console.log(` ${chalk.bold('npm install -g agentxchain@latest')}`);
|
|
38
|
+
console.log('');
|
|
39
|
+
console.log(chalk.dim(` Error: ${err.message}`));
|
|
40
|
+
console.log('');
|
|
41
|
+
}
|
|
42
|
+
}
|