agentxchain 0.8.6 → 0.8.8
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 +36 -38
- package/bin/agentxchain.js +62 -3
- package/package.json +3 -2
- package/scripts/agentxchain-autonudge.applescript +49 -10
- package/scripts/run-autonudge.sh +1 -1
- package/src/adapters/claude-code.js +7 -14
- package/src/adapters/cursor-local.js +26 -29
- package/src/commands/branch.js +2 -2
- package/src/commands/claim.js +86 -15
- package/src/commands/config.js +16 -0
- package/src/commands/doctor.js +9 -1
- package/src/commands/init.js +24 -5
- package/src/commands/rebind.js +77 -0
- package/src/commands/stop.js +65 -33
- package/src/commands/update.js +24 -3
- package/src/commands/watch.js +115 -34
- package/src/lib/config.js +47 -12
- package/src/lib/filter-agents.js +12 -0
- package/src/lib/generate-vscode.js +158 -51
- package/src/lib/next-owner.js +116 -0
- package/src/lib/notify.js +14 -12
- package/src/lib/prompt-core.js +108 -0
- package/src/lib/safe-write.js +44 -0
- package/src/lib/schema.js +68 -0
- package/src/lib/seed-prompt-polling.js +21 -83
- package/src/lib/seed-prompt.js +17 -63
- package/src/lib/validation.js +30 -19
- package/src/lib/verify-command.js +72 -0
package/README.md
CHANGED
|
@@ -16,12 +16,21 @@ npx agentxchain init
|
|
|
16
16
|
|
|
17
17
|
## Quick start
|
|
18
18
|
|
|
19
|
+
### Happy path: net-new project
|
|
20
|
+
|
|
19
21
|
```bash
|
|
20
|
-
|
|
21
|
-
agentxchain init
|
|
22
|
+
npx agentxchain init
|
|
23
|
+
cd my-agentxchain-project # default with init -y, or your chosen folder name
|
|
24
|
+
agentxchain kickoff
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Happy path: existing project
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
Run these commands from inside your existing project folder:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
agentxchain doctor
|
|
33
|
+
agentxchain generate
|
|
25
34
|
agentxchain kickoff
|
|
26
35
|
```
|
|
27
36
|
|
|
@@ -43,9 +52,11 @@ Agents are now required to maintain `TALK.md` as the human-readable handoff log
|
|
|
43
52
|
| `doctor` | Validate local setup (tools, trigger flow, accessibility checks) |
|
|
44
53
|
| `claim` | Human takes control (agents stop claiming) |
|
|
45
54
|
| `release` | Hand lock back to agents |
|
|
46
|
-
| `stop` |
|
|
47
|
-
| `
|
|
55
|
+
| `stop` | Stop watch daemon, end Claude Code sessions; Cursor/VS Code chats close manually |
|
|
56
|
+
| `branch` | Show/set Cursor branch override for launches |
|
|
57
|
+
| `watch` | Referee loop: validates turns, writes next trigger, and force-releases stale locks |
|
|
48
58
|
| `config` | View/edit config, add/remove agents, change rules |
|
|
59
|
+
| `rebind` | Rebuild Cursor workspace/prompt bindings for agents |
|
|
49
60
|
| `update` | Self-update CLI from npm |
|
|
50
61
|
|
|
51
62
|
### Full command list
|
|
@@ -56,7 +67,9 @@ agentxchain status
|
|
|
56
67
|
agentxchain start
|
|
57
68
|
agentxchain kickoff
|
|
58
69
|
agentxchain stop
|
|
70
|
+
agentxchain branch
|
|
59
71
|
agentxchain config
|
|
72
|
+
agentxchain rebind
|
|
60
73
|
agentxchain generate
|
|
61
74
|
agentxchain watch
|
|
62
75
|
agentxchain supervise
|
|
@@ -94,40 +107,28 @@ agentxchain watch --daemon # run watch in background
|
|
|
94
107
|
agentxchain supervise --autonudge # run watch + AppleScript nudge loop
|
|
95
108
|
agentxchain supervise --autonudge --send # auto-press Enter after paste
|
|
96
109
|
agentxchain supervise --interval 2 # set auto-nudge poll interval
|
|
110
|
+
agentxchain rebind # regenerate agent prompt/workspace bindings
|
|
111
|
+
agentxchain rebind --open # regenerate and reopen all Cursor agent windows
|
|
112
|
+
agentxchain rebind --agent pm # regenerate one agent binding only
|
|
97
113
|
agentxchain claim --agent pm # guarded claim as agent turn owner
|
|
98
114
|
agentxchain release --agent pm # guarded release as agent turn owner
|
|
99
115
|
agentxchain release --force # force-release non-human holder lock
|
|
116
|
+
agentxchain config --set "rules.strict_next_owner true" # TALK-only next owner (no cyclic fallback)
|
|
100
117
|
```
|
|
101
118
|
|
|
102
119
|
## macOS auto-nudge (AppleScript)
|
|
103
120
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
1) Keep watcher running in your project:
|
|
121
|
+
**Recommended:** `agentxchain supervise --autonudge` (starts `watch` + auto-nudge together). Requires macOS, `jq`, and Accessibility for Terminal + Cursor.
|
|
107
122
|
|
|
108
123
|
```bash
|
|
109
|
-
agentxchain watch
|
|
110
|
-
# or use the combined command:
|
|
111
124
|
agentxchain supervise --autonudge
|
|
125
|
+
agentxchain supervise --autonudge --send # paste + Enter
|
|
112
126
|
```
|
|
113
127
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
bash scripts/run-autonudge.sh --project "/absolute/path/to/your-project"
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
By default this is **paste-only** (safe mode): it opens chat and pastes the nudge message, but does not press Enter.
|
|
121
|
-
|
|
122
|
-
3) Enable auto-send once confirmed:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
bash scripts/run-autonudge.sh --project "/absolute/path/to/your-project" --send
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Stop it anytime:
|
|
128
|
+
**Advanced (debugging):** from a checkout of `cli/`, run the script alone while `watch` is already running:
|
|
129
129
|
|
|
130
130
|
```bash
|
|
131
|
+
bash scripts/run-autonudge.sh --project "/absolute/path/to/your-project" [--send]
|
|
131
132
|
bash scripts/stop-autonudge.sh
|
|
132
133
|
```
|
|
133
134
|
|
|
@@ -146,22 +147,21 @@ Notes:
|
|
|
146
147
|
2. Each window gets a unique prompt copied to clipboard
|
|
147
148
|
3. Kickoff validates PM signoff and launches remaining agents
|
|
148
149
|
4. Agent prompts are single-turn: claim → work → validate → release → stop
|
|
149
|
-
5. Agents
|
|
150
|
+
5. Agents use the latest `Next owner:` in `TALK.md` to pick who goes next (fallback: config order)
|
|
150
151
|
6. Human can `claim` to pause and `release` to resume anytime
|
|
151
152
|
|
|
152
153
|
### VS Code mode
|
|
153
154
|
|
|
154
155
|
1. `agentxchain init` generates `.github/agents/*.agent.md` (VS Code custom agents) and `.github/hooks/` (lifecycle hooks)
|
|
155
|
-
2. VS Code
|
|
156
|
+
2. VS Code discovers custom agents in Chat when using **GitHub Copilot** agents (see Microsoft docs)
|
|
156
157
|
3. The `Stop` hook acts as referee — hands off to next agent automatically
|
|
157
158
|
|
|
158
|
-
### Turn
|
|
159
|
+
### Turn ownership
|
|
159
160
|
|
|
160
|
-
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
-
-
|
|
164
|
-
- And so on...
|
|
161
|
+
Agent turns are handoff-driven:
|
|
162
|
+
- Each turn appends a `Next owner:` in `TALK.md` with a valid agent id
|
|
163
|
+
- `watch`/`supervise` dispatches the next trigger from that handoff
|
|
164
|
+
- `claim --agent <id>` enforces that expected owner (with guarded fallback)
|
|
165
165
|
|
|
166
166
|
## Key features
|
|
167
167
|
|
|
@@ -176,14 +176,12 @@ Agents follow a round-robin order defined in `agentxchain.json`:
|
|
|
176
176
|
|
|
177
177
|
## VS Code extension (optional)
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
The VSIX is not committed to the repo. Build/package from `cli/vscode-extension/` (see that folder’s README or `vsce package`), then:
|
|
180
180
|
|
|
181
181
|
```bash
|
|
182
|
-
code --install-extension
|
|
182
|
+
code --install-extension /path/to/agentxchain-*.vsix
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
Adds: status bar (lock holder, turn, phase), sidebar dashboard, command palette integration.
|
|
186
|
-
|
|
187
185
|
## Publish updates (maintainers)
|
|
188
186
|
|
|
189
187
|
```bash
|
package/bin/agentxchain.js
CHANGED
|
@@ -1,9 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
4
|
-
import { join, dirname } from 'path';
|
|
3
|
+
import { readFileSync, existsSync } from 'fs';
|
|
4
|
+
import { join, dirname, parse as pathParse, resolve } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
|
+
|
|
8
|
+
// Load .env from AgentXchain project root when available, then cwd as fallback.
|
|
9
|
+
(function loadDotenv() {
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const projectRoot = findNearestProjectRoot(cwd);
|
|
12
|
+
const envPaths = [];
|
|
13
|
+
|
|
14
|
+
if (projectRoot) {
|
|
15
|
+
envPaths.push(join(projectRoot, '.env'));
|
|
16
|
+
}
|
|
17
|
+
if (!projectRoot || projectRoot !== cwd) {
|
|
18
|
+
envPaths.push(join(cwd, '.env'));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const envPath of envPaths) {
|
|
22
|
+
if (!existsSync(envPath)) continue;
|
|
23
|
+
try {
|
|
24
|
+
const content = readFileSync(envPath, 'utf8');
|
|
25
|
+
for (const line of content.split(/\r?\n/)) {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
28
|
+
const eqIdx = trimmed.indexOf('=');
|
|
29
|
+
if (eqIdx === -1) continue;
|
|
30
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
31
|
+
let val = trimmed.slice(eqIdx + 1).trim();
|
|
32
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
33
|
+
val = val.slice(1, -1);
|
|
34
|
+
}
|
|
35
|
+
if (!process.env[key]) process.env[key] = val;
|
|
36
|
+
}
|
|
37
|
+
} catch {}
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
|
|
41
|
+
function findNearestProjectRoot(startDir) {
|
|
42
|
+
let dir = resolve(startDir);
|
|
43
|
+
const { root: fsRoot } = pathParse(dir);
|
|
44
|
+
while (true) {
|
|
45
|
+
if (existsSync(join(dir, 'agentxchain.json'))) return dir;
|
|
46
|
+
if (dir === fsRoot) return null;
|
|
47
|
+
dir = join(dir, '..');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
7
50
|
import { initCommand } from '../src/commands/init.js';
|
|
8
51
|
import { statusCommand } from '../src/commands/status.js';
|
|
9
52
|
import { startCommand } from '../src/commands/start.js';
|
|
@@ -17,6 +60,8 @@ import { doctorCommand } from '../src/commands/doctor.js';
|
|
|
17
60
|
import { superviseCommand } from '../src/commands/supervise.js';
|
|
18
61
|
import { validateCommand } from '../src/commands/validate.js';
|
|
19
62
|
import { kickoffCommand } from '../src/commands/kickoff.js';
|
|
63
|
+
import { rebindCommand } from '../src/commands/rebind.js';
|
|
64
|
+
import { branchCommand } from '../src/commands/branch.js';
|
|
20
65
|
|
|
21
66
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
67
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
@@ -60,7 +105,7 @@ program
|
|
|
60
105
|
|
|
61
106
|
program
|
|
62
107
|
.command('stop')
|
|
63
|
-
.description('Stop
|
|
108
|
+
.description('Stop watch daemon and Claude Code sessions; close Cursor/VS Code chats manually')
|
|
64
109
|
.action(stopCommand);
|
|
65
110
|
|
|
66
111
|
program
|
|
@@ -72,6 +117,13 @@ program
|
|
|
72
117
|
.option('-j, --json', 'Output config as JSON')
|
|
73
118
|
.action(configCommand);
|
|
74
119
|
|
|
120
|
+
program
|
|
121
|
+
.command('branch [name]')
|
|
122
|
+
.description('Show or set the Cursor branch used for launches')
|
|
123
|
+
.option('--use-current', 'Set override to the current local git branch')
|
|
124
|
+
.option('--unset', 'Remove override and follow the active git branch automatically')
|
|
125
|
+
.action(branchCommand);
|
|
126
|
+
|
|
75
127
|
program
|
|
76
128
|
.command('generate')
|
|
77
129
|
.description('Regenerate VS Code agent files (.agent.md, hooks) from agentxchain.json')
|
|
@@ -91,6 +143,13 @@ program
|
|
|
91
143
|
.option('--interval <seconds>', 'Auto-nudge poll interval in seconds', '3')
|
|
92
144
|
.action(superviseCommand);
|
|
93
145
|
|
|
146
|
+
program
|
|
147
|
+
.command('rebind')
|
|
148
|
+
.description('Rebuild Cursor prompt/workspace bindings for agents')
|
|
149
|
+
.option('--agent <id>', 'Rebind a single agent only')
|
|
150
|
+
.option('--open', 'Reopen Cursor windows after rebinding')
|
|
151
|
+
.action(rebindCommand);
|
|
152
|
+
|
|
94
153
|
program
|
|
95
154
|
.command('claim')
|
|
96
155
|
.description('Claim the lock as a human (take control)')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentxchain",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
4
4
|
"description": "CLI for AgentXchain — multi-agent coordination in your IDE",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"dev": "node bin/agentxchain.js",
|
|
17
|
+
"test": "node --test test/*.test.js",
|
|
17
18
|
"build:macos": "bun build bin/agentxchain.js --compile --target=bun-darwin-arm64 --outfile=dist/agentxchain-macos-arm64",
|
|
18
19
|
"build:linux": "bun build bin/agentxchain.js --compile --target=bun-linux-x64 --outfile=dist/agentxchain-linux-x64",
|
|
19
20
|
"publish:npm": "bash scripts/publish-npm.sh"
|
|
@@ -43,6 +44,6 @@
|
|
|
43
44
|
"ora": "^8.0.0"
|
|
44
45
|
},
|
|
45
46
|
"engines": {
|
|
46
|
-
"node": ">=18"
|
|
47
|
+
"node": ">=18.17.0 || >=20.5.0"
|
|
47
48
|
}
|
|
48
49
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
property projectRoot : ""
|
|
2
2
|
property pollSeconds : 3
|
|
3
3
|
property autoSend : false
|
|
4
|
+
property lastFailedDispatch : ""
|
|
4
5
|
|
|
5
6
|
on run argv
|
|
6
7
|
if (count of argv) < 1 then
|
|
@@ -41,8 +42,10 @@ on run argv
|
|
|
41
42
|
set lastKey to do shell script "test -f " & quoted form of statePath & " && cat " & quoted form of statePath & " || echo ''"
|
|
42
43
|
|
|
43
44
|
if dispatchKey is not lastKey then
|
|
44
|
-
my nudgeAgent(agentId, turnNum)
|
|
45
|
-
|
|
45
|
+
set nudgedOk to my nudgeAgent(agentId, turnNum, dispatchKey)
|
|
46
|
+
if nudgedOk then
|
|
47
|
+
do shell script "printf %s " & quoted form of dispatchKey & " > " & quoted form of statePath
|
|
48
|
+
end if
|
|
46
49
|
end if
|
|
47
50
|
end if
|
|
48
51
|
end if
|
|
@@ -52,35 +55,67 @@ on run argv
|
|
|
52
55
|
end repeat
|
|
53
56
|
end run
|
|
54
57
|
|
|
55
|
-
on nudgeAgent(agentId, turnNum)
|
|
58
|
+
on nudgeAgent(agentId, turnNum, dispatchKey)
|
|
56
59
|
set nudgeText to "Hey " & agentId & ", it is your turn now (turn " & turnNum & "). Read lock.json, claim the lock, check state.md + history.jsonl + planning docs, do your work, and release lock."
|
|
57
60
|
set the clipboard to nudgeText
|
|
58
61
|
|
|
62
|
+
tell application "System Events"
|
|
63
|
+
if not (exists process "Cursor") then
|
|
64
|
+
if lastFailedDispatch is not dispatchKey then
|
|
65
|
+
do shell script "osascript -e " & quoted form of ("display notification \"Cursor is not running.\" with title \"AgentXchain\"")
|
|
66
|
+
set lastFailedDispatch to dispatchKey
|
|
67
|
+
end if
|
|
68
|
+
return false
|
|
69
|
+
end if
|
|
70
|
+
end tell
|
|
71
|
+
|
|
59
72
|
tell application "Cursor" to activate
|
|
60
|
-
delay 0.
|
|
73
|
+
delay 0.6
|
|
74
|
+
|
|
75
|
+
-- Verify Cursor is actually frontmost before sending keystrokes
|
|
76
|
+
tell application "System Events"
|
|
77
|
+
set frontApp to name of first application process whose frontmost is true
|
|
78
|
+
if frontApp is not "Cursor" then
|
|
79
|
+
if lastFailedDispatch is not dispatchKey then
|
|
80
|
+
do shell script "osascript -e " & quoted form of ("display notification \"Cursor lost focus, skipping nudge for " & agentId & ".\" with title \"AgentXchain\"")
|
|
81
|
+
set lastFailedDispatch to dispatchKey
|
|
82
|
+
end if
|
|
83
|
+
return false
|
|
84
|
+
end if
|
|
85
|
+
end tell
|
|
86
|
+
|
|
61
87
|
set focusedOk to my focusAgentWindow(agentId)
|
|
62
88
|
if focusedOk is false then
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
if lastFailedDispatch is not dispatchKey then
|
|
90
|
+
do shell script "osascript -e " & quoted form of ("display notification \"Could not identify a unique window for " & agentId & ".\" with title \"AgentXchain\"")
|
|
91
|
+
set lastFailedDispatch to dispatchKey
|
|
92
|
+
end if
|
|
93
|
+
return false
|
|
65
94
|
end if
|
|
66
|
-
delay 0.
|
|
95
|
+
delay 0.3
|
|
67
96
|
|
|
97
|
+
-- Re-verify focus before keystrokes
|
|
68
98
|
tell application "System Events"
|
|
69
|
-
|
|
99
|
+
set frontApp to name of first application process whose frontmost is true
|
|
100
|
+
if frontApp is not "Cursor" then
|
|
101
|
+
return false
|
|
102
|
+
end if
|
|
70
103
|
|
|
71
104
|
tell process "Cursor"
|
|
72
105
|
set frontmost to true
|
|
73
106
|
keystroke "l" using {command down}
|
|
74
|
-
delay 0.
|
|
107
|
+
delay 0.3
|
|
75
108
|
keystroke "v" using {command down}
|
|
76
109
|
if autoSend then
|
|
77
|
-
delay 0.
|
|
110
|
+
delay 0.2
|
|
78
111
|
key code 36
|
|
79
112
|
end if
|
|
80
113
|
end tell
|
|
81
114
|
end tell
|
|
82
115
|
|
|
116
|
+
set lastFailedDispatch to ""
|
|
83
117
|
do shell script "osascript -e " & quoted form of ("display notification \"Nudged " & agentId & " for turn " & turnNum & "\" with title \"AgentXchain\"")
|
|
118
|
+
return true
|
|
84
119
|
end nudgeAgent
|
|
85
120
|
|
|
86
121
|
on focusAgentWindow(agentId)
|
|
@@ -117,6 +152,10 @@ end focusAgentWindow
|
|
|
117
152
|
|
|
118
153
|
on isStrongWindowMatch(windowName, agentId)
|
|
119
154
|
set tokenA to ".agentxchain-workspaces/" & agentId
|
|
155
|
+
set tokenB to ".agentxchain-workspaces\\" & agentId
|
|
156
|
+
set tokenC to agentId & ".code-workspace"
|
|
120
157
|
if windowName contains tokenA then return true
|
|
158
|
+
if windowName contains tokenB then return true
|
|
159
|
+
if windowName contains tokenC then return true
|
|
121
160
|
return false
|
|
122
161
|
end isStrongWindowMatch
|
package/scripts/run-autonudge.sh
CHANGED
|
@@ -104,7 +104,7 @@ echo "Mode: $( [[ "${AUTO_SEND}" == "true" ]] && echo "auto-send" || echo "
|
|
|
104
104
|
echo "Interval: ${INTERVAL_SECONDS}s"
|
|
105
105
|
echo ""
|
|
106
106
|
echo "Requirements:"
|
|
107
|
-
echo "-
|
|
107
|
+
echo "- Watch must be running (e.g. 'agentxchain supervise --autonudge' starts it in the same supervisor, or run 'agentxchain watch' in another terminal)."
|
|
108
108
|
echo "- Grant Accessibility permission to Terminal and Cursor."
|
|
109
109
|
echo ""
|
|
110
110
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { generateSeedPrompt } from '../lib/seed-prompt.js';
|
|
4
|
-
import { writeFileSync } from 'fs';
|
|
5
4
|
import { join } from 'path';
|
|
5
|
+
import { safeWriteJson } from '../lib/safe-write.js';
|
|
6
|
+
import { filterAgents } from '../lib/filter-agents.js';
|
|
6
7
|
|
|
7
8
|
export async function launchClaudeCodeAgents(config, root, opts) {
|
|
8
9
|
const agents = filterAgents(config, opts.agent);
|
|
@@ -28,22 +29,14 @@ export async function launchClaudeCodeAgents(config, root, opts) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
if (launched.length > 0) {
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
safeWriteJson(join(root, '.agentxchain-session.json'), {
|
|
33
|
+
launched,
|
|
34
|
+
started_at: new Date().toISOString(),
|
|
35
|
+
ide: 'claude-code'
|
|
36
|
+
});
|
|
33
37
|
console.log('');
|
|
34
38
|
console.log(chalk.dim(` Session saved to .agentxchain-session.json`));
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
return launched;
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
function filterAgents(config, specificId) {
|
|
41
|
-
if (specificId) {
|
|
42
|
-
if (!config.agents[specificId]) {
|
|
43
|
-
console.log(chalk.red(` Agent "${specificId}" not found in agentxchain.json`));
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
return { [specificId]: config.agents[specificId] };
|
|
47
|
-
}
|
|
48
|
-
return config.agents;
|
|
49
|
-
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
-
import { writeFileSync, mkdirSync
|
|
2
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import { generatePollingPrompt } from '../lib/seed-prompt-polling.js';
|
|
7
|
+
import { filterAgents } from '../lib/filter-agents.js';
|
|
7
8
|
|
|
8
9
|
export async function launchCursorLocal(config, root, opts) {
|
|
9
10
|
const agents = filterAgents(config, opts.agent);
|
|
@@ -29,7 +30,7 @@ export async function launchCursorLocal(config, root, opts) {
|
|
|
29
30
|
writeFileSync(join(promptDir, `${id}.prompt.md`), prompt);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
// Create per-agent
|
|
33
|
+
// Create per-agent workspace files so each Cursor window has a unique identity
|
|
33
34
|
const workspacesDir = join(root, '.agentxchain-workspaces');
|
|
34
35
|
mkdirSync(workspacesDir, { recursive: true });
|
|
35
36
|
|
|
@@ -39,28 +40,33 @@ export async function launchCursorLocal(config, root, opts) {
|
|
|
39
40
|
? generateKickoffPrompt(id, agent, config, root)
|
|
40
41
|
: generatePollingPrompt(id, agent, config, root);
|
|
41
42
|
|
|
42
|
-
// Create
|
|
43
|
-
const agentWorkspace = join(workspacesDir, id);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!existsSync(agentWorkspace)) {
|
|
50
|
-
symlinkSync(root, agentWorkspace, 'dir');
|
|
51
|
-
}
|
|
52
|
-
} catch {}
|
|
43
|
+
// Create workspace file: .agentxchain-workspaces/<id>.code-workspace
|
|
44
|
+
const agentWorkspace = join(workspacesDir, `${id}.code-workspace`);
|
|
45
|
+
const workspaceJson = {
|
|
46
|
+
folders: [{ path: root }],
|
|
47
|
+
settings: { 'agentxchain.agentId': id }
|
|
48
|
+
};
|
|
49
|
+
writeFileSync(agentWorkspace, JSON.stringify(workspaceJson, null, 2) + '\n');
|
|
53
50
|
|
|
54
51
|
console.log(chalk.cyan(` ─── Agent ${i + 1}/${total}: ${chalk.bold(id)} — ${agent.name} ───`));
|
|
55
52
|
console.log('');
|
|
56
53
|
|
|
57
|
-
copyToClipboard(prompt);
|
|
58
|
-
|
|
54
|
+
const copied = copyToClipboard(prompt);
|
|
55
|
+
if (copied) {
|
|
56
|
+
console.log(chalk.green(' ✓ Prompt copied to clipboard.'));
|
|
57
|
+
} else {
|
|
58
|
+
console.log(chalk.yellow(' ! Clipboard copy failed. Use the saved prompt file manually.'));
|
|
59
|
+
}
|
|
59
60
|
console.log(chalk.dim(` Saved to: .agentxchain-prompts/${id}.prompt.md`));
|
|
60
61
|
|
|
61
62
|
// Open a separate Cursor window using the symlinked path
|
|
62
|
-
openCursorWindow(agentWorkspace);
|
|
63
|
-
|
|
63
|
+
const opened = openCursorWindow(agentWorkspace);
|
|
64
|
+
if (opened) {
|
|
65
|
+
console.log(chalk.dim(` Cursor window opened for ${id}.`));
|
|
66
|
+
} else {
|
|
67
|
+
console.log(chalk.yellow(` Could not open Cursor window automatically for ${id}.`));
|
|
68
|
+
console.log(chalk.dim(` Open manually: cursor --new-window "${agentWorkspace}"`));
|
|
69
|
+
}
|
|
64
70
|
|
|
65
71
|
console.log('');
|
|
66
72
|
console.log(` ${chalk.bold('In the new Cursor window:')}`);
|
|
@@ -112,17 +118,6 @@ export async function launchCursorLocal(config, root, opts) {
|
|
|
112
118
|
console.log('');
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
function filterAgents(config, specificId) {
|
|
116
|
-
if (specificId) {
|
|
117
|
-
if (!config.agents[specificId]) {
|
|
118
|
-
console.log(chalk.red(` Agent "${specificId}" not found in agentxchain.json`));
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
return { [specificId]: config.agents[specificId] };
|
|
122
|
-
}
|
|
123
|
-
return config.agents;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
121
|
function copyToClipboard(text) {
|
|
127
122
|
try {
|
|
128
123
|
if (process.platform === 'darwin') {
|
|
@@ -141,10 +136,12 @@ function openCursorWindow(folderPath) {
|
|
|
141
136
|
try {
|
|
142
137
|
if (process.platform === 'darwin') {
|
|
143
138
|
execSync(`open -na "Cursor" --args "${folderPath}"`, { stdio: 'ignore' });
|
|
144
|
-
return;
|
|
139
|
+
return true;
|
|
145
140
|
}
|
|
146
141
|
execSync(`cursor --new-window "${folderPath}"`, { stdio: 'ignore' });
|
|
142
|
+
return true;
|
|
147
143
|
} catch {}
|
|
144
|
+
return false;
|
|
148
145
|
}
|
|
149
146
|
|
|
150
147
|
function isPmLike(agentId, agentDef) {
|
package/src/commands/branch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { writeFileSync } from 'fs';
|
|
2
1
|
import { join } from 'path';
|
|
3
2
|
import chalk from 'chalk';
|
|
4
3
|
import { loadConfig, CONFIG_FILE } from '../lib/config.js';
|
|
4
|
+
import { safeWriteJson } from '../lib/safe-write.js';
|
|
5
5
|
import { getCurrentBranch } from '../lib/repo.js';
|
|
6
6
|
|
|
7
7
|
export async function branchCommand(name, opts) {
|
|
@@ -94,5 +94,5 @@ function setBranchOverride(config, configPath, branch) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function saveConfig(configPath, config) {
|
|
97
|
-
|
|
97
|
+
safeWriteJson(configPath, config);
|
|
98
98
|
}
|