agentxchain 0.4.3 → 0.5.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # agentxchain
2
2
 
3
- CLI for multi-agent coordination in your IDE. Define a team of AI agents, launch them in Cursor, and let them coordinate via a shared protocol.
3
+ CLI for multi-agent coordination in your IDE. Define a team of AI agents, let them take turns building your project via shared state and lifecycle hooks.
4
4
 
5
5
  ## Install
6
6
 
@@ -14,38 +14,52 @@ Or run without installing:
14
14
  npx agentxchain init
15
15
  ```
16
16
 
17
- ## Quick start
17
+ ## Quick start — VS Code / Cursor (recommended)
18
+
19
+ No API keys or cloud connection needed. Uses native VS Code custom agents.
18
20
 
19
21
  ```bash
20
- agentxchain init # create a project (template selection)
22
+ agentxchain init # create project with agents + hooks
23
+ cd my-project/ && code . # open in VS Code / Cursor
24
+ # Select an agent from Chat dropdown (auto-discovered from .github/agents/)
25
+ agentxchain release # release human lock to begin turns
26
+ ```
27
+
28
+ The `Stop` hook acts as referee: when an agent finishes, it hands off to the next agent automatically.
29
+
30
+ ## Quick start — Cursor Cloud Agents
31
+
32
+ ```bash
33
+ agentxchain init
21
34
  cd my-project/
22
- echo "CURSOR_API_KEY=your_key" >> .env # from cursor.com/settings -> Cloud Agents
23
- # In Cursor, connect GitHub account (needed for private repos)
24
- # Cursor Settings -> GitHub integration
25
- agentxchain start --ide cursor # launch agents
26
- agentxchain watch # coordinate turns automatically
35
+ echo "CURSOR_API_KEY=your_key" >> .env # cursor.com/settings -> Cloud Agents
36
+ # Connect GitHub in Cursor Settings -> GitHub integration
37
+ agentxchain start --ide cursor # launch cloud agents
38
+ agentxchain watch # coordinate turns
39
+ agentxchain release # begin turns (initial lock is human)
27
40
  ```
28
41
 
29
- > `CURSOR_API_KEY` is required for Cursor commands (`start/watch/stop/claim/release` when using Cursor sessions). Your Cursor account also needs GitHub access to the target repository.
42
+ > `CURSOR_API_KEY` is required for Cloud commands. Your Cursor account needs GitHub access to the target repository.
30
43
 
31
44
  ## Commands
32
45
 
33
46
  | Command | What it does |
34
47
  |---------|-------------|
35
- | `init` | Create project folder with agents, protocol files, and templates |
36
- | `start` | Launch agents in Cursor, Claude Code, or VS Code (`CURSOR_API_KEY` required for Cursor) |
37
- | `watch` | The referee coordinates turns, enforces TTL, wakes agents |
48
+ | `init` | Create project folder with agents, protocol files, hooks, and templates |
49
+ | `generate` | Regenerate VS Code agent files (`.agent.md`, hooks) from `agentxchain.json` |
50
+ | `start` | Launch agents in Cursor Cloud, Claude Code, or VS Code |
51
+ | `watch` | The referee for cloud mode — coordinates turns, enforces TTL, wakes agents |
38
52
  | `status` | Show lock, phase, agents, Cursor session info |
39
53
  | `claim` | Human takes control (pauses Cursor agents) |
40
54
  | `release` | Hand lock back to agents |
41
- | `stop` | Terminate all running agents |
55
+ | `stop` | Terminate all running cloud agents |
42
56
  | `branch` | Show/set Cursor branch override (`cursor.ref`) |
43
57
  | `config` | View/edit config, add/remove agents, change rules |
44
58
  | `update` | Self-update CLI from npm |
45
59
 
46
60
  ### Branch selection
47
61
 
48
- By default, Cursor launches use your current local git branch. You can override this when needed.
62
+ By default, Cursor launches use your current local git branch.
49
63
 
50
64
  ```bash
