a2acalling 0.6.54 → 0.6.56
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/CLAUDE-INSTALL.md +2 -0
- package/SKILL.md +91 -0
- package/bin/cli.js +69 -0
- package/package.json +1 -1
- package/scripts/install-skills.js +123 -11
- package/scripts/postinstall.js +45 -5
package/CLAUDE-INSTALL.md
CHANGED
package/SKILL.md
CHANGED
|
@@ -27,6 +27,97 @@ metadata:
|
|
|
27
27
|
|
|
28
28
|
Enable agent-to-agent communication across OpenClaw instances.
|
|
29
29
|
|
|
30
|
+
## AFTER INSTALL — START HERE
|
|
31
|
+
|
|
32
|
+
**Just installed `a2acalling`? Here's what to do next:**
|
|
33
|
+
|
|
34
|
+
1. **Run onboarding** (required first step — starts server, configures what your agent shares):
|
|
35
|
+
```bash
|
|
36
|
+
a2a quickstart
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. **Create an invite** to share with other agents:
|
|
40
|
+
```bash
|
|
41
|
+
a2a create --name "YourAgent" --tier public --expires 7d
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
3. **Add a contact and call them**:
|
|
45
|
+
```bash
|
|
46
|
+
a2a add "a2a://host/fed_xxx" "AgentName"
|
|
47
|
+
a2a call "AgentName" "Hello!"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Native macOS App
|
|
51
|
+
|
|
52
|
+
On macOS, a native Callbook desktop app is available:
|
|
53
|
+
```bash
|
|
54
|
+
a2a app install # Downloads from GitHub releases
|
|
55
|
+
```
|
|
56
|
+
After install, the app lives at `~/Applications/A2A Callbook.app`. Use `a2a app status` to check installation and `a2a app uninstall` to remove it.
|
|
57
|
+
|
|
58
|
+
### Full CLI Reference
|
|
59
|
+
|
|
60
|
+
**Onboarding & Setup:**
|
|
61
|
+
| Command | Description |
|
|
62
|
+
|---------|-------------|
|
|
63
|
+
| `a2a quickstart` | First-time setup — port, hostname, disclosure topics |
|
|
64
|
+
| `a2a quickstart --force` | Re-run onboarding from scratch |
|
|
65
|
+
| `a2a quickstart --hostname DOMAIN:443 --port 3001` | Setup with public hostname |
|
|
66
|
+
| `a2a setup` | Auto setup (gateway-aware dashboard install) |
|
|
67
|
+
| `a2a version` | Show installed version |
|
|
68
|
+
|
|
69
|
+
**Tokens & Invites:**
|
|
70
|
+
| Command | Description |
|
|
71
|
+
|---------|-------------|
|
|
72
|
+
| `a2a create --name NAME --tier TIER --expires DURATION` | Create invite token |
|
|
73
|
+
| `a2a list` | List active tokens |
|
|
74
|
+
| `a2a revoke <id>` | Revoke a token |
|
|
75
|
+
|
|
76
|
+
Token options: `--name/-n`, `--tier/-p` (public/friends/family), `--expires/-e` (1h/1d/7d/30d/never), `--disclosure/-d` (public/minimal/none), `--notify` (all/summary/none)
|
|
77
|
+
|
|
78
|
+
**Contacts & Calling:**
|
|
79
|
+
| Command | Description |
|
|
80
|
+
|---------|-------------|
|
|
81
|
+
| `a2a add <url> [name]` | Add contact from invite URL |
|
|
82
|
+
| `a2a contacts` | List all contacts |
|
|
83
|
+
| `a2a call <contact> <msg>` | Multi-turn call (8-25 turns) |
|
|
84
|
+
| `a2a call <contact> <msg> --single` | One-shot call |
|
|
85
|
+
| `a2a ping <url>` | Check if agent is reachable |
|
|
86
|
+
|
|
87
|
+
**Dashboard & GUI:**
|
|
88
|
+
| Command | Description |
|
|
89
|
+
|---------|-------------|
|
|
90
|
+
| `a2a gui` | Open dashboard in browser |
|
|
91
|
+
| `a2a gui --tab logs` | Open specific tab (contacts/calls/logs/settings/invites) |
|
|
92
|
+
|
|
93
|
+
**Server Management:**
|
|
94
|
+
| Command | Description |
|
|
95
|
+
|---------|-------------|
|
|
96
|
+
| `a2a server --port 3001` | Start server manually |
|
|
97
|
+
| `a2a update` | Update to latest version |
|
|
98
|
+
| `a2a update --check` | Check for updates without installing |
|
|
99
|
+
| `a2a uninstall` | Stop server and remove config |
|
|
100
|
+
| `a2a skills` | Install Claude Code + Codex skill files |
|
|
101
|
+
|
|
102
|
+
**Native App (macOS only):**
|
|
103
|
+
| Command | Description |
|
|
104
|
+
|---------|-------------|
|
|
105
|
+
| `a2a app status` | Check native app installation |
|
|
106
|
+
| `a2a app install` | Install/update from GitHub releases |
|
|
107
|
+
| `a2a app install --force` | Reinstall even if current |
|
|
108
|
+
| `a2a app uninstall` | Remove from ~/Applications |
|
|
109
|
+
|
|
110
|
+
### Claude Code Slash Commands
|
|
111
|
+
|
|
112
|
+
These are available after install:
|
|
113
|
+
- `/a2a-setup` — Run onboarding or reset configuration
|
|
114
|
+
- `/a2a-call <contact> <message>` — Call another A2A agent
|
|
115
|
+
- `/a2a-invite [name] [--tier]` — Create and share an invite token
|
|
116
|
+
- `/a2a-contacts` — List and manage contacts
|
|
117
|
+
- `/a2a-status` — Check server and agent health
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
30
121
|
## Install & Onboarding
|
|
31
122
|
|
|
32
123
|
```bash
|
package/bin/cli.js
CHANGED
|
@@ -2711,6 +2711,75 @@ a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'f
|
|
|
2711
2711
|
console.log('Removing database... ⏭️');
|
|
2712
2712
|
}
|
|
2713
2713
|
|
|
2714
|
+
// ── Remove installed skill files using the manifest ──────────────────
|
|
2715
|
+
//
|
|
2716
|
+
// The postinstall script writes .a2a-manifest.json listing every file
|
|
2717
|
+
// it installed (CLAUDE.md section, slash commands, skill reference, etc.).
|
|
2718
|
+
// We read the manifest to remove exactly the files we installed, avoiding
|
|
2719
|
+
// accidental deletion of user files. For CLAUDE.md we only remove the
|
|
2720
|
+
// A2A section (bounded by markers), not the entire file.
|
|
2721
|
+
const manifestCwd = process.env.INIT_CWD || process.cwd();
|
|
2722
|
+
const manifestPath = path.join(manifestCwd, '.a2a-manifest.json');
|
|
2723
|
+
if (fs.existsSync(manifestPath)) {
|
|
2724
|
+
process.stdout.write('Removing installed skill files... ');
|
|
2725
|
+
try {
|
|
2726
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
2727
|
+
let skillOk = true;
|
|
2728
|
+
for (const entry of (manifest.files || [])) {
|
|
2729
|
+
const filePath = path.join(manifestCwd, entry.path);
|
|
2730
|
+
// Skip the manifest itself (we remove it last) and non-existent files
|
|
2731
|
+
if (entry.path === '.a2a-manifest.json') continue;
|
|
2732
|
+
if (!fs.existsSync(filePath)) continue;
|
|
2733
|
+
|
|
2734
|
+
// For CLAUDE.md: only remove the A2A section, not the whole file.
|
|
2735
|
+
// The user may have their own project-specific content in CLAUDE.md.
|
|
2736
|
+
if (entry.path === 'CLAUDE.md') {
|
|
2737
|
+
try {
|
|
2738
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
2739
|
+
const mergeKey = '# A2A Calling';
|
|
2740
|
+
const endMarker = '<!-- END A2A CALLING SECTION -->';
|
|
2741
|
+
if (content.includes(mergeKey)) {
|
|
2742
|
+
const start = content.indexOf(mergeKey);
|
|
2743
|
+
const endIdx = content.indexOf(endMarker, start);
|
|
2744
|
+
let cleaned;
|
|
2745
|
+
if (endIdx !== -1) {
|
|
2746
|
+
// Remove the bounded A2A section (header through end marker)
|
|
2747
|
+
const before = content.slice(0, start).trimEnd();
|
|
2748
|
+
const after = content.slice(endIdx + endMarker.length).trimStart();
|
|
2749
|
+
cleaned = before + (after ? '\n\n' + after : '');
|
|
2750
|
+
} else {
|
|
2751
|
+
// Legacy: no end marker — remove from header to EOF
|
|
2752
|
+
cleaned = content.slice(0, start).trimEnd();
|
|
2753
|
+
}
|
|
2754
|
+
if (cleaned.trim()) {
|
|
2755
|
+
fs.writeFileSync(filePath, cleaned.trimEnd() + '\n');
|
|
2756
|
+
} else {
|
|
2757
|
+
// CLAUDE.md was entirely A2A content — remove the file
|
|
2758
|
+
fs.rmSync(filePath, { force: true });
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
} catch (e) {
|
|
2762
|
+
skillOk = false;
|
|
2763
|
+
}
|
|
2764
|
+
continue;
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
// For all other files: remove them entirely
|
|
2768
|
+
try {
|
|
2769
|
+
fs.rmSync(filePath, { force: true });
|
|
2770
|
+
} catch (e) {
|
|
2771
|
+
skillOk = false;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
// Remove the manifest file itself last
|
|
2775
|
+
fs.rmSync(manifestPath, { force: true });
|
|
2776
|
+
console.log(skillOk ? '✅' : '⚠️');
|
|
2777
|
+
} catch (err) {
|
|
2778
|
+
console.log('⚠️');
|
|
2779
|
+
console.error(` Could not read manifest: ${err.message}`);
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2714
2783
|
// Remove native macOS app if present
|
|
2715
2784
|
if (os.platform() === 'darwin') {
|
|
2716
2785
|
const appCandidates = [
|
package/package.json
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A2A Skill Installer
|
|
3
3
|
*
|
|
4
|
-
* Copies Claude Code commands, CLAUDE.md context,
|
|
5
|
-
* target project directory. Idempotent: skips files
|
|
6
|
-
* identical content.
|
|
4
|
+
* Copies Claude Code commands, CLAUDE.md context, SKILL.md reference, and
|
|
5
|
+
* Codex AGENTS.md into a target project directory. Idempotent: skips files
|
|
6
|
+
* that already exist with identical content.
|
|
7
7
|
*
|
|
8
8
|
* CLAUDE.md is the key file — Claude Code reads it automatically, giving the
|
|
9
9
|
* agent full context about the a2a CLI, native app, and onboarding flow
|
|
10
10
|
* immediately after npm install.
|
|
11
|
+
*
|
|
12
|
+
* SKILL.md is the deep reference for A2A — invite formatting templates,
|
|
13
|
+
* incoming call handling, disclosure manifest flow, and protocol details.
|
|
14
|
+
* We copy it to .claude/ so Claude Code can discover it naturally without
|
|
15
|
+
* having to grep through node_modules. The .claude/ directory is already
|
|
16
|
+
* used for slash commands, so this is a natural home for reference docs.
|
|
17
|
+
*
|
|
18
|
+
* Unlike CLAUDE.md (which is auto-loaded), the skill reference file is only
|
|
19
|
+
* read when the agent explicitly looks in .claude/ — so it serves as opt-in
|
|
20
|
+
* deep reference rather than always-loaded context (avoiding token bloat).
|
|
11
21
|
*/
|
|
12
22
|
|
|
13
23
|
const fs = require('fs');
|
|
@@ -15,9 +25,18 @@ const path = require('path');
|
|
|
15
25
|
|
|
16
26
|
const PACKAGE_ROOT = path.join(__dirname, '..');
|
|
17
27
|
|
|
28
|
+
// Section delimiter used to bound the A2A block inside CLAUDE.md.
|
|
29
|
+
// This allows safe replacement of ONLY the A2A content when updating,
|
|
30
|
+
// preserving any user content before or after the A2A section.
|
|
31
|
+
const A2A_SECTION_END_MARKER = '<!-- END A2A CALLING SECTION -->';
|
|
32
|
+
|
|
18
33
|
const SKILL_FILES = [
|
|
19
34
|
// CLAUDE.md — gives Claude Code instant context about the a2a CLI
|
|
20
35
|
{ src: 'CLAUDE-INSTALL.md', dest: 'CLAUDE.md', mergeKey: '# A2A Calling' },
|
|
36
|
+
// SKILL.md — deep reference for A2A (invite formatting, call handling, etc.)
|
|
37
|
+
// Copied to .claude/ so Claude Code discovers it naturally without grepping
|
|
38
|
+
// node_modules. This is opt-in context: only loaded when the agent looks.
|
|
39
|
+
{ src: 'SKILL.md', dest: '.claude/a2a-skill-reference.md' },
|
|
21
40
|
// Claude Code slash commands
|
|
22
41
|
{ src: '.claude/commands/a2a-call.md', dest: '.claude/commands/a2a-call.md' },
|
|
23
42
|
{ src: '.claude/commands/a2a-invite.md', dest: '.claude/commands/a2a-invite.md' },
|
|
@@ -31,6 +50,20 @@ const SKILL_FILES = [
|
|
|
31
50
|
function installSkills(targetDir, options = {}) {
|
|
32
51
|
const result = { installed: [], skipped: [], errors: [] };
|
|
33
52
|
|
|
53
|
+
// Install manifest: .a2a-manifest.json
|
|
54
|
+
//
|
|
55
|
+
// After installing skill files, we write a manifest recording every file
|
|
56
|
+
// we touched, what action was taken, and the package version. This serves
|
|
57
|
+
// three purposes:
|
|
58
|
+
// 1. Transparency — user can see exactly what the package added
|
|
59
|
+
// 2. Clean uninstall — `a2a uninstall` reads the manifest to remove files
|
|
60
|
+
// 3. Upgrade tracking — on re-install, we can compare versions and
|
|
61
|
+
// only update files that actually changed
|
|
62
|
+
//
|
|
63
|
+
// The manifest is written to the project root (same level as CLAUDE.md)
|
|
64
|
+
// so it's easy to find.
|
|
65
|
+
const manifestEntries = [];
|
|
66
|
+
|
|
34
67
|
for (const file of SKILL_FILES) {
|
|
35
68
|
const srcPath = path.join(PACKAGE_ROOT, file.src);
|
|
36
69
|
const destPath = path.join(targetDir, file.dest);
|
|
@@ -38,6 +71,7 @@ function installSkills(targetDir, options = {}) {
|
|
|
38
71
|
try {
|
|
39
72
|
if (!fs.existsSync(srcPath)) {
|
|
40
73
|
result.errors.push({ file: file.src, error: 'Source file not found' });
|
|
74
|
+
manifestEntries.push({ path: file.dest, action: 'error', detail: 'Source file not found' });
|
|
41
75
|
continue;
|
|
42
76
|
}
|
|
43
77
|
|
|
@@ -46,47 +80,125 @@ function installSkills(targetDir, options = {}) {
|
|
|
46
80
|
if (fs.existsSync(destPath)) {
|
|
47
81
|
const existing = fs.readFileSync(destPath, 'utf8');
|
|
48
82
|
|
|
49
|
-
//
|
|
50
|
-
//
|
|
83
|
+
// ── CLAUDE.md merge strategy ──────────────────────────────────────
|
|
84
|
+
//
|
|
85
|
+
// The a2acalling package installs context into the project's CLAUDE.md so
|
|
86
|
+
// Claude Code has immediate awareness of the CLI, commands, and native app.
|
|
87
|
+
// But projects often have their own CLAUDE.md with project-specific instructions.
|
|
88
|
+
//
|
|
89
|
+
// To avoid clobbering user content, we use a section-delimited merge:
|
|
90
|
+
// 1. If CLAUDE.md doesn't exist: write CLAUDE-INSTALL.md as-is
|
|
91
|
+
// 2. If CLAUDE.md exists WITHOUT an A2A section: append at the end
|
|
92
|
+
// 3. If CLAUDE.md exists WITH an A2A section: replace only between
|
|
93
|
+
// "# A2A Calling" and "<!-- END A2A CALLING SECTION -->"
|
|
94
|
+
//
|
|
95
|
+
// The end marker ensures user content after the A2A block is preserved.
|
|
96
|
+
// Legacy installs without the marker fall back to replace-to-EOF (old behavior)
|
|
97
|
+
// but the marker is added during the update so future updates are safe.
|
|
51
98
|
if (file.mergeKey) {
|
|
52
99
|
if (existing.includes(file.mergeKey)) {
|
|
53
|
-
// A2A section already present —
|
|
100
|
+
// A2A section already present — find its boundaries
|
|
54
101
|
const sectionStart = existing.indexOf(file.mergeKey);
|
|
55
|
-
|
|
102
|
+
|
|
103
|
+
// Check if the end marker exists to determine section boundaries.
|
|
104
|
+
// If found, we only replace the bounded section, preserving any
|
|
105
|
+
// user content after the marker. If not found (legacy installs
|
|
106
|
+
// that predate the marker), we replace from the header to EOF
|
|
107
|
+
// and add the marker so future updates are safe.
|
|
108
|
+
const endMarkerIndex = existing.indexOf(A2A_SECTION_END_MARKER, sectionStart);
|
|
109
|
+
|
|
110
|
+
let existingSection;
|
|
111
|
+
let after = '';
|
|
112
|
+
|
|
113
|
+
if (endMarkerIndex !== -1) {
|
|
114
|
+
// End marker found — extract only the bounded A2A section
|
|
115
|
+
const sectionEnd = endMarkerIndex + A2A_SECTION_END_MARKER.length;
|
|
116
|
+
existingSection = existing.slice(sectionStart, sectionEnd);
|
|
117
|
+
// Preserve everything after the end marker (user's trailing content)
|
|
118
|
+
after = existing.slice(sectionEnd);
|
|
119
|
+
} else {
|
|
120
|
+
// Legacy install without end marker — take everything from header to EOF
|
|
121
|
+
// The new content includes the marker, so future updates will be safe
|
|
122
|
+
existingSection = existing.slice(sectionStart);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Compare the existing A2A section with the new source content.
|
|
126
|
+
// Skip the update if they're identical (idempotent behavior).
|
|
56
127
|
if (!options.force && existingSection.trim() === srcContent.trim()) {
|
|
57
128
|
result.skipped.push(file.dest);
|
|
129
|
+
manifestEntries.push({ path: file.dest, action: 'skipped' });
|
|
58
130
|
continue;
|
|
59
131
|
}
|
|
60
|
-
|
|
132
|
+
|
|
133
|
+
// Replace only the A2A section, preserving content before and after
|
|
61
134
|
const before = existing.slice(0, sectionStart).trimEnd();
|
|
62
|
-
|
|
135
|
+
let merged = before ? before + '\n\n' + srcContent : srcContent;
|
|
136
|
+
// Re-attach any trailing content that was after the end marker
|
|
137
|
+
if (after) {
|
|
138
|
+
merged = merged.trimEnd() + '\n' + after;
|
|
139
|
+
}
|
|
63
140
|
fs.writeFileSync(destPath, merged);
|
|
64
141
|
result.installed.push(file.dest + ' (updated A2A section)');
|
|
142
|
+
manifestEntries.push({ path: file.dest, action: 'updated A2A section' });
|
|
65
143
|
} else {
|
|
66
|
-
// Existing CLAUDE.md without A2A section — append
|
|
144
|
+
// Existing CLAUDE.md without A2A section — append at the end
|
|
67
145
|
const merged = existing.trimEnd() + '\n\n' + srcContent;
|
|
68
146
|
fs.writeFileSync(destPath, merged);
|
|
69
147
|
result.installed.push(file.dest + ' (appended A2A section)');
|
|
148
|
+
manifestEntries.push({ path: file.dest, action: 'appended A2A section' });
|
|
70
149
|
}
|
|
71
150
|
continue;
|
|
72
151
|
}
|
|
73
152
|
|
|
74
|
-
// Standard mode: skip if identical
|
|
153
|
+
// Standard mode: skip if identical (non-merge files)
|
|
75
154
|
if (!options.force && existing === srcContent) {
|
|
76
155
|
result.skipped.push(file.dest);
|
|
156
|
+
manifestEntries.push({ path: file.dest, action: 'skipped' });
|
|
77
157
|
continue;
|
|
78
158
|
}
|
|
159
|
+
} else {
|
|
160
|
+
// File doesn't exist yet — will be created below
|
|
79
161
|
}
|
|
80
162
|
|
|
81
163
|
// Create directory and write file
|
|
82
164
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
83
165
|
fs.writeFileSync(destPath, srcContent);
|
|
84
166
|
result.installed.push(file.dest);
|
|
167
|
+
manifestEntries.push({ path: file.dest, action: 'created' });
|
|
85
168
|
} catch (err) {
|
|
86
169
|
result.errors.push({ file: file.dest, error: err.message });
|
|
170
|
+
manifestEntries.push({ path: file.dest, action: 'error', detail: err.message });
|
|
87
171
|
}
|
|
88
172
|
}
|
|
89
173
|
|
|
174
|
+
// ── Write install manifest ────────────────────────────────────────────
|
|
175
|
+
//
|
|
176
|
+
// The manifest records every file we touched so the user (or `a2a uninstall`)
|
|
177
|
+
// knows exactly what was installed. We include the manifest itself in the list
|
|
178
|
+
// for completeness.
|
|
179
|
+
try {
|
|
180
|
+
const pkg = require('../package.json');
|
|
181
|
+
const manifestPath = path.join(targetDir, '.a2a-manifest.json');
|
|
182
|
+
|
|
183
|
+
// Add the manifest file itself to the entries list
|
|
184
|
+
manifestEntries.push({ path: '.a2a-manifest.json', action: 'created' });
|
|
185
|
+
// Add the install log file (written by postinstall.js)
|
|
186
|
+
manifestEntries.push({ path: '.a2a-install.log', action: 'created' });
|
|
187
|
+
|
|
188
|
+
const manifest = {
|
|
189
|
+
version: pkg.version,
|
|
190
|
+
installed_at: new Date().toISOString(),
|
|
191
|
+
files: manifestEntries,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
195
|
+
} catch (err) {
|
|
196
|
+
// Non-fatal — the manifest is a convenience for cleanup, not required
|
|
197
|
+
// for the skill files to work. Failure here (e.g., read-only dir)
|
|
198
|
+
// should not break the install.
|
|
199
|
+
result.errors.push({ file: '.a2a-manifest.json', error: err.message });
|
|
200
|
+
}
|
|
201
|
+
|
|
90
202
|
return result;
|
|
91
203
|
}
|
|
92
204
|
|
package/scripts/postinstall.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) process.exit(0);
|
|
24
24
|
if (process.env.DOCKER) process.exit(0);
|
|
25
25
|
|
|
26
|
+
const fs = require('fs');
|
|
26
27
|
const path = require('path');
|
|
27
28
|
const os = require('os');
|
|
28
29
|
const { spawnSync } = require('child_process');
|
|
@@ -70,6 +71,22 @@ function printGettingStarted() {
|
|
|
70
71
|
const isMac = os.platform() === 'darwin';
|
|
71
72
|
const pkg = require('../package.json');
|
|
72
73
|
|
|
74
|
+
// ── Short critical stderr output ──────────────────────────────────────
|
|
75
|
+
//
|
|
76
|
+
// npm v7+ suppresses stdout/stderr from postinstall scripts unless
|
|
77
|
+
// --foreground-scripts is used. We print a minimal critical line to stderr
|
|
78
|
+
// (which npm sometimes still shows) AND write the full banner to a log file
|
|
79
|
+
// so that Claude Code or a human can always find the getting-started info.
|
|
80
|
+
const shortLines = [
|
|
81
|
+
`a2acalling v${pkg.version} installed.`,
|
|
82
|
+
'Next step: a2a quickstart',
|
|
83
|
+
'Skills installed: /a2a-setup, /a2a-call, /a2a-contacts, /a2a-invite, /a2a-status',
|
|
84
|
+
];
|
|
85
|
+
// Print the short critical summary to stderr — this is the most likely
|
|
86
|
+
// output to survive npm's output suppression in v7+
|
|
87
|
+
console.error(shortLines.join('\n'));
|
|
88
|
+
|
|
89
|
+
// ── Full verbose banner ───────────────────────────────────────────────
|
|
73
90
|
const lines = [
|
|
74
91
|
'',
|
|
75
92
|
'╔══════════════════════════════════════════════════════════════╗',
|
|
@@ -176,9 +193,32 @@ function printGettingStarted() {
|
|
|
176
193
|
'',
|
|
177
194
|
);
|
|
178
195
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
console.log(
|
|
196
|
+
const fullBanner = lines.join('\n');
|
|
197
|
+
|
|
198
|
+
// Print the full banner to stdout for contexts where it's visible
|
|
199
|
+
// (e.g., --foreground-scripts, direct script execution, agent contexts)
|
|
200
|
+
console.log(fullBanner);
|
|
201
|
+
|
|
202
|
+
// ── Write full banner to a log file ─────────────────────────────────
|
|
203
|
+
//
|
|
204
|
+
// Even when npm suppresses terminal output, the agent or human can read
|
|
205
|
+
// this file to get the full getting-started info. We write to the project
|
|
206
|
+
// root (initCwd) so it's next to CLAUDE.md and easy to discover.
|
|
207
|
+
try {
|
|
208
|
+
const logPath = path.join(initCwd, '.a2a-install.log');
|
|
209
|
+
const logContent = [
|
|
210
|
+
`a2acalling v${pkg.version} — install summary`,
|
|
211
|
+
`Installed at: ${new Date().toISOString()}`,
|
|
212
|
+
`Target directory: ${initCwd}`,
|
|
213
|
+
`Global install: ${isGlobal}`,
|
|
214
|
+
'',
|
|
215
|
+
...shortLines,
|
|
216
|
+
'',
|
|
217
|
+
fullBanner,
|
|
218
|
+
].join('\n');
|
|
219
|
+
fs.writeFileSync(logPath, logContent);
|
|
220
|
+
} catch (e) {
|
|
221
|
+
// Non-fatal — the log file is a convenience, not a requirement.
|
|
222
|
+
// This can fail if the target directory is read-only (e.g., system dirs).
|
|
223
|
+
}
|
|
184
224
|
}
|