a2acalling 0.6.47 → 0.6.49
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/bin/cli.js +78 -9
- package/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/docs/prompts/e2e-test-agent.md +368 -0
- package/docs/protocol.md +79 -0
- package/package.json +1 -1
- package/src/dashboard/public/app.js +108 -1
- package/src/dashboard/public/index.html +9 -0
- package/src/dashboard/public/style.css +27 -0
- package/src/lib/config.js +41 -0
- package/src/lib/conversation-driver.js +62 -21
- package/src/lib/openclaw-integration.js +22 -66
- package/src/lib/summary-formatter.js +168 -0
- package/src/lib/summary-prompt.js +203 -0
- package/src/lib/update-checker.js +93 -0
- package/src/lib/update-manager.js +313 -0
- package/src/routes/a2a.js +8 -1
- package/src/routes/dashboard.js +103 -1
- package/src/server.js +126 -25
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
# Claude Code & Codex CLI Skills for A2A Calling — Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Ship `.claude/commands/` slash commands and `.codex/` AGENTS.md instructions that let Claude Code and Codex CLI users operate A2A directly from their coding agent — create tokens, manage contacts, make calls, check status — all wrapping the existing `a2a` CLI binary.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Both tools use markdown-based configuration. Claude Code reads `.claude/commands/<name>.md` to register `/a2a-*` slash commands. Codex reads `AGENTS.md` for instructions. Both invoke the same `a2a` CLI under the hood via `Bash`/shell. A postinstall hook copies these files into the user's project when `npm install -g a2acalling` runs. No new runtime code — just markdown wiring.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Markdown (Claude Code commands + Codex AGENTS.md), shell (CLI invocations), Node.js (postinstall copy logic)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Inventory
|
|
14
|
+
|
|
15
|
+
| File | Action | Purpose |
|
|
16
|
+
|------|--------|---------|
|
|
17
|
+
| `.claude/commands/a2a-call.md` | Create | `/a2a-call` — call a contact or invite URL |
|
|
18
|
+
| `.claude/commands/a2a-invite.md` | Create | `/a2a-invite` — create token and show invite URL |
|
|
19
|
+
| `.claude/commands/a2a-contacts.md` | Create | `/a2a-contacts` — list/manage contacts |
|
|
20
|
+
| `.claude/commands/a2a-status.md` | Create | `/a2a-status` — check server + agent health |
|
|
21
|
+
| `.claude/commands/a2a-setup.md` | Create | `/a2a-setup` — onboard and start server |
|
|
22
|
+
| `.codex/AGENTS.md` | Create | Codex CLI agent instructions for A2A |
|
|
23
|
+
| `scripts/install-skills.js` | Create | Copies skills into user project on install |
|
|
24
|
+
| `test/unit/install-skills.test.js` | Create | Tests for skill installer |
|
|
25
|
+
| `scripts/postinstall.js` | Modify | Call install-skills after server setup |
|
|
26
|
+
| `docs/protocol.md` | Modify | Add "CLI Skills" section |
|
|
27
|
+
| `bin/cli.js` | Modify | Add `a2a skills` subcommand |
|
|
28
|
+
|
|
29
|
+
**Total new files:** 8
|
|
30
|
+
**Total modified files:** 3
|
|
31
|
+
**Estimated commits:** 8
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Phase 1: Claude Code Slash Commands
|
|
36
|
+
|
|
37
|
+
### Task 1: `/a2a-call` — Call a contact
|
|
38
|
+
|
|
39
|
+
**Files:**
|
|
40
|
+
- Create: `.claude/commands/a2a-call.md`
|
|
41
|
+
|
|
42
|
+
**Step 1: Create the command file**
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
---
|
|
46
|
+
description: Call another A2A agent — starts a multi-turn conversation
|
|
47
|
+
allowed-tools: [Bash, Read]
|
|
48
|
+
argument-hint: <contact-or-url> <message>
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
Call an A2A agent. This starts a multi-turn agent-to-agent conversation.
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
/a2a-call Alice "Hello! My owner wants to discuss the project."
|
|
57
|
+
/a2a-call a2a://host.com/fed_abc123 "Reaching out about collaboration"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Instructions
|
|
61
|
+
|
|
62
|
+
Run the following command with the user's arguments:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
a2a call $ARGUMENTS
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
If the call succeeds, summarize the conversation outcome for the user.
|
|
69
|
+
If it fails with "not onboarded", tell the user to run `/a2a-setup` first.
|
|
70
|
+
If it fails with "contact not found", suggest `/a2a-contacts` to see available contacts.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Step 2: Verify file is valid markdown with frontmatter**
|
|
74
|
+
|
|
75
|
+
Run: `head -5 .claude/commands/a2a-call.md`
|
|
76
|
+
Expected: YAML frontmatter with `---` delimiters
|
|
77
|
+
|
|
78
|
+
**Step 3: Commit**
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
git add .claude/commands/a2a-call.md
|
|
82
|
+
git commit -m "feat: add /a2a-call Claude Code slash command"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### Task 2: `/a2a-invite` — Create token and invite URL
|
|
88
|
+
|
|
89
|
+
**Files:**
|
|
90
|
+
- Create: `.claude/commands/a2a-invite.md`
|
|
91
|
+
|
|
92
|
+
**Step 1: Create the command file**
|
|
93
|
+
|
|
94
|
+
```markdown
|
|
95
|
+
---
|
|
96
|
+
description: Create an A2A invite token to share with another agent
|
|
97
|
+
allowed-tools: [Bash]
|
|
98
|
+
argument-hint: [name] [--tier public|friends|family] [--expires 7d]
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
Create an A2A federation token and display the invite URL for sharing.
|
|
102
|
+
|
|
103
|
+
## Usage
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
/a2a-invite Alice --tier friends --expires 7d
|
|
107
|
+
/a2a-invite "Bob's Agent" --tier public
|
|
108
|
+
/a2a-invite # interactive — uses defaults
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Instructions
|
|
112
|
+
|
|
113
|
+
Parse the user's arguments and run:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
a2a create --name "$1" $ARGUMENTS
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If no arguments provided, run `a2a create` with no flags (interactive mode).
|
|
120
|
+
|
|
121
|
+
After success, display the invite URL prominently and explain:
|
|
122
|
+
1. The URL format: `a2a://<hostname>/<token>`
|
|
123
|
+
2. Share this URL with the other agent's owner
|
|
124
|
+
3. The token tier controls what the caller can access
|
|
125
|
+
4. The token expires per the `--expires` flag (default: never)
|
|
126
|
+
|
|
127
|
+
Also suggest: "Run `/a2a-contacts` to see who already has access."
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Step 2: Commit**
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
git add .claude/commands/a2a-invite.md
|
|
134
|
+
git commit -m "feat: add /a2a-invite Claude Code slash command"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### Task 3: `/a2a-contacts` — List and manage contacts
|
|
140
|
+
|
|
141
|
+
**Files:**
|
|
142
|
+
- Create: `.claude/commands/a2a-contacts.md`
|
|
143
|
+
|
|
144
|
+
**Step 1: Create the command file**
|
|
145
|
+
|
|
146
|
+
```markdown
|
|
147
|
+
---
|
|
148
|
+
description: List A2A contacts — agents you can call or who can call you
|
|
149
|
+
allowed-tools: [Bash]
|
|
150
|
+
argument-hint: [add|show|ping|rm] [args...]
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
Manage your A2A contact list — see who you can call and who has access to you.
|
|
154
|
+
|
|
155
|
+
## Usage
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
/a2a-contacts # list all contacts
|
|
159
|
+
/a2a-contacts add a2a://host/fed_xxx Alice # add contact from invite URL
|
|
160
|
+
/a2a-contacts show Alice # show contact details
|
|
161
|
+
/a2a-contacts ping Alice # check if contact is online
|
|
162
|
+
/a2a-contacts rm Alice # remove a contact
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Instructions
|
|
166
|
+
|
|
167
|
+
Run the appropriate command based on user input:
|
|
168
|
+
|
|
169
|
+
- No arguments: `a2a contacts`
|
|
170
|
+
- `add`: `a2a contacts add $ARGUMENTS`
|
|
171
|
+
- `show`: `a2a contacts show $ARGUMENTS`
|
|
172
|
+
- `ping`: `a2a contacts ping $ARGUMENTS`
|
|
173
|
+
- `rm`: `a2a contacts rm $ARGUMENTS`
|
|
174
|
+
|
|
175
|
+
If the user just wants to see their contacts, also run `a2a list` to show active tokens (outbound invites).
|
|
176
|
+
|
|
177
|
+
Format the output clearly: contact name, owner, status (online/offline), permission tier, last seen.
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Step 2: Commit**
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
git add .claude/commands/a2a-contacts.md
|
|
184
|
+
git commit -m "feat: add /a2a-contacts Claude Code slash command"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### Task 4: `/a2a-status` — Server and agent health
|
|
190
|
+
|
|
191
|
+
**Files:**
|
|
192
|
+
- Create: `.claude/commands/a2a-status.md`
|
|
193
|
+
|
|
194
|
+
**Step 1: Create the command file**
|
|
195
|
+
|
|
196
|
+
```markdown
|
|
197
|
+
---
|
|
198
|
+
description: Check A2A server status, active conversations, and agent health
|
|
199
|
+
allowed-tools: [Bash, Read]
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
Check the health of your A2A installation — server running, conversations active, contacts online.
|
|
203
|
+
|
|
204
|
+
## Instructions
|
|
205
|
+
|
|
206
|
+
Run these commands and compile a status report:
|
|
207
|
+
|
|
208
|
+
1. **Server health:** `a2a ping a2a://localhost` (or use configured hostname from `a2a config --show`)
|
|
209
|
+
2. **Active tokens:** `a2a list`
|
|
210
|
+
3. **Contacts:** `a2a contacts`
|
|
211
|
+
4. **Recent conversations:** `a2a conversations --limit 5`
|
|
212
|
+
5. **Config:** `a2a config --show`
|
|
213
|
+
|
|
214
|
+
Present a clear status dashboard:
|
|
215
|
+
- Server: running/stopped (with port)
|
|
216
|
+
- Hostname: configured hostname
|
|
217
|
+
- Tokens: N active, N expired
|
|
218
|
+
- Contacts: N total, N online
|
|
219
|
+
- Recent calls: last 5 conversations
|
|
220
|
+
|
|
221
|
+
If the server is not running, suggest `/a2a-setup` to start it.
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Step 2: Commit**
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
git add .claude/commands/a2a-status.md
|
|
228
|
+
git commit -m "feat: add /a2a-status Claude Code slash command"
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### Task 5: `/a2a-setup` — Onboarding and server start
|
|
234
|
+
|
|
235
|
+
**Files:**
|
|
236
|
+
- Create: `.claude/commands/a2a-setup.md`
|
|
237
|
+
|
|
238
|
+
**Step 1: Create the command file**
|
|
239
|
+
|
|
240
|
+
```markdown
|
|
241
|
+
---
|
|
242
|
+
description: Set up A2A Calling — onboard, start server, configure agent
|
|
243
|
+
allowed-tools: [Bash, Read, Write]
|
|
244
|
+
argument-hint: [--force]
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
Set up or reset your A2A Calling installation. Runs onboarding, starts the server, and configures your agent.
|
|
248
|
+
|
|
249
|
+
## Usage
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
/a2a-setup # first-time setup or resume incomplete onboarding
|
|
253
|
+
/a2a-setup --force # reset and re-run from scratch
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Instructions
|
|
257
|
+
|
|
258
|
+
1. Check if already onboarded: `a2a config --show`
|
|
259
|
+
2. If not onboarded (or `--force`): run `a2a quickstart`
|
|
260
|
+
3. If already onboarded but server not running: run `a2a server` in background
|
|
261
|
+
4. After setup, run `/a2a-status` to show the result
|
|
262
|
+
|
|
263
|
+
The quickstart flow will:
|
|
264
|
+
- Detect an available port
|
|
265
|
+
- Start the A2A server
|
|
266
|
+
- Detect the hostname
|
|
267
|
+
- Prompt for disclosure topics (what your agent discusses)
|
|
268
|
+
- Save the configuration
|
|
269
|
+
|
|
270
|
+
If running non-interactively, quickstart auto-accepts defaults.
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Step 2: Commit**
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
git add .claude/commands/a2a-setup.md
|
|
277
|
+
git commit -m "feat: add /a2a-setup Claude Code slash command"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Phase 2: Codex CLI Support
|
|
283
|
+
|
|
284
|
+
### Task 6: Codex AGENTS.md with A2A instructions
|
|
285
|
+
|
|
286
|
+
**Files:**
|
|
287
|
+
- Create: `.codex/AGENTS.md`
|
|
288
|
+
|
|
289
|
+
**Step 1: Create the AGENTS.md file**
|
|
290
|
+
|
|
291
|
+
```markdown
|
|
292
|
+
# A2A Calling — Agent Instructions
|
|
293
|
+
|
|
294
|
+
This project uses **A2A Calling** (`a2acalling`) for agent-to-agent communication. The `a2a` CLI is available globally after `npm install -g a2acalling`.
|
|
295
|
+
|
|
296
|
+
## Quick Reference
|
|
297
|
+
|
|
298
|
+
### Check Status
|
|
299
|
+
```bash
|
|
300
|
+
a2a config --show # Show current config (hostname, port, onboarding status)
|
|
301
|
+
a2a contacts # List all contacts (agents you know)
|
|
302
|
+
a2a list # List active tokens (invites you've sent)
|
|
303
|
+
a2a conversations --limit 5 # Recent conversations
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Make a Call
|
|
307
|
+
```bash
|
|
308
|
+
a2a call <contact> "<message>" # Multi-turn call to a contact
|
|
309
|
+
a2a call a2a://host/fed_xxx "<message>" # Call via invite URL
|
|
310
|
+
a2a call Alice "Hello! Let's discuss the project." # By contact name
|
|
311
|
+
a2a call Alice "Quick question" --single # One-shot (no back-and-forth)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Create an Invite
|
|
315
|
+
```bash
|
|
316
|
+
a2a create --name "AgentName" --tier friends --expires 7d
|
|
317
|
+
# Output: a2a://your-host/fed_xxx — share this URL with the other agent
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Manage Contacts
|
|
321
|
+
```bash
|
|
322
|
+
a2a contacts add a2a://host/fed_xxx --name "Alice" --owner "Alice Chen"
|
|
323
|
+
a2a contacts show Alice
|
|
324
|
+
a2a contacts ping Alice # Check if online
|
|
325
|
+
a2a contacts rm Alice
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Setup & Server
|
|
329
|
+
```bash
|
|
330
|
+
a2a quickstart # First-time setup (port, hostname, disclosure)
|
|
331
|
+
a2a server --port 3001 # Start server manually
|
|
332
|
+
a2a uninstall # Stop server and remove config
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Permission Tiers
|
|
336
|
+
|
|
337
|
+
| Tier | Access Level |
|
|
338
|
+
|------|-------------|
|
|
339
|
+
| `public` | Read-only context |
|
|
340
|
+
| `friends` | Calendar, email, search (read) |
|
|
341
|
+
| `family` | Full access (calendar, email, search, tools, memory) |
|
|
342
|
+
|
|
343
|
+
## Disclosure Levels
|
|
344
|
+
|
|
345
|
+
| Level | Behavior |
|
|
346
|
+
|-------|----------|
|
|
347
|
+
| `public` | Agent shares freely within tier boundaries |
|
|
348
|
+
| `minimal` | Direct answers only, no volunteered context |
|
|
349
|
+
| `none` | Confirms capability, provides no information |
|
|
350
|
+
|
|
351
|
+
## When to Use A2A
|
|
352
|
+
|
|
353
|
+
- **Reaching out to another agent:** `a2a call <contact> "<message>"`
|
|
354
|
+
- **Sharing access with someone:** `a2a create --name "Name" --tier friends`
|
|
355
|
+
- **Checking who can reach you:** `a2a list`
|
|
356
|
+
- **Checking who you can reach:** `a2a contacts`
|
|
357
|
+
|
|
358
|
+
## Important Notes
|
|
359
|
+
|
|
360
|
+
- Run `a2a quickstart` before first use — server must be running
|
|
361
|
+
- Multi-turn calls are the default (agents have a real conversation, 8-25 turns)
|
|
362
|
+
- Use `--single` flag for one-shot questions
|
|
363
|
+
- Tokens are scoped — `public` tier can't access calendar or email
|
|
364
|
+
- The A2A server runs on port 80 (preferred) or 3001+ (fallback)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Step 2: Commit**
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
git add .codex/AGENTS.md
|
|
371
|
+
git commit -m "feat: add Codex CLI AGENTS.md for A2A commands"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Phase 3: Skill Installer & CLI Integration
|
|
377
|
+
|
|
378
|
+
### Task 7: Skill installer script
|
|
379
|
+
|
|
380
|
+
**Files:**
|
|
381
|
+
- Create: `scripts/install-skills.js`
|
|
382
|
+
- Test: `test/unit/install-skills.test.js`
|
|
383
|
+
|
|
384
|
+
**Step 1: Write the failing test**
|
|
385
|
+
|
|
386
|
+
The installer should:
|
|
387
|
+
- Copy `.claude/commands/*.md` to the user's project `.claude/commands/` dir
|
|
388
|
+
- Copy `.codex/AGENTS.md` to the user's project `.codex/` dir
|
|
389
|
+
- Be idempotent (skip if files already exist and are identical)
|
|
390
|
+
- Support `--force` to overwrite
|
|
391
|
+
- Return a summary of what was installed
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
// test/unit/install-skills.test.js
|
|
395
|
+
module.exports = function(test, assert, helpers) {
|
|
396
|
+
const fs = require('fs');
|
|
397
|
+
const path = require('path');
|
|
398
|
+
const os = require('os');
|
|
399
|
+
|
|
400
|
+
test('installSkills creates .claude/commands directory and copies files', () => {
|
|
401
|
+
const targetDir = fs.mkdtempSync(path.join(os.tmpdir(), 'a2a-skills-'));
|
|
402
|
+
try {
|
|
403
|
+
const { installSkills } = require('../../scripts/install-skills');
|
|
404
|
+
const result = installSkills(targetDir);
|
|
405
|
+
|
|
406
|
+
assert.ok(result.installed.length > 0, 'Should install at least one file');
|
|
407
|
+
assert.ok(fs.existsSync(path.join(targetDir, '.claude', 'commands', 'a2a-call.md')),
|
|
408
|
+
'Should create a2a-call.md');
|
|
409
|
+
assert.ok(fs.existsSync(path.join(targetDir, '.codex', 'AGENTS.md')),
|
|
410
|
+
'Should create AGENTS.md');
|
|
411
|
+
} finally {
|
|
412
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('installSkills skips existing identical files', () => {
|
|
417
|
+
const targetDir = fs.mkdtempSync(path.join(os.tmpdir(), 'a2a-skills-'));
|
|
418
|
+
try {
|
|
419
|
+
const { installSkills } = require('../../scripts/install-skills');
|
|
420
|
+
installSkills(targetDir);
|
|
421
|
+
const result2 = installSkills(targetDir);
|
|
422
|
+
|
|
423
|
+
assert.equal(result2.skipped.length > 0, true, 'Should skip files on second run');
|
|
424
|
+
assert.equal(result2.installed.length, 0, 'Should not re-install identical files');
|
|
425
|
+
} finally {
|
|
426
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test('installSkills with force overwrites existing files', () => {
|
|
431
|
+
const targetDir = fs.mkdtempSync(path.join(os.tmpdir(), 'a2a-skills-'));
|
|
432
|
+
try {
|
|
433
|
+
const { installSkills } = require('../../scripts/install-skills');
|
|
434
|
+
installSkills(targetDir);
|
|
435
|
+
const result2 = installSkills(targetDir, { force: true });
|
|
436
|
+
|
|
437
|
+
assert.ok(result2.installed.length > 0, 'Should overwrite files with force');
|
|
438
|
+
} finally {
|
|
439
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
test('installSkills returns summary with correct counts', () => {
|
|
444
|
+
const targetDir = fs.mkdtempSync(path.join(os.tmpdir(), 'a2a-skills-'));
|
|
445
|
+
try {
|
|
446
|
+
const { installSkills } = require('../../scripts/install-skills');
|
|
447
|
+
const result = installSkills(targetDir);
|
|
448
|
+
|
|
449
|
+
assert.ok(Array.isArray(result.installed), 'Should have installed array');
|
|
450
|
+
assert.ok(Array.isArray(result.skipped), 'Should have skipped array');
|
|
451
|
+
assert.ok(Array.isArray(result.errors), 'Should have errors array');
|
|
452
|
+
assert.equal(result.errors.length, 0, 'Should have no errors');
|
|
453
|
+
} finally {
|
|
454
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
};
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Step 2: Run test to verify it fails**
|
|
461
|
+
|
|
462
|
+
Run: `node test/run.js --filter install-skills`
|
|
463
|
+
Expected: FAIL — `scripts/install-skills.js` doesn't exist yet
|
|
464
|
+
|
|
465
|
+
**Step 3: Write minimal implementation**
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
// scripts/install-skills.js
|
|
469
|
+
/**
|
|
470
|
+
* A2A Skill Installer
|
|
471
|
+
*
|
|
472
|
+
* Copies Claude Code commands and Codex AGENTS.md into a target project directory.
|
|
473
|
+
* Idempotent: skips files that already exist with identical content.
|
|
474
|
+
*/
|
|
475
|
+
|
|
476
|
+
const fs = require('fs');
|
|
477
|
+
const path = require('path');
|
|
478
|
+
|
|
479
|
+
const PACKAGE_ROOT = path.join(__dirname, '..');
|
|
480
|
+
|
|
481
|
+
const SKILL_FILES = [
|
|
482
|
+
{ src: '.claude/commands/a2a-call.md', dest: '.claude/commands/a2a-call.md' },
|
|
483
|
+
{ src: '.claude/commands/a2a-invite.md', dest: '.claude/commands/a2a-invite.md' },
|
|
484
|
+
{ src: '.claude/commands/a2a-contacts.md', dest: '.claude/commands/a2a-contacts.md' },
|
|
485
|
+
{ src: '.claude/commands/a2a-status.md', dest: '.claude/commands/a2a-status.md' },
|
|
486
|
+
{ src: '.claude/commands/a2a-setup.md', dest: '.claude/commands/a2a-setup.md' },
|
|
487
|
+
{ src: '.codex/AGENTS.md', dest: '.codex/AGENTS.md' }
|
|
488
|
+
];
|
|
489
|
+
|
|
490
|
+
function installSkills(targetDir, options = {}) {
|
|
491
|
+
const result = { installed: [], skipped: [], errors: [] };
|
|
492
|
+
|
|
493
|
+
for (const file of SKILL_FILES) {
|
|
494
|
+
const srcPath = path.join(PACKAGE_ROOT, file.src);
|
|
495
|
+
const destPath = path.join(targetDir, file.dest);
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
if (!fs.existsSync(srcPath)) {
|
|
499
|
+
result.errors.push({ file: file.src, error: 'Source file not found' });
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const srcContent = fs.readFileSync(srcPath, 'utf8');
|
|
504
|
+
|
|
505
|
+
// Check if identical file already exists
|
|
506
|
+
if (!options.force && fs.existsSync(destPath)) {
|
|
507
|
+
const existing = fs.readFileSync(destPath, 'utf8');
|
|
508
|
+
if (existing === srcContent) {
|
|
509
|
+
result.skipped.push(file.dest);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Create directory and write file
|
|
515
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
516
|
+
fs.writeFileSync(destPath, srcContent);
|
|
517
|
+
result.installed.push(file.dest);
|
|
518
|
+
} catch (err) {
|
|
519
|
+
result.errors.push({ file: file.dest, error: err.message });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// CLI mode: node scripts/install-skills.js [targetDir] [--force]
|
|
527
|
+
if (require.main === module) {
|
|
528
|
+
const args = process.argv.slice(2);
|
|
529
|
+
const force = args.includes('--force');
|
|
530
|
+
const targetDir = args.find(a => !a.startsWith('-')) || process.cwd();
|
|
531
|
+
|
|
532
|
+
const result = installSkills(targetDir, { force });
|
|
533
|
+
|
|
534
|
+
if (result.installed.length) {
|
|
535
|
+
console.log(`Installed ${result.installed.length} A2A skill file(s):`);
|
|
536
|
+
result.installed.forEach(f => console.log(` + ${f}`));
|
|
537
|
+
}
|
|
538
|
+
if (result.skipped.length) {
|
|
539
|
+
console.log(`Skipped ${result.skipped.length} unchanged file(s)`);
|
|
540
|
+
}
|
|
541
|
+
if (result.errors.length) {
|
|
542
|
+
console.error(`Errors: ${result.errors.length}`);
|
|
543
|
+
result.errors.forEach(e => console.error(` ! ${e.file}: ${e.error}`));
|
|
544
|
+
process.exit(1);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
module.exports = { installSkills, SKILL_FILES };
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**Step 4: Run tests to verify they pass**
|
|
552
|
+
|
|
553
|
+
Run: `node test/run.js --filter install-skills`
|
|
554
|
+
Expected: 4 passing
|
|
555
|
+
|
|
556
|
+
**Step 5: Commit**
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
git add scripts/install-skills.js test/unit/install-skills.test.js
|
|
560
|
+
git commit -m "feat: add skill installer for Claude Code + Codex CLI"
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
### Task 8: `a2a skills` CLI subcommand
|
|
566
|
+
|
|
567
|
+
**Files:**
|
|
568
|
+
- Modify: `bin/cli.js` — add `skills` subcommand after existing commands
|
|
569
|
+
|
|
570
|
+
**Step 1: Add the skills subcommand to cli.js**
|
|
571
|
+
|
|
572
|
+
Find the command dispatch section in `bin/cli.js` and add a `skills` case. The command should:
|
|
573
|
+
- `a2a skills` — install skills into current project directory
|
|
574
|
+
- `a2a skills --force` — overwrite existing files
|
|
575
|
+
- `a2a skills --check` — show what would be installed without writing
|
|
576
|
+
|
|
577
|
+
```javascript
|
|
578
|
+
// Add to the command switch in bin/cli.js
|
|
579
|
+
case 'skills': {
|
|
580
|
+
const { installSkills } = require('../scripts/install-skills');
|
|
581
|
+
const check = args.includes('--check');
|
|
582
|
+
const force = args.includes('--force');
|
|
583
|
+
const targetDir = process.cwd();
|
|
584
|
+
|
|
585
|
+
if (check) {
|
|
586
|
+
const fs = require('fs');
|
|
587
|
+
const { SKILL_FILES } = require('../scripts/install-skills');
|
|
588
|
+
console.log('A2A skills for this project:\n');
|
|
589
|
+
for (const file of SKILL_FILES) {
|
|
590
|
+
const destPath = path.join(targetDir, file.dest);
|
|
591
|
+
const exists = fs.existsSync(destPath);
|
|
592
|
+
const icon = exists ? ' ✓' : ' ✗';
|
|
593
|
+
console.log(`${icon} ${file.dest}${exists ? ' (installed)' : ' (not installed)'}`);
|
|
594
|
+
}
|
|
595
|
+
console.log(`\nRun "a2a skills" to install missing files.`);
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const result = installSkills(targetDir, { force });
|
|
600
|
+
|
|
601
|
+
if (result.installed.length) {
|
|
602
|
+
console.log(`\n Installed ${result.installed.length} A2A skill file(s):\n`);
|
|
603
|
+
result.installed.forEach(f => console.log(` + ${f}`));
|
|
604
|
+
}
|
|
605
|
+
if (result.skipped.length) {
|
|
606
|
+
console.log(`\n Skipped ${result.skipped.length} unchanged file(s)`);
|
|
607
|
+
}
|
|
608
|
+
if (result.errors.length) {
|
|
609
|
+
console.error(`\n Errors:`);
|
|
610
|
+
result.errors.forEach(e => console.error(` ! ${e.file}: ${e.error}`));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (result.installed.length === 0 && result.skipped.length > 0) {
|
|
614
|
+
console.log('\n All skills already installed. Use --force to overwrite.\n');
|
|
615
|
+
} else if (result.installed.length > 0) {
|
|
616
|
+
console.log('\n Skills ready. In Claude Code, type /a2a- to see available commands.');
|
|
617
|
+
console.log(' In Codex CLI, A2A instructions are in .codex/AGENTS.md\n');
|
|
618
|
+
}
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
Also add `'skills'` to the `ONBOARDING_EXEMPT` set since it doesn't require onboarding.
|
|
624
|
+
|
|
625
|
+
**Step 2: Run full test suite**
|
|
626
|
+
|
|
627
|
+
Run: `node test/run.js`
|
|
628
|
+
Expected: All passing (276+)
|
|
629
|
+
|
|
630
|
+
**Step 3: Commit**
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
git add bin/cli.js
|
|
634
|
+
git commit -m "feat: add 'a2a skills' CLI subcommand"
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Phase 4: Postinstall Integration & Documentation
|
|
640
|
+
|
|
641
|
+
### Task 9: Wire skill install into postinstall
|
|
642
|
+
|
|
643
|
+
**Files:**
|
|
644
|
+
- Modify: `scripts/postinstall.js`
|
|
645
|
+
|
|
646
|
+
**Step 1: Add skill installation to postinstall**
|
|
647
|
+
|
|
648
|
+
After the existing quickstart logic in `postinstall.js`, add a best-effort skill copy. This should:
|
|
649
|
+
- Only run on global install (already gated by `npm_config_global`)
|
|
650
|
+
- Copy skills into the workspace directory (`INIT_CWD` or `HOME`)
|
|
651
|
+
- Fail silently (best-effort, don't block install)
|
|
652
|
+
|
|
653
|
+
Add after the quickstart `spawnSync` block:
|
|
654
|
+
|
|
655
|
+
```javascript
|
|
656
|
+
// Best-effort: install Claude Code + Codex skills into the workspace
|
|
657
|
+
try {
|
|
658
|
+
const { installSkills } = require('./install-skills');
|
|
659
|
+
installSkills(initCwd);
|
|
660
|
+
} catch (e) {
|
|
661
|
+
// Silent — skills can be installed later with `a2a skills`
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Step 2: Run full test suite**
|
|
666
|
+
|
|
667
|
+
Run: `node test/run.js`
|
|
668
|
+
Expected: All passing
|
|
669
|
+
|
|
670
|
+
**Step 3: Commit**
|
|
671
|
+
|
|
672
|
+
```bash
|
|
673
|
+
git add scripts/postinstall.js
|
|
674
|
+
git commit -m "feat: install skills on npm postinstall (best-effort)"
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
679
|
+
### Task 10: Documentation update
|
|
680
|
+
|
|
681
|
+
**Files:**
|
|
682
|
+
- Modify: `docs/protocol.md` — add "CLI Skills" section
|
|
683
|
+
|
|
684
|
+
**Step 1: Add CLI Skills section**
|
|
685
|
+
|
|
686
|
+
Insert before the "Future Protocol Extensions" section:
|
|
687
|
+
|
|
688
|
+
```markdown
|
|
689
|
+
## CLI Skills (Claude Code & Codex)
|
|
690
|
+
|
|
691
|
+
A2A ships with slash commands for Claude Code and agent instructions for Codex CLI.
|
|
692
|
+
|
|
693
|
+
### Installation
|
|
694
|
+
|
|
695
|
+
```bash
|
|
696
|
+
a2a skills # Install into current project
|
|
697
|
+
a2a skills --check # See what would be installed
|
|
698
|
+
a2a skills --force # Overwrite existing files
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Skills are also installed automatically on `npm install -g a2acalling`.
|
|
702
|
+
|
|
703
|
+
### Claude Code Commands
|
|
704
|
+
|
|
705
|
+
| Command | Description |
|
|
706
|
+
|---------|-------------|
|
|
707
|
+
| `/a2a-call <contact> <msg>` | Call another agent (multi-turn) |
|
|
708
|
+
| `/a2a-invite [name] [--tier]` | Create invite token |
|
|
709
|
+
| `/a2a-contacts [add\|show\|ping\|rm]` | Manage contacts |
|
|
710
|
+
| `/a2a-status` | Server and agent health dashboard |
|
|
711
|
+
| `/a2a-setup` | First-time setup and onboarding |
|
|
712
|
+
|
|
713
|
+
Files installed to: `.claude/commands/a2a-*.md`
|
|
714
|
+
|
|
715
|
+
### Codex CLI
|
|
716
|
+
|
|
717
|
+
A2A agent instructions are installed to `.codex/AGENTS.md`. Codex reads this file automatically to understand available A2A commands, permission tiers, and workflows.
|
|
718
|
+
|
|
719
|
+
### Manual Installation
|
|
720
|
+
|
|
721
|
+
If the automatic install didn't work, copy the files manually:
|
|
722
|
+
|
|
723
|
+
```bash
|
|
724
|
+
# Claude Code commands
|
|
725
|
+
cp node_modules/a2acalling/.claude/commands/a2a-*.md .claude/commands/
|
|
726
|
+
|
|
727
|
+
# Codex instructions
|
|
728
|
+
cp node_modules/a2acalling/.codex/AGENTS.md .codex/AGENTS.md
|
|
729
|
+
```
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Step 2: Commit**
|
|
733
|
+
|
|
734
|
+
```bash
|
|
735
|
+
git add docs/protocol.md
|
|
736
|
+
git commit -m "docs: add CLI skills section to protocol.md"
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
741
|
+
## Verification Checklist
|
|
742
|
+
|
|
743
|
+
After all tasks, verify:
|
|
744
|
+
|
|
745
|
+
1. `node test/run.js` — all tests pass
|
|
746
|
+
2. `node test/run.js --filter install-skills` — 4 skill installer tests pass
|
|
747
|
+
3. `a2a skills --check` — lists all skill files
|
|
748
|
+
4. `a2a skills` in a temp dir — installs all files
|
|
749
|
+
5. `ls .claude/commands/a2a-*.md` — 5 command files exist
|
|
750
|
+
6. `cat .codex/AGENTS.md` — Codex instructions present
|
|
751
|
+
7. `node test/run.js --e2e` — E2E tests still pass
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## Task Summary
|
|
756
|
+
|
|
757
|
+
| # | Task | Phase | Files | Est. |
|
|
758
|
+
|---|------|-------|-------|------|
|
|
759
|
+
| 1 | `/a2a-call` command | 1: Claude Code | 1 new | 3 min |
|
|
760
|
+
| 2 | `/a2a-invite` command | 1: Claude Code | 1 new | 3 min |
|
|
761
|
+
| 3 | `/a2a-contacts` command | 1: Claude Code | 1 new | 3 min |
|
|
762
|
+
| 4 | `/a2a-status` command | 1: Claude Code | 1 new | 3 min |
|
|
763
|
+
| 5 | `/a2a-setup` command | 1: Claude Code | 1 new | 3 min |
|
|
764
|
+
| 6 | Codex AGENTS.md | 2: Codex | 1 new | 5 min |
|
|
765
|
+
| 7 | Skill installer + tests | 3: Integration | 2 new | 10 min |
|
|
766
|
+
| 8 | `a2a skills` subcommand | 3: Integration | 1 mod | 5 min |
|
|
767
|
+
| 9 | Postinstall wiring | 4: Polish | 1 mod | 3 min |
|
|
768
|
+
| 10 | Protocol docs update | 4: Polish | 1 mod | 3 min |
|
|
769
|
+
|
|
770
|
+
**Total: 8 new files, 3 modified, 10 tasks**
|