51
65
  agentxchain branch # show current/effective branch
@@ -54,20 +68,39 @@ agentxchain branch --use-current # pin to whatever branch you're on now
54
68
  agentxchain branch --unset # remove pin; follow active git branch
55
69
  ```
56
70
 
57
- ### Additional command flags
71
+ ### Additional flags
58
72
 
59
73
  ```bash
60
74
  agentxchain watch --daemon # run watch in background
61
75
  agentxchain release --force # force-release non-human holder lock
62
76
  ```
63
77
 
78
+ ## VS Code plugin
79
+
80
+ `agentxchain init` generates native VS Code agent files:
81
+
82
+ - `.github/agents/*.agent.md` — custom agents (auto-discovered by VS Code Chat)
83
+ - `.github/hooks/agentxchain.json` — lifecycle hooks (Stop = referee, SessionStart = context injection)
84
+ - `scripts/agentxchain-*.sh` — hook shell scripts
85
+
86
+ VS Code extension (optional, for UI):
87
+ - Status bar: lock holder, turn, phase (live-updated via file watcher)
88
+ - Sidebar: agent dashboard with quick actions
89
+ - Commands: Claim, Release, Status, Generate
90
+
91
+ Install the extension:
92
+ ```bash
93
+ code --install-extension cli/vscode-extension/agentxchain-0.1.0.vsix
94
+ ```
95
+
64
96
  ## Key features
65
97
 
98
+ - **Native VS Code agents** — `.agent.md` files, lifecycle hooks, handoffs
66
99
  - **Claim-based coordination** — no fixed turn order; agents self-organize
100
+ - **Stop hook referee** — deterministic turn coordination via VS Code hooks
67
101
  - **User-defined teams** — any number of agents, any roles
68
- - **Cursor Cloud Agents** — launch and manage agents via API
69
- - **Branch-safe launching** — defaults to active git branch; optional `branch` override
70
- - **Project `.env` loading** — CLI auto-reads `CURSOR_API_KEY` from project root `.env`
102
+ - **Cursor Cloud Agents** — launch and manage via API (optional)
103
+ - **Branch-safe launching** — defaults to active git branch
71
104
  - **Lock TTL** — stale locks auto-released after timeout
72
105
  - **Verify command** — agents must pass tests before releasing
73
106
  - **Human-in-the-loop** — claim/release to intervene anytime
@@ -10,6 +10,7 @@ import { updateCommand } from '../src/commands/update.js';
10
10
  import { watchCommand } from '../src/commands/watch.js';
11
11
  import { claimCommand, releaseCommand } from '../src/commands/claim.js';
12
12
  import { branchCommand } from '../src/commands/branch.js';
13
+ import { generateCommand } from '../src/commands/generate.js';
13
14
 
14
15
  const program = new Command();
15
16
 
@@ -59,6 +60,11 @@ program
59
60
  .option('--unset', 'Remove override and follow active git branch automatically')
60
61
  .action(branchCommand);
61
62
 
63
+ program
64
+ .command('generate')
65
+ .description('Regenerate VS Code agent files (.agent.md, hooks) from agentxchain.json')
66
+ .action(generateCommand);
67
+
62
68
  program
63
69
  .command('watch')
64
70
  .description('Watch lock.json and coordinate agent turns (the referee)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "CLI for AgentXchain — multi-agent coordination in your IDE",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,44 @@
1
+ import chalk from 'chalk';
2
+ import { loadConfig } from '../lib/config.js';
3
+ import { generateVSCodeFiles } from '../lib/generate-vscode.js';
4
+
5
+ export async function generateCommand() {
6
+ const result = loadConfig();
7
+ if (!result) {
8
+ console.log(chalk.red(' No agentxchain.json found. Run `agentxchain init` first.'));
9
+ process.exit(1);
10
+ }
11
+
12
+ const { root, config } = result;
13
+ const agentIds = Object.keys(config.agents || {});
14
+
15
+ if (agentIds.length === 0) {
16
+ console.log(chalk.red(' No agents configured in agentxchain.json.'));
17
+ process.exit(1);
18
+ }
19
+
20
+ console.log('');
21
+ console.log(chalk.bold(' Generating VS Code agent files...'));
22
+ console.log(chalk.dim(` Project: ${config.project}`));
23
+ console.log('');
24
+
25
+ const vsResult = generateVSCodeFiles(root, config);
26
+
27
+ console.log(chalk.green(` ✓ Generated ${vsResult.agentCount} agent files`));
28
+ console.log('');
29
+
30
+ for (const id of agentIds) {
31
+ const name = config.agents[id].name;
32
+ console.log(` ${chalk.cyan(id)}.agent.md — ${name}`);
33
+ }
34
+
35
+ console.log('');
36
+ console.log(chalk.dim(' Files written:'));
37
+ console.log(` .github/agents/ ${chalk.dim(`(${vsResult.agentCount} .agent.md files)`)}`);
38
+ console.log(` .github/hooks/ ${chalk.dim('agentxchain.json')}`);
39
+ console.log(` scripts/ ${chalk.dim('session-start, stop, pre-tool hooks')}`);
40
+ console.log('');
41
+ console.log(chalk.dim(' VS Code will auto-discover agents from .github/agents/.'));
42
+ console.log(chalk.dim(' Select an agent from the Chat dropdown to start a turn.'));
43
+ console.log('');
44
+ }
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'url';
4
4
  import chalk from 'chalk';
5
5
  import inquirer from 'inquirer';
6
6
  import { CONFIG_FILE, LOCK_FILE, STATE_FILE } from '../lib/config.js';
7
+ import { generateVSCodeFiles } from '../lib/generate-vscode.js';
7
8
 
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
10
  const TEMPLATES_DIR = join(__dirname, '../templates');
@@ -194,7 +195,7 @@ export async function initCommand(opts) {
194
195
  }
195
196
  };
196
197
 
197
- const lock = { holder: null, last_released_by: null, turn_number: 0, claimed_at: null };
198
+ const lock = { holder: 'human', last_released_by: null, turn_number: 0, claimed_at: new Date().toISOString() };
198
199
  const state = { phase: 'discovery', blocked: false, blocked_on: null, project };
199
200
 
200
201
  // Core protocol files
@@ -247,6 +248,9 @@ export async function initCommand(opts) {
247
248
 
248
249
  writeFileSync(join(dir, '.planning', 'qa', 'BUGS.md'), `# Bugs — ${project}\n\n## Open\n\n(QA adds bugs here with reproduction steps.)\n\n## Fixed\n\n(Bugs move here when dev confirms the fix and QA verifies it.)\n`);
249
250
 
251
+ // VS Code agent files (.github/agents/, .github/hooks/, scripts/)
252
+ const vsResult = generateVSCodeFiles(dir, config);
253
+
250
254
  const agentCount = Object.keys(agents).length;
251
255
  console.log('');
252
256
  console.log(chalk.green(` ✓ Created ${chalk.bold(folderName)}/`));
@@ -255,10 +259,13 @@ export async function initCommand(opts) {
255
259
  console.log(` ${chalk.dim('├──')} lock.json`);
256
260
  console.log(` ${chalk.dim('├──')} state.json / state.md / history.jsonl`);
257
261
  console.log(` ${chalk.dim('├──')} log.md / HUMAN_TASKS.md`);
258
- console.log(` ${chalk.dim('└──')} .planning/`);
259
- console.log(` ${chalk.dim('├──')} PROJECT.md / REQUIREMENTS.md / ROADMAP.md`);
260
- console.log(` ${chalk.dim('├──')} research/ / phases/`);
261
- console.log(` ${chalk.dim('└──')} qa/ ${chalk.dim('TEST-COVERAGE / BUGS / UX-AUDIT / ACCEPTANCE-MATRIX')}`);
262
+ console.log(` ${chalk.dim('├──')} .planning/`);
263
+ console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} PROJECT.md / REQUIREMENTS.md / ROADMAP.md`);
264
+ console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} research/ / phases/`);
265
+ console.log(` ${chalk.dim('│')} ${chalk.dim('└──')} qa/ ${chalk.dim('TEST-COVERAGE / BUGS / UX-AUDIT / ACCEPTANCE-MATRIX')}`);
266
+ console.log(` ${chalk.dim('├──')} .github/agents/ ${chalk.dim(`(${agentCount} .agent.md files)`)}`);
267
+ console.log(` ${chalk.dim('├──')} .github/hooks/ ${chalk.dim('agentxchain.json')}`);
268
+ console.log(` ${chalk.dim('└──')} scripts/ ${chalk.dim('hook shell scripts')}`);
262
269
  console.log('');
263
270
  console.log(` ${chalk.dim('Agents:')} ${Object.keys(agents).join(', ')}`);
264
271
  console.log('');
@@ -267,5 +274,6 @@ export async function initCommand(opts) {
267
274
  console.log(` ${chalk.bold('edit .env')} ${chalk.dim('# set CURSOR_API_KEY (required for Cursor mode)')}`);
268
275
  console.log(` ${chalk.bold('agentxchain start')} ${chalk.dim('# launch agents in Cursor')}`);
269
276
  console.log(` ${chalk.bold('agentxchain watch')} ${chalk.dim('# start the referee')}`);
277
+ console.log(` ${chalk.bold('agentxchain release')} ${chalk.dim('# begin automation (initial lock is human)')}`);
270
278
  console.log('');
271
279
  }
@@ -0,0 +1,286 @@
1
+ import { writeFileSync, mkdirSync, existsSync, readFileSync, chmodSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { generateSeedPrompt } from './seed-prompt.js';
4
+
5
+ export function generateVSCodeFiles(dir, config) {
6
+ const agentsDir = join(dir, '.github', 'agents');
7
+ const hooksDir = join(dir, '.github', 'hooks');
8
+ const scriptsDir = join(dir, 'scripts');
9
+
10
+ mkdirSync(agentsDir, { recursive: true });
11
+ mkdirSync(hooksDir, { recursive: true });
12
+ mkdirSync(scriptsDir, { recursive: true });
13
+
14
+ const agentIds = Object.keys(config.agents);
15
+
16
+ for (const id of agentIds) {
17
+ const agent = config.agents[id];
18
+ const md = buildAgentMd(id, agent, config, agentIds);
19
+ writeFileSync(join(agentsDir, `${id}.agent.md`), md);
20
+ }
21
+
22
+ writeFileSync(join(hooksDir, 'agentxchain.json'), buildHooksJson());
23
+ writeFileSync(join(scriptsDir, 'agentxchain-session-start.sh'), SESSION_START_SCRIPT);
24
+ writeFileSync(join(scriptsDir, 'agentxchain-stop.sh'), buildStopScript(config));
25
+ writeFileSync(join(scriptsDir, 'agentxchain-pre-tool.sh'), PRE_TOOL_SCRIPT);
26
+
27
+ try {
28
+ chmodSync(join(scriptsDir, 'agentxchain-session-start.sh'), 0o755);
29
+ chmodSync(join(scriptsDir, 'agentxchain-stop.sh'), 0o755);
30
+ chmodSync(join(scriptsDir, 'agentxchain-pre-tool.sh'), 0o755);
31
+ } catch {}
32
+
33
+ return { agentsDir, hooksDir, scriptsDir, agentCount: agentIds.length };
34
+ }
35
+
36
+ function buildAgentMd(agentId, agentDef, config, allAgentIds) {
37
+ const otherAgents = allAgentIds.filter(id => id !== agentId);
38
+ const verifyCmd = config.rules?.verify_command || null;
39
+ const maxClaims = config.rules?.max_consecutive_claims || 2;
40
+ const stateFile = config.state_file || 'state.md';
41
+ const historyFile = config.history_file || 'history.jsonl';
42
+ const logFile = config.log || 'log.md';
43
+ const useSplit = config.state_file || config.history_file;
44
+
45
+ const handoffs = otherAgents.map(otherId => {
46
+ const other = config.agents[otherId];
47
+ return ` - label: "Hand off to ${other.name}"
48
+ agent: ${otherId}
49
+ prompt: "Previous agent finished. Read lock.json, claim it, and do your work as ${other.name}."
50
+ send: true`;
51
+ });
52
+
53
+ handoffs.push(` - label: "Request Human Review"
54
+ agent: agent
55
+ prompt: "An agent requests human review. Check HUMAN_TASKS.md and lock.json."
56
+ send: false`);
57
+
58
+ const toolsList = "['search', 'fetch', 'editFiles', 'terminalLastCommand', 'codebase', 'usages']";
59
+
60
+ const frontmatter = `---
61
+ name: "${agentDef.name}"
62
+ description: "${escapeYaml(agentDef.mandate.split('\n')[0].slice(0, 120))}"
63
+ tools: ${toolsList}
64
+ model: ['claude-sonnet-4-5-20250514', 'gpt-4.1']
65
+ handoffs:
66
+ ${handoffs.join('\n')}
67
+ hooks:
68
+ Stop:
69
+ - type: command
70
+ command: "./scripts/agentxchain-stop.sh"
71
+ SessionStart:
72
+ - type: command
73
+ command: "./scripts/agentxchain-session-start.sh"
74
+ ---`;
75
+
76
+ const readInstructions = useSplit
77
+ ? `Read these files at the start of your turn:
78
+ - \`${stateFile}\` — living project state (primary context)
79
+ - \`${historyFile}\` — last 3 lines for recent turns
80
+ - \`lock.json\` — current lock holder
81
+ - \`state.json\` — phase and blocked status`
82
+ : `Read these files at the start of your turn:
83
+ - \`${logFile}\` — message log (read last few messages)
84
+ - \`lock.json\` — current lock holder
85
+ - \`state.json\` — phase and blocked status`;
86
+
87
+ const writeInstructions = useSplit
88
+ ? `When you finish your work, write in this order:
89
+ 1. Your actual work: code, files, commands, decisions.
90
+ 2. Overwrite \`${stateFile}\` with current project state.
91
+ 3. Append one line to \`${historyFile}\`:
92
+ \`{"turn": N, "agent": "${agentId}", "summary": "...", "files_changed": [...], "verify_result": "pass|fail|skipped", "timestamp": "ISO8601"}\`
93
+ 4. Update \`state.json\` if phase or blocked status changed.`
94
+ : `When you finish your work, write in this order:
95
+ 1. Your actual work: code, files, commands, decisions.
96
+ 2. Append one message to \`${logFile}\`:
97
+ \`### [${agentId}] (${agentDef.name}) | Turn N\`
98
+ with Status, Decision, Action, Next sections.
99
+ 3. Update \`state.json\` if phase or blocked status changed.`;
100
+
101
+ const verifyInstructions = verifyCmd
102
+ ? `\n## Verify before release\nBefore releasing the lock, run: \`${verifyCmd}\`\nIf it fails, fix the problem and run again. Do NOT release with a failing verification.`
103
+ : '';
104
+
105
+ const body = `# ${agentDef.name}
106
+
107
+ You are "${agentId}" on an AgentXchain team.
108
+
109
+ ${agentDef.mandate}
110
+
111
+ ---
112
+
113
+ ## Project documentation
114
+
115
+ Read the files relevant to your role in the \`.planning/\` folder:
116
+ - \`.planning/PROJECT.md\` — Vision, constraints, stack (PM writes)
117
+ - \`.planning/REQUIREMENTS.md\` — Requirements with acceptance criteria (PM writes)
118
+ - \`.planning/ROADMAP.md\` — Phased delivery plan (PM maintains)
119
+ - \`.planning/research/\` — Domain research
120
+ - \`.planning/phases/\` — Per-phase plans, reviews, tests, bugs
121
+ - \`.planning/qa/\` — TEST-COVERAGE, BUGS, UX-AUDIT, ACCEPTANCE-MATRIX, REGRESSION-LOG (QA maintains)
122
+
123
+ Create or update these files when your role requires it.
124
+
125
+ ---
126
+
127
+ ## Your turn
128
+
129
+ The AgentXchain system coordinates turns. When prompted, do this:
130
+
131
+ 1. **CLAIM**: Write \`lock.json\` with \`holder="${agentId}"\` and \`claimed_at\` = current time. Re-read to confirm.
132
+ 2. **READ**: ${readInstructions}
133
+ 3. **THINK**: What did the previous agent do? What is most important for YOUR role? What is one risk?
134
+ 4. **WORK**: ${writeInstructions}${verifyInstructions}
135
+ 5. **RELEASE**: Write \`lock.json\`: \`holder=null\`, \`last_released_by="${agentId}"\`, \`turn_number\` = previous + 1, \`claimed_at=null\`.
136
+ This MUST be the last thing you write.
137
+
138
+ ---
139
+
140
+ ## Rules
141
+
142
+ - Never write files without holding the lock.
143
+ - One git commit per turn: "Turn N - ${agentId} - description"
144
+ - Max ${maxClaims} consecutive turns. If limit hit, do a short turn and release.
145
+ - ALWAYS release the lock. A stuck lock blocks the entire team.
146
+ - ALWAYS find at least one problem, risk, or question about the previous work. Blind agreement is forbidden.
147
+ `;
148
+
149
+ return frontmatter + '\n\n' + body;
150
+ }
151
+
152
+ function escapeYaml(str) {
153
+ return str.replace(/"/g, '\\"').replace(/\n/g, ' ');
154
+ }
155
+
156
+ function buildHooksJson() {
157
+ const hooks = {
158
+ hooks: {
159
+ SessionStart: [
160
+ {
161
+ type: 'command',
162
+ command: './scripts/agentxchain-session-start.sh'
163
+ }
164
+ ],
165
+ Stop: [
166
+ {
167
+ type: 'command',
168
+ command: './scripts/agentxchain-stop.sh'
169
+ }
170
+ ]
171
+ }
172
+ };
173
+ return JSON.stringify(hooks, null, 2) + '\n';
174
+ }
175
+
176
+ function buildStopScript(config) {
177
+ const verifyCmd = config.rules?.verify_command || '';
178
+ const verifyBlock = verifyCmd
179
+ ? `
180
+ # Run verify command before allowing release
181
+ if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
182
+ VERIFY_CMD="${verifyCmd}"
183
+ if [ -n "$VERIFY_CMD" ]; then
184
+ if ! eval "$VERIFY_CMD" > /dev/null 2>&1; then
185
+ echo '{"hookSpecificOutput":{"hookEventName":"Stop","decision":"block","reason":"Verification failed: '"$VERIFY_CMD"'. Fix the issue and release the lock."}}'
186
+ exit 0
187
+ fi
188
+ fi
189
+ fi
190
+ `
191
+ : '';
192
+
193
+ return `#!/bin/bash
194
+ INPUT=$(cat)
195
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
196
+
197
+ if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
198
+ echo '{"continue":true}'
199
+ exit 0
200
+ fi
201
+
202
+ if [ ! -f "lock.json" ]; then
203
+ echo '{"continue":true}'
204
+ exit 0
205
+ fi
206
+
207
+ LOCK=$(cat lock.json 2>/dev/null)
208
+ HOLDER=$(echo "$LOCK" | jq -r '.holder // empty')
209
+ TURN=$(echo "$LOCK" | jq -r '.turn_number // 0')
210
+ ${verifyBlock}
211
+ if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
212
+ LAST=$(echo "$LOCK" | jq -r '.last_released_by // empty')
213
+
214
+ if [ ! -f "agentxchain.json" ]; then
215
+ echo '{"continue":true}'
216
+ exit 0
217
+ fi
218
+
219
+ NEXT=$(node -e "
220
+ const cfg = JSON.parse(require('fs').readFileSync('agentxchain.json','utf8'));
221
+ const ids = Object.keys(cfg.agents);
222
+ const last = process.argv[1] || '';
223
+ const idx = ids.indexOf(last);
224
+ const next = ids[(idx + 1) % ids.length];
225
+ process.stdout.write(next);
226
+ " -- "$LAST" 2>/dev/null)
227
+
228
+ if [ -z "$NEXT" ]; then
229
+ echo '{"continue":true}'
230
+ exit 0
231
+ fi
232
+
233
+ NEXT_NAME=$(node -e "
234
+ const cfg = JSON.parse(require('fs').readFileSync('agentxchain.json','utf8'));
235
+ const a = cfg.agents[process.argv[1]];
236
+ process.stdout.write(a ? a.name : process.argv[1]);
237
+ " -- "$NEXT" 2>/dev/null)
238
+
239
+ echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"Stop\\",\\"decision\\":\\"block\\",\\"reason\\":\\"Turn $TURN complete. Next agent: $NEXT ($NEXT_NAME). Read lock.json, claim it, and do your work.\\"}}"
240
+ elif [ "$HOLDER" = "human" ]; then
241
+ echo '{"continue":true}'
242
+ else
243
+ echo '{"continue":true}'
244
+ fi
245
+ `;
246
+ }
247
+
248
+ const SESSION_START_SCRIPT = `#!/bin/bash
249
+ if [ ! -f "lock.json" ] || [ ! -f "state.json" ]; then
250
+ echo '{"continue":true}'
251
+ exit 0
252
+ fi
253
+
254
+ LOCK=$(cat lock.json 2>/dev/null)
255
+ STATE=$(cat state.json 2>/dev/null)
256
+
257
+ HOLDER=$(echo "$LOCK" | jq -r '.holder // "none"')
258
+ TURN=$(echo "$LOCK" | jq -r '.turn_number // 0')
259
+ LAST=$(echo "$LOCK" | jq -r '.last_released_by // "none"')
260
+ PHASE=$(echo "$STATE" | jq -r '.phase // "unknown"')
261
+ BLOCKED=$(echo "$STATE" | jq -r '.blocked // false')
262
+ PROJECT=$(echo "$STATE" | jq -r '.project // "unknown"')
263
+
264
+ CONTEXT="AgentXchain context: Project=$PROJECT | Phase=$PHASE | Turn=$TURN | Lock=$HOLDER | Last released by=$LAST | Blocked=$BLOCKED"
265
+
266
+ echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"SessionStart\\",\\"additionalContext\\":\\"$CONTEXT\\"}}"
267
+ `;
268
+
269
+ const PRE_TOOL_SCRIPT = `#!/bin/bash
270
+ INPUT=$(cat)
271
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
272
+
273
+ WRITE_TOOLS="editFiles|createFile|create_file|replace_string_in_file|deleteFile"
274
+
275
+ if echo "$TOOL_NAME" | grep -qE "^($WRITE_TOOLS)\$"; then
276
+ if [ -f "lock.json" ]; then
277
+ HOLDER=$(cat lock.json | jq -r '.holder // empty')
278
+ if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
279
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"You must claim lock.json before writing files. Write holder=your_agent_id first."}}'
280
+ exit 0
281
+ fi
282
+ fi
283
+ fi
284
+
285
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
286
+ `;