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.
Files changed (2) hide show
  1. package/bin/install.js +97 -108
  2. 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 RAW_BASE = 'https://raw.githubusercontent.com/aawp-ai/aawp/main/skills/aawp';
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
- // ── Fetch helper ──────────────────────────────────────────────────────────────
30
- async function fetchText(url) {
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.text();
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 fetchText(res.headers.location).then(resolve).catch(reject);
51
+ return fetchBuffer(res.headers.location).then(resolve).catch(reject);
41
52
  }
42
- if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
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).toString()));
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
- async function downloadSkillMd() {
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
- return await fetchText(`${RAW_BASE}/SKILL.md`);
85
+ const md = await fetchBuffer(`${RAW_BASE}/SKILL.md`);
86
+ fs.writeFileSync(path.join(dest, 'SKILL.md'), md);
54
87
  } catch {
55
- return fetchText(`${FALLBACK}/SKILL.md`);
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
- name: 'OpenClaw',
72
- detect: () => hasCmd('clawhub') || dirExists('skills'),
73
- install: async () => {
74
- if (!hasCmd('git')) return false;
75
- const dest = path.join('skills', SKILL_NAME);
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 detectedNames = detected.map(c => c.name);
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: ${detectedNames.join(', ')}`);
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
- let count = 0;
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 dirsToInstall = [];
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
- dirsToInstall.push({ dir: client.skillDir, label: client.name });
131
+ targets.push({ dir: client.skillDir, label: client.name });
169
132
  }
170
133
  }
171
-
172
134
  if (!seenDirs.has(UNIVERSAL_DIR)) {
173
- dirsToInstall.push({ dir: UNIVERSAL_DIR, label: 'universal (~/.agents/skills)' });
135
+ targets.push({ dir: UNIVERSAL_DIR, label: 'universal' });
174
136
  }
175
137
 
176
- for (const { dir, label } of dirsToInstall) {
177
- const shortDir = dir.replace(HOME, '~');
178
- info(`Installing to ${shortDir}/aawp/ ${dim('(' + label + ')')}`);
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
- const dest = installToDir(dir, skillMd);
181
- success(`Installed → ${dest.replace(HOME, '~')}/SKILL.md`);
182
- count++;
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(` ${dim('Full autonomy (24/7 daemon): git clone https://github.com/aawp-ai/aawp.git skills/aawp')}`);
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
- warn('Nothing was installed.');
198
- console.log(` ${dim('Try manually: copy SKILL.md to your client\'s skills directory.')}`);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aawp-ai",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Install the AAWP skill for any Agent Skills compatible AI client",
5
5
  "keywords": [
6
6
  "aawp",