agentxchain 0.7.2 → 0.8.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.
@@ -39,7 +39,7 @@ program
39
39
  program
40
40
  .command('start')
41
41
  .description('Launch agents in your IDE')
42
- .option('--ide <ide>', 'Target IDE: vscode, claude-code', 'vscode')
42
+ .option('--ide <ide>', 'Target IDE: cursor, vscode, claude-code', 'cursor')
43
43
  .option('--agent <id>', 'Launch a specific agent only')
44
44
  .option('--dry-run', 'Print what would be launched without doing it')
45
45
  .action(startCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "CLI for AgentXchain — multi-agent coordination in your IDE",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,95 @@
1
+ import { execSync } from 'child_process';
2
+ import { writeFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import chalk from 'chalk';
5
+ import inquirer from 'inquirer';
6
+ import { generatePollingPrompt } from '../lib/seed-prompt-polling.js';
7
+
8
+ export async function launchCursorLocal(config, root, opts) {
9
+ const agents = filterAgents(config, opts.agent);
10
+ const agentEntries = Object.entries(agents);
11
+ const total = agentEntries.length;
12
+
13
+ console.log(chalk.bold(` Opening ${total} Cursor windows for: ${Object.keys(agents).join(', ')}`));
14
+ console.log('');
15
+
16
+ const promptDir = join(root, '.agentxchain-prompts');
17
+ try { execSync(`mkdir -p "${promptDir}"`); } catch {}
18
+
19
+ for (let i = 0; i < agentEntries.length; i++) {
20
+ const [id, agent] = agentEntries[i];
21
+ const prompt = generatePollingPrompt(id, agent, config);
22
+ const promptFile = join(promptDir, `${id}.prompt.md`);
23
+ writeFileSync(promptFile, prompt);
24
+
25
+ console.log(` ${chalk.cyan(`Window ${i + 1}/${total}:`)} ${chalk.bold(id)} — ${agent.name}`);
26
+
27
+ copyToClipboard(prompt);
28
+ console.log(chalk.green(` Prompt copied to clipboard.`));
29
+ console.log(chalk.dim(` Also saved to: .agentxchain-prompts/${id}.prompt.md`));
30
+
31
+ tryOpenCursor(root);
32
+
33
+ if (i < agentEntries.length - 1) {
34
+ console.log('');
35
+ await inquirer.prompt([{
36
+ type: 'input',
37
+ name: 'ready',
38
+ message: chalk.dim(` Paste prompt in Cursor chat (Cmd+V), send it, then press Enter here for the next agent...`)
39
+ }]);
40
+ } else {
41
+ console.log('');
42
+ console.log(chalk.dim(` Paste this last prompt in Cursor chat (Cmd+V) and send it.`));
43
+ }
44
+ }
45
+
46
+ console.log('');
47
+ console.log(chalk.green(' All agent prompts ready.'));
48
+ console.log('');
49
+ console.log(` ${chalk.cyan('Next:')}`);
50
+ console.log(` ${chalk.bold('agentxchain release')} ${chalk.dim('# release human lock — agents start claiming turns')}`);
51
+ console.log(` ${chalk.bold('agentxchain watch')} ${chalk.dim('# optional: TTL safety net + status logging')}`);
52
+ console.log(` ${chalk.bold('agentxchain status')} ${chalk.dim('# check who holds the lock')}`);
53
+ console.log(` ${chalk.bold('agentxchain claim')} ${chalk.dim('# pause agents and take control')}`);
54
+ console.log('');
55
+ console.log(chalk.dim(' Agents self-coordinate via lock.json polling (sleep 60s between checks).'));
56
+ console.log(chalk.dim(' Prompts saved in .agentxchain-prompts/ if you need to re-paste.'));
57
+ console.log('');
58
+ }
59
+
60
+ function filterAgents(config, specificId) {
61
+ if (specificId) {
62
+ if (!config.agents[specificId]) {
63
+ console.log(chalk.red(` Agent "${specificId}" not found in agentxchain.json`));
64
+ process.exit(1);
65
+ }
66
+ return { [specificId]: config.agents[specificId] };
67
+ }
68
+ return config.agents;
69
+ }
70
+
71
+ function copyToClipboard(text) {
72
+ try {
73
+ if (process.platform === 'darwin') {
74
+ execSync('pbcopy', { input: text, encoding: 'utf8' });
75
+ return true;
76
+ }
77
+ if (process.platform === 'linux') {
78
+ execSync('xclip -selection clipboard', { input: text, encoding: 'utf8' });
79
+ return true;
80
+ }
81
+ } catch {}
82
+ return false;
83
+ }
84
+
85
+ function tryOpenCursor(root) {
86
+ try {
87
+ if (process.platform === 'darwin') {
88
+ execSync(`open -na "Cursor" --args "${root}"`, { stdio: 'ignore' });
89
+ return;
90
+ }
91
+ execSync(`cursor "${root}"`, { stdio: 'ignore' });
92
+ } catch {
93
+ // Cursor not found or can't open — user will open manually
94
+ }
95
+ }
@@ -207,7 +207,7 @@ export async function initCommand(opts) {
207
207
  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`);
208
208
  writeFileSync(join(dir, 'HUMAN_TASKS.md'), '# Human Tasks\n\n(Agents append tasks here when they need human action.)\n');
209
209
  const gitignorePath = join(dir, '.gitignore');
210
- const requiredIgnores = ['.env', '.agentxchain-trigger.json'];
210
+ const requiredIgnores = ['.env', '.agentxchain-trigger.json', '.agentxchain-prompts/'];
211
211
  if (!existsSync(gitignorePath)) {
212
212
  writeFileSync(gitignorePath, requiredIgnores.join('\n') + '\n');
213
213
  } else {
@@ -63,13 +63,18 @@ export async function startCommand(opts) {
63
63
  console.log('');
64
64
  break;
65
65
  }
66
+ case 'cursor': {
67
+ const { launchCursorLocal } = await import('../adapters/cursor-local.js');
68
+ await launchCursorLocal(config, root, opts);
69
+ break;
70
+ }
66
71
  case 'claude-code': {
67
72
  const { launchClaudeCodeAgents } = await import('../adapters/claude-code.js');
68
73
  await launchClaudeCodeAgents(config, root, opts);
69
74
  break;
70
75
  }
71
76
  default:
72
- console.log(chalk.red(` Unknown IDE: ${ide}. Supported: vscode, claude-code`));
77
+ console.log(chalk.red(` Unknown IDE: ${ide}. Supported: vscode, cursor, claude-code`));
73
78
  process.exit(1);
74
79
  }
75
80
  }
@@ -0,0 +1,128 @@
1
+ export function generatePollingPrompt(agentId, agentDef, config) {
2
+ const logFile = config.log || 'log.md';
3
+ const maxClaims = config.rules?.max_consecutive_claims || 2;
4
+ const verifyCmd = config.rules?.verify_command || null;
5
+ const stateFile = config.state_file || 'state.md';
6
+ const historyFile = config.history_file || 'history.jsonl';
7
+ const useSplit = config.state_file || config.history_file;
8
+
9
+ const agentIds = Object.keys(config.agents);
10
+ const myIndex = agentIds.indexOf(agentId);
11
+ const prevAgent = myIndex === 0 ? null : agentIds[myIndex - 1];
12
+ const isFirstAgent = myIndex === 0;
13
+
14
+ const turnCondition = isFirstAgent
15
+ ? `It is YOUR turn when lock.json shows holder=null AND (last_released_by is null, "human", "system", OR the LAST agent in the rotation: "${agentIds[agentIds.length - 1]}")`
16
+ : `It is YOUR turn when lock.json shows holder=null AND last_released_by="${prevAgent}"`;
17
+
18
+ const readSection = useSplit
19
+ ? `READ THESE FILES:
20
+ - "${stateFile}" — the living project state. Read fully. Primary context.
21
+ - "${historyFile}" — turn history. Read last 3 lines for recent context.
22
+ - lock.json — who holds the lock.
23
+ - state.json — phase and blocked status.`
24
+ : `READ THESE FILES:
25
+ - "${logFile}" — the message log. Read last few messages.
26
+ - lock.json — who holds the lock.
27
+ - state.json — phase and blocked status.`;
28
+
29
+ const writeSection = useSplit
30
+ ? `WRITE (in this order):
31
+ a. Do your actual work: write code, create files, run commands, make decisions.
32
+ b. Update "${stateFile}" — OVERWRITE with current project state.
33
+ c. Append ONE line to "${historyFile}":
34
+ {"turn": N, "agent": "${agentId}", "summary": "what you did", "files_changed": [...], "verify_result": "pass|fail|skipped", "timestamp": "ISO8601"}
35
+ d. Update state.json if phase or blocked status changed.`
36
+ : `WRITE (in this order):
37
+ a. Do your actual work: write code, create files, run commands, make decisions.
38
+ b. Append ONE message to ${logFile}:
39
+ ---
40
+ ### [${agentId}] (${agentDef.name}) | Turn N
41
+ **Status:** Current project state.
42
+ **Decision:** What you decided and why.
43
+ **Action:** What you did. Commands, files, results.
44
+ **Next:** What the next agent should focus on.
45
+ c. Update state.json if phase or blocked status changed.`;
46
+
47
+ const verifySection = verifyCmd
48
+ ? `
49
+ VERIFY (mandatory before release):
50
+ Run: ${verifyCmd}
51
+ If it FAILS: fix the problem. Run again. Do NOT release with failing verification.
52
+ If it PASSES: report the result. Then release.`
53
+ : '';
54
+
55
+ return `You are "${agentId}" — ${agentDef.name}.
56
+
57
+ ${agentDef.mandate}
58
+
59
+ ---
60
+
61
+ PROJECT DOCUMENTATION (.planning/ folder):
62
+
63
+ These files give you project context. Read the ones relevant to your role.
64
+
65
+ - .planning/PROJECT.md — Vision, constraints, stack decisions. PM writes this.
66
+ - .planning/REQUIREMENTS.md — Scoped requirements with acceptance criteria. PM writes this.
67
+ - .planning/ROADMAP.md — Phased delivery plan. PM maintains this.
68
+ - .planning/research/ — Domain research, prior art, technical investigation.
69
+ - .planning/phases/ — Per-phase plans (PLAN.md), reviews (REVIEW.md), test results (TESTS.md), bugs (BUGS.md).
70
+ - .planning/qa/TEST-COVERAGE.md — Which features are tested and how. QA maintains this.
71
+ - .planning/qa/BUGS.md — Open and fixed bugs with reproduction steps. QA maintains this.
72
+ - .planning/qa/UX-AUDIT.md — UX checklist and visual/usability issues. QA maintains this.
73
+ - .planning/qa/ACCEPTANCE-MATRIX.md — Requirements mapped to test status. QA maintains this.
74
+ - .planning/qa/REGRESSION-LOG.md — Fixed bugs and their regression tests.
75
+
76
+ When your role requires it, CREATE or UPDATE these files.
77
+
78
+ ---
79
+
80
+ TEAM ROTATION: ${agentIds.join(' → ')} → (repeat)
81
+ YOUR POSITION: ${agentId} (index ${myIndex} of ${agentIds.length})
82
+ ${turnCondition}
83
+
84
+ ---
85
+
86
+ YOUR LOOP (run forever, never exit, never say "I'm done"):
87
+
88
+ 1. READ lock.json.
89
+
90
+ 2. CHECK — is it my turn?
91
+ - If holder is NOT null → someone else is working. Run the shell command: sleep 60
92
+ Then go back to step 1.
93
+ - If holder IS null → check last_released_by:
94
+ ${isFirstAgent
95
+ ? `- If last_released_by is null, "human", starts with "system", or "${agentIds[agentIds.length - 1]}" → IT IS YOUR TURN. Go to step 3.`
96
+ : `- If last_released_by is "${prevAgent}" → IT IS YOUR TURN. Go to step 3.`}
97
+ - Otherwise → it is another agent's turn. Run the shell command: sleep 60
98
+ Then go back to step 1.
99
+
100
+ 3. CLAIM the lock:
101
+ Write lock.json: {"holder":"${agentId}","last_released_by":<keep previous>,"turn_number":<keep previous>,"claimed_at":"<current ISO timestamp>"}
102
+ Then RE-READ lock.json immediately. If holder is not "${agentId}", someone else won. Go to step 1.
103
+
104
+ 4. DO YOUR WORK:
105
+ ${readSection}
106
+
107
+ THINK: What did the previous agent do? What is most important for YOUR role? What is one risk?
108
+
109
+ ${writeSection}${verifySection}
110
+
111
+ 5. RELEASE the lock:
112
+ Write lock.json: {"holder":null,"last_released_by":"${agentId}","turn_number":<previous + 1>,"claimed_at":null}
113
+ THIS MUST BE THE LAST FILE YOU WRITE.
114
+
115
+ 6. Run the shell command: sleep 60
116
+ Then go back to step 1.
117
+
118
+ ---
119
+
120
+ CRITICAL RULES:
121
+ - ACTUALLY RUN "sleep 60" in the terminal between checks. Do NOT skip this. Do NOT just say "waiting."
122
+ - Never write files or code without holding the lock. Reading is always allowed.
123
+ - One git commit per turn: "Turn N - ${agentId} - description"
124
+ - Max ${maxClaims} consecutive turns. If you have held the lock ${maxClaims} times in a row, do a short turn and release.
125
+ - ALWAYS release the lock. A stuck lock blocks the entire team.
126
+ - ALWAYS find at least one problem, risk, or question about the previous work. Blind agreement is forbidden.
127
+ - NEVER exit or stop. After releasing, always sleep and poll again. You are a persistent agent.`;
128
+ }