aawp-ai 1.6.0 → 1.6.2
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/install.js +97 -108
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -8,7 +8,8 @@ const { execSync, spawnSync } = require('child_process');
|
|
|
8
8
|
|
|
9
9
|
const VERSION = require('../package.json').version;
|
|
10
10
|
const SKILL_NAME = 'aawp';
|
|
11
|
-
const
|
|
11
|
+
const REPO_URL = 'https://github.com/aawp-ai/aawp.git';
|
|
12
|
+
const RAW_BASE = 'https://raw.githubusercontent.com/aawp-ai/aawp/main';
|
|
12
13
|
const FALLBACK = 'https://aawp.ai/skill';
|
|
13
14
|
|
|
14
15
|
// ── ANSI colors ───────────────────────────────────────────────────────────────
|
|
@@ -26,96 +27,82 @@ const success = s => console.log(` ${green('✓')} ${s}`);
|
|
|
26
27
|
const warn = s => console.log(` ${yellow('!')} ${s}`);
|
|
27
28
|
const fail = s => console.log(` ${red('✗')} ${s}`);
|
|
28
29
|
|
|
29
|
-
// ──
|
|
30
|
-
|
|
30
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
31
|
+
const HOME = os.homedir();
|
|
32
|
+
|
|
33
|
+
function hasCmd(cmd) {
|
|
34
|
+
try { execSync(`command -v ${cmd}`, { stdio: 'ignore' }); return true; } catch { return false; }
|
|
35
|
+
}
|
|
36
|
+
function dirExists(p) {
|
|
37
|
+
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
38
|
+
}
|
|
39
|
+
function hasGit() { return hasCmd('git'); }
|
|
40
|
+
|
|
41
|
+
async function fetchBuffer(url) {
|
|
31
42
|
if (typeof fetch !== 'undefined') {
|
|
32
43
|
const res = await fetch(url);
|
|
33
44
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
34
|
-
return res.
|
|
45
|
+
return Buffer.from(await res.arrayBuffer());
|
|
35
46
|
}
|
|
36
47
|
return new Promise((resolve, reject) => {
|
|
37
48
|
const proto = url.startsWith('https') ? require('https') : require('http');
|
|
38
49
|
proto.get(url, res => {
|
|
39
50
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
40
|
-
return
|
|
51
|
+
return fetchBuffer(res.headers.location).then(resolve).catch(reject);
|
|
41
52
|
}
|
|
42
|
-
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.
|
|
53
|
+
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.status}`));
|
|
43
54
|
const chunks = [];
|
|
44
55
|
res.on('data', c => chunks.push(c));
|
|
45
|
-
res.on('end', () => resolve(Buffer.concat(chunks)
|
|
56
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
46
57
|
res.on('error', reject);
|
|
47
58
|
}).on('error', reject);
|
|
48
59
|
});
|
|
49
60
|
}
|
|
50
61
|
|
|
51
|
-
|
|
62
|
+
// ── Git clone (full install) ──────────────────────────────────────────────────
|
|
63
|
+
function gitClone(dest) {
|
|
64
|
+
if (dirExists(dest) && dirExists(path.join(dest, '.git'))) {
|
|
65
|
+
info('Existing install found — pulling latest...');
|
|
66
|
+
const r = spawnSync('git', ['-C', dest, 'pull', '--ff-only'], { stdio: 'inherit' });
|
|
67
|
+
return r.status === 0;
|
|
68
|
+
}
|
|
69
|
+
if (dirExists(dest)) {
|
|
70
|
+
// Directory exists but not a git repo — remove and re-clone
|
|
71
|
+
warn(`${dest} exists but is not a git repo — replacing...`);
|
|
72
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
75
|
+
const r = spawnSync('git', ['clone', '--depth', '1', REPO_URL, dest], { stdio: 'inherit' });
|
|
76
|
+
return r.status === 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Manifest-only fallback (no git) ──────────────────────────────────────────
|
|
80
|
+
async function manifestFallback(dest) {
|
|
81
|
+
info('git not available — installing SKILL.md manifest only');
|
|
82
|
+
warn('Full functionality requires git. Install git and re-run: npx aawp-ai');
|
|
83
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
52
84
|
try {
|
|
53
|
-
|
|
85
|
+
const md = await fetchBuffer(`${RAW_BASE}/SKILL.md`);
|
|
86
|
+
fs.writeFileSync(path.join(dest, 'SKILL.md'), md);
|
|
54
87
|
} catch {
|
|
55
|
-
|
|
88
|
+
const md = await fetchBuffer(`${FALLBACK}/SKILL.md`);
|
|
89
|
+
fs.writeFileSync(path.join(dest, 'SKILL.md'), md);
|
|
56
90
|
}
|
|
91
|
+
return true;
|
|
57
92
|
}
|
|
58
93
|
|
|
59
94
|
// ── Client detection ──────────────────────────────────────────────────────────
|
|
60
|
-
const HOME = os.homedir();
|
|
61
|
-
|
|
62
|
-
function hasCmd(cmd) {
|
|
63
|
-
try { execSync(`command -v ${cmd}`, { stdio: 'ignore' }); return true; } catch { return false; }
|
|
64
|
-
}
|
|
65
|
-
function dirExists(p) {
|
|
66
|
-
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
95
|
const CLIENTS = [
|
|
70
|
-
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (dirExists(dest)) { warn(`${dest} already exists — skipping git clone`); return true; }
|
|
77
|
-
const r = spawnSync('git', ['clone', '--depth', '1', 'https://github.com/aawp-ai/aawp.git', dest], { stdio: 'inherit' });
|
|
78
|
-
return r.status === 0;
|
|
79
|
-
},
|
|
80
|
-
skillDir: null,
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: 'Cursor',
|
|
84
|
-
detect: () => hasCmd('cursor') || dirExists(path.join(HOME, '.cursor')),
|
|
85
|
-
skillDir: path.join(HOME, '.cursor', 'skills'),
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'Claude Code',
|
|
89
|
-
detect: () => hasCmd('claude') || dirExists(path.join(HOME, '.claude')),
|
|
90
|
-
skillDir: path.join(HOME, '.claude', 'skills'),
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
name: 'Gemini CLI',
|
|
94
|
-
detect: () => hasCmd('gemini') || dirExists(path.join(HOME, '.gemini')),
|
|
95
|
-
skillDir: path.join(HOME, '.gemini', 'skills'),
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: 'OpenCode',
|
|
99
|
-
detect: () => hasCmd('opencode') || dirExists(path.join(HOME, '.config', 'opencode')),
|
|
100
|
-
skillDir: path.join(HOME, '.config', 'opencode', 'skills'),
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: 'Goose',
|
|
104
|
-
detect: () => hasCmd('goose') || dirExists(path.join(HOME, '.config', 'goose')),
|
|
105
|
-
skillDir: path.join(HOME, '.config', 'goose', 'skills'),
|
|
106
|
-
},
|
|
96
|
+
{ name: 'OpenClaw', detect: () => hasCmd('clawhub') || hasCmd('openclaw') || dirExists('skills'), skillDir: 'skills' },
|
|
97
|
+
{ name: 'Cursor', detect: () => hasCmd('cursor') || dirExists(path.join(HOME, '.cursor')), skillDir: path.join(HOME, '.cursor', 'skills') },
|
|
98
|
+
{ name: 'Claude Code',detect: () => hasCmd('claude') || dirExists(path.join(HOME, '.claude')), skillDir: path.join(HOME, '.claude', 'skills') },
|
|
99
|
+
{ name: 'Gemini CLI', detect: () => hasCmd('gemini') || dirExists(path.join(HOME, '.gemini')), skillDir: path.join(HOME, '.gemini', 'skills') },
|
|
100
|
+
{ name: 'OpenCode', detect: () => hasCmd('opencode') || dirExists(path.join(HOME, '.config', 'opencode')), skillDir: path.join(HOME, '.config', 'opencode', 'skills') },
|
|
101
|
+
{ name: 'Goose', detect: () => hasCmd('goose') || dirExists(path.join(HOME, '.config', 'goose')), skillDir: path.join(HOME, '.config', 'goose', 'skills') },
|
|
107
102
|
];
|
|
108
103
|
|
|
109
104
|
const UNIVERSAL_DIR = path.join(HOME, '.agents', 'skills');
|
|
110
105
|
|
|
111
|
-
// ── Install to dir ────────────────────────────────────────────────────────────
|
|
112
|
-
function installToDir(baseDir, skillMd) {
|
|
113
|
-
const dest = path.join(baseDir, SKILL_NAME);
|
|
114
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
115
|
-
fs.writeFileSync(path.join(dest, 'SKILL.md'), skillMd, 'utf8');
|
|
116
|
-
return dest;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
106
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
120
107
|
async function main() {
|
|
121
108
|
console.log('');
|
|
@@ -124,78 +111,80 @@ async function main() {
|
|
|
124
111
|
console.log('');
|
|
125
112
|
|
|
126
113
|
const detected = CLIENTS.filter(c => c.detect());
|
|
127
|
-
const
|
|
114
|
+
const git = hasGit();
|
|
128
115
|
|
|
129
116
|
if (detected.length === 0) {
|
|
130
117
|
info('No AI clients detected — installing to universal ~/.agents/skills/');
|
|
131
118
|
} else {
|
|
132
|
-
info(`Detected: ${
|
|
133
|
-
}
|
|
134
|
-
console.log('');
|
|
135
|
-
|
|
136
|
-
info('Downloading SKILL.md...');
|
|
137
|
-
let skillMd;
|
|
138
|
-
try {
|
|
139
|
-
skillMd = await downloadSkillMd();
|
|
140
|
-
success('SKILL.md fetched');
|
|
141
|
-
} catch (e) {
|
|
142
|
-
fail(`Failed to download SKILL.md: ${e.message}`);
|
|
143
|
-
process.exit(1);
|
|
119
|
+
info(`Detected: ${detected.map(c => c.name).join(', ')}`);
|
|
144
120
|
}
|
|
121
|
+
if (!git) warn('git not found — will install manifest only (limited functionality)');
|
|
145
122
|
console.log('');
|
|
146
123
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const openclaw = detected.find(c => c.name === 'OpenClaw');
|
|
150
|
-
if (openclaw) {
|
|
151
|
-
info('Installing via git clone (OpenClaw)...');
|
|
152
|
-
try {
|
|
153
|
-
const ok = await openclaw.install();
|
|
154
|
-
if (ok) { success('Installed via git clone'); count++; }
|
|
155
|
-
else { warn('git clone failed — falling back to file install'); }
|
|
156
|
-
} catch (e) {
|
|
157
|
-
warn(`git clone error: ${e.message}`);
|
|
158
|
-
}
|
|
159
|
-
console.log('');
|
|
160
|
-
}
|
|
161
|
-
|
|
124
|
+
// Collect unique install dirs
|
|
162
125
|
const seenDirs = new Set();
|
|
163
|
-
const
|
|
126
|
+
const targets = [];
|
|
164
127
|
|
|
165
128
|
for (const client of detected) {
|
|
166
129
|
if (client.skillDir && !seenDirs.has(client.skillDir)) {
|
|
167
130
|
seenDirs.add(client.skillDir);
|
|
168
|
-
|
|
131
|
+
targets.push({ dir: client.skillDir, label: client.name });
|
|
169
132
|
}
|
|
170
133
|
}
|
|
171
|
-
|
|
172
134
|
if (!seenDirs.has(UNIVERSAL_DIR)) {
|
|
173
|
-
|
|
135
|
+
targets.push({ dir: UNIVERSAL_DIR, label: 'universal' });
|
|
174
136
|
}
|
|
175
137
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
138
|
+
let count = 0;
|
|
139
|
+
|
|
140
|
+
for (const { dir, label } of targets) {
|
|
141
|
+
const dest = path.join(dir, SKILL_NAME);
|
|
142
|
+
const shortDest = dest.replace(HOME, '~');
|
|
143
|
+
info(`Installing to ${shortDest} ${dim('(' + label + ')')}`);
|
|
144
|
+
|
|
179
145
|
try {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
146
|
+
let ok = false;
|
|
147
|
+
if (git) {
|
|
148
|
+
ok = gitClone(dest);
|
|
149
|
+
if (!ok) {
|
|
150
|
+
warn('git clone failed — falling back to manifest-only');
|
|
151
|
+
ok = await manifestFallback(dest);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
ok = await manifestFallback(dest);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (ok) {
|
|
158
|
+
// Run bootstrap if full install (downloads binary if missing)
|
|
159
|
+
const bootstrap = path.join(dest, 'scripts', 'bootstrap.sh');
|
|
160
|
+
if (fs.existsSync(bootstrap)) {
|
|
161
|
+
info('Running bootstrap (downloading binary artifacts)...');
|
|
162
|
+
const r = spawnSync('bash', [bootstrap], { stdio: 'inherit', cwd: dest });
|
|
163
|
+
if (r.status === 0) {
|
|
164
|
+
success('Bootstrap complete — binary verified');
|
|
165
|
+
} else {
|
|
166
|
+
warn('Bootstrap had issues — binary may need manual download');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
success(`Installed → ${shortDest}`);
|
|
170
|
+
count++;
|
|
171
|
+
}
|
|
183
172
|
} catch (e) {
|
|
184
173
|
fail(`Failed: ${e.message}`);
|
|
185
174
|
}
|
|
175
|
+
console.log('');
|
|
186
176
|
}
|
|
187
177
|
|
|
188
|
-
console.log('');
|
|
189
178
|
if (count > 0) {
|
|
190
179
|
console.log(` ${bold(green('AAWP skill installed!'))}`);
|
|
191
180
|
console.log('');
|
|
192
181
|
console.log(` ${dim('Restart your AI client to load the skill.')}`);
|
|
193
|
-
console.log(` ${dim('Then ask: "set up my AAWP wallet"')}`);
|
|
194
|
-
console.log(
|
|
195
|
-
console.log(` ${dim('Docs: https://aawp.ai · https://github.com/aawp-ai/aawp')}`);
|
|
182
|
+
console.log(` ${dim('Then ask your AI: "set up my AAWP wallet"')}`);
|
|
183
|
+
console.log('');
|
|
184
|
+
console.log(` ${dim('Docs: https://aawp.ai · GitHub: https://github.com/aawp-ai/aawp')}`);
|
|
196
185
|
} else {
|
|
197
|
-
|
|
198
|
-
console.log(` ${dim('Try
|
|
186
|
+
fail('Nothing was installed.');
|
|
187
|
+
console.log(` ${dim('Try: git clone https://github.com/aawp-ai/aawp.git ~/.agents/skills/aawp')}`);
|
|
199
188
|
}
|
|
200
189
|
console.log('');
|
|
201
190
|
}
|