@ax-llm/ax 25.4.0 → 25.4.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/README.md +29 -0
- package/cli/index.mjs +70 -317
- package/index.cjs +1 -1
- package/index.cjs.map +1 -1
- package/index.d.cts +2 -1
- package/index.d.ts +2 -1
- package/index.global.js +1 -1
- package/index.global.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +16 -202
- package/scripts/skill-bundles.mjs +70 -0
- package/scripts/skill-config.mjs +59 -0
- package/scripts/skill-install.mjs +123 -0
- package/skills/ax-agent-context.md +1 -1
- package/skills/ax-agent-memory-skills.md +1 -1
- package/skills/ax-agent-observability.md +1 -1
- package/skills/ax-agent-optimize.md +1 -1
- package/skills/ax-agent-rlm.md +1 -1
- package/skills/ax-agent.md +1 -1
- package/skills/ax-ai.md +1 -1
- package/skills/ax-audio.md +1 -1
- package/skills/ax-flow.md +1 -1
- package/skills/ax-gen.md +1 -1
- package/skills/ax-gepa.md +1 -1
- package/skills/ax-llm.md +46 -353
- package/skills/ax-mcp.md +1 -1
- package/skills/ax-playbook.md +1 -1
- package/skills/ax-refine.md +1 -1
- package/skills/ax-signature.md +1 -1
- package/skills/ax-typesafe.md +5 -5
- package/skills/references/quickstart.md +354 -0
package/README.md
CHANGED
|
@@ -157,6 +157,35 @@ const analyzer = ax(`
|
|
|
157
157
|
npm install @ax-llm/ax
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
+
Outside CI, installation sets up one Claude Code skill, `ax-llm`, with a compact
|
|
161
|
+
index and bundled guides your agent reads as needed. To disable future automatic
|
|
162
|
+
setup, run `npx @ax-llm/ax skills-auto-install off` from your project root. This
|
|
163
|
+
saves `ax.skills.autoInstall: false` in your project's `package.json`; commit
|
|
164
|
+
that setting to share it with your team. Use `skills-auto-install on` to re-enable
|
|
165
|
+
automatic setup on future installs. Set `AX_SKIP_SKILL_INSTALL=1` to skip setup
|
|
166
|
+
for a single installation; CI and this environment variable take precedence over
|
|
167
|
+
the saved setting.
|
|
168
|
+
|
|
169
|
+
You can also set the preference before installing Ax for the first time:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"ax": {
|
|
174
|
+
"skills": {
|
|
175
|
+
"autoInstall": false
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Merge this field into your existing `package.json`. Manual
|
|
182
|
+
`npx @ax-llm/ax setup-claude` works even when automatic setup is disabled;
|
|
183
|
+
add `--all` to register every specialist or `--skill ax-ai` to install only one.
|
|
184
|
+
Changing the preference leaves installed skills in place, and manual setup or
|
|
185
|
+
removal preserves the saved preference.
|
|
186
|
+
Existing specialists remain installed; use `remove-claude` followed by
|
|
187
|
+
`setup-claude` to switch an older full installation to the compact bundle.
|
|
188
|
+
|
|
160
189
|
### Your First AI Feature (2 minutes)
|
|
161
190
|
|
|
162
191
|
```typescript
|
package/cli/index.mjs
CHANGED
|
@@ -1,294 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* CLI for @ax-llm/ax
|
|
5
|
-
*
|
|
6
|
-
* Commands:
|
|
7
|
-
* setup-claude [--force] Install/upgrade Claude Code skills to .claude/skills/<name>/SKILL.md (project-local)
|
|
8
|
-
* remove-claude Remove all Claude Code skills
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* npx @ax-llm/ax setup-claude # Install or upgrade if newer version
|
|
12
|
-
* npx @ax-llm/ax setup-claude --force # Force overwrite regardless of version
|
|
13
|
-
* npx @ax-llm/ax remove-claude # Remove all skills
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
existsSync,
|
|
18
|
-
mkdirSync,
|
|
19
|
-
readdirSync,
|
|
20
|
-
readFileSync,
|
|
21
|
-
rmSync,
|
|
22
|
-
writeFileSync,
|
|
23
|
-
} from 'node:fs';
|
|
24
|
-
import { basename, dirname, join } from 'node:path';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
25
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { setAutoSkillInstall } from '../scripts/skill-config.mjs';
|
|
6
|
+
import { installSkills, removeSkills } from '../scripts/skill-install.mjs';
|
|
26
7
|
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
// Skill files directory in the package
|
|
31
|
-
const SKILLS_SOURCE_DIR = join(__dirname, '..', 'skills');
|
|
32
|
-
|
|
33
|
-
// Base target location for skills in current working directory (project-local)
|
|
34
|
-
const SKILLS_BASE_DIR = join(process.cwd(), '.claude', 'skills');
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get all *.md skill files from the source directory
|
|
38
|
-
*/
|
|
39
|
-
function getSkillFiles() {
|
|
40
|
-
if (!existsSync(SKILLS_SOURCE_DIR)) {
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
return readdirSync(SKILLS_SOURCE_DIR).filter((f) => f.endsWith('.md'));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get the skill name from YAML frontmatter, falling back to filename without .md
|
|
48
|
-
*/
|
|
49
|
-
function getSkillName(filePath) {
|
|
50
|
-
try {
|
|
51
|
-
const content = readFileSync(filePath, 'utf8');
|
|
52
|
-
const match = content.match(/^name:\s*["']?([^"'\n\r]+)/m);
|
|
53
|
-
if (match) return match[1].trim();
|
|
54
|
-
} catch {
|
|
55
|
-
// Fall through to fallback
|
|
56
|
-
}
|
|
57
|
-
return basename(filePath, '.md');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Compare semver versions
|
|
62
|
-
* Returns: 1 if a > b, -1 if a < b, 0 if equal
|
|
63
|
-
*/
|
|
64
|
-
function compareSemver(a, b) {
|
|
65
|
-
const parseVersion = (v) => {
|
|
66
|
-
const match = v.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
67
|
-
if (!match) return [0, 0, 0];
|
|
68
|
-
return [
|
|
69
|
-
Number.parseInt(match[1], 10),
|
|
70
|
-
Number.parseInt(match[2], 10),
|
|
71
|
-
Number.parseInt(match[3], 10),
|
|
72
|
-
];
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const [aMajor, aMinor, aPatch] = parseVersion(a);
|
|
76
|
-
const [bMajor, bMinor, bPatch] = parseVersion(b);
|
|
77
|
-
|
|
78
|
-
if (aMajor !== bMajor) return aMajor > bMajor ? 1 : -1;
|
|
79
|
-
if (aMinor !== bMinor) return aMinor > bMinor ? 1 : -1;
|
|
80
|
-
if (aPatch !== bPatch) return aPatch > bPatch ? 1 : -1;
|
|
81
|
-
return 0;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Get the installed skill version from a file
|
|
86
|
-
*/
|
|
87
|
-
function getInstalledVersion(targetPath) {
|
|
88
|
-
if (!existsSync(targetPath)) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const content = readFileSync(targetPath, 'utf8');
|
|
94
|
-
const match = content.match(/^version:\s*["']?([^"'\n\r]+)/m);
|
|
95
|
-
return match ? match[1].trim() : null;
|
|
96
|
-
} catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Get the package version from a skill source file
|
|
103
|
-
*/
|
|
104
|
-
function getPackageVersion(sourcePath) {
|
|
105
|
-
if (!existsSync(sourcePath)) {
|
|
106
|
-
// Fallback: try to read from package.json
|
|
107
|
-
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
108
|
-
if (existsSync(packageJsonPath)) {
|
|
109
|
-
try {
|
|
110
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
111
|
-
return pkg.version || null;
|
|
112
|
-
} catch {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
const content = readFileSync(sourcePath, 'utf8');
|
|
121
|
-
const match = content.match(/^version:\s*["']?([^"'\n\r]+)/m);
|
|
122
|
-
return match ? match[1].trim() : null;
|
|
123
|
-
} catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Install or upgrade Claude Code skills
|
|
130
|
-
*/
|
|
131
|
-
function setupClaude(force = false) {
|
|
132
|
-
const skillFiles = getSkillFiles();
|
|
133
|
-
|
|
134
|
-
if (skillFiles.length === 0) {
|
|
135
|
-
console.error('Error: No skill files found. The package may be corrupted.');
|
|
136
|
-
process.exit(1);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Clean up legacy flat file structure (.claude/skills/ax/*.md except SKILL.md)
|
|
140
|
-
const legacyDir = join(SKILLS_BASE_DIR, 'ax');
|
|
141
|
-
if (existsSync(legacyDir)) {
|
|
142
|
-
try {
|
|
143
|
-
const legacyFiles = readdirSync(legacyDir).filter(
|
|
144
|
-
(f) => f.endsWith('.md') && f !== 'SKILL.md'
|
|
145
|
-
);
|
|
146
|
-
for (const f of legacyFiles) {
|
|
147
|
-
rmSync(join(legacyDir, f), { force: true });
|
|
148
|
-
}
|
|
149
|
-
} catch {
|
|
150
|
-
// Ignore cleanup errors
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
let allUpToDate = true;
|
|
155
|
-
|
|
156
|
-
for (const file of skillFiles) {
|
|
157
|
-
const skillSource = join(SKILLS_SOURCE_DIR, file);
|
|
158
|
-
const skillName = getSkillName(skillSource);
|
|
159
|
-
const skillDir = join(SKILLS_BASE_DIR, skillName);
|
|
160
|
-
const skillTarget = join(skillDir, 'SKILL.md');
|
|
161
|
-
|
|
162
|
-
const packageVersion = getPackageVersion(skillSource);
|
|
163
|
-
const installedVersion = getInstalledVersion(skillTarget);
|
|
8
|
+
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const [command = 'help', ...flags] = process.argv.slice(2);
|
|
164
10
|
|
|
165
|
-
// Determine if we should install
|
|
166
|
-
let shouldInstall = false;
|
|
167
|
-
let action = 'Installed';
|
|
168
|
-
|
|
169
|
-
if (!existsSync(skillTarget)) {
|
|
170
|
-
shouldInstall = true;
|
|
171
|
-
} else if (force) {
|
|
172
|
-
shouldInstall = true;
|
|
173
|
-
action = installedVersion ? 'Reinstalled' : 'Installed';
|
|
174
|
-
} else if (installedVersion && packageVersion) {
|
|
175
|
-
const comparison = compareSemver(packageVersion, installedVersion);
|
|
176
|
-
if (comparison > 0) {
|
|
177
|
-
shouldInstall = true;
|
|
178
|
-
action = 'Upgraded';
|
|
179
|
-
} else if (comparison === 0) {
|
|
180
|
-
console.log(`${skillName} is up to date (v${installedVersion})`);
|
|
181
|
-
continue;
|
|
182
|
-
} else {
|
|
183
|
-
console.log(
|
|
184
|
-
`${skillName} is already at v${installedVersion} (package has v${packageVersion})`
|
|
185
|
-
);
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
} else if (!installedVersion && existsSync(skillTarget)) {
|
|
189
|
-
shouldInstall = true;
|
|
190
|
-
action = 'Upgraded';
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (!shouldInstall) {
|
|
194
|
-
console.log(
|
|
195
|
-
`${skillName} is up to date (v${installedVersion || 'unknown'})`
|
|
196
|
-
);
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
allUpToDate = false;
|
|
201
|
-
|
|
202
|
-
// Create target directory if it doesn't exist
|
|
203
|
-
if (!existsSync(skillDir)) {
|
|
204
|
-
mkdirSync(skillDir, { recursive: true });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
const content = readFileSync(skillSource, 'utf8');
|
|
209
|
-
writeFileSync(skillTarget, content, 'utf8');
|
|
210
|
-
|
|
211
|
-
if (action === 'Upgraded' && installedVersion && packageVersion) {
|
|
212
|
-
console.log(
|
|
213
|
-
`\u2713 ${action} ${skillName} (v${installedVersion} \u2192 v${packageVersion})`
|
|
214
|
-
);
|
|
215
|
-
} else {
|
|
216
|
-
console.log(
|
|
217
|
-
`\u2713 ${action} ${skillName} (v${packageVersion || 'unknown'})`
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
} catch (err) {
|
|
221
|
-
console.error(`Error installing ${skillName}: ${err.message}`);
|
|
222
|
-
process.exit(1);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (allUpToDate) {
|
|
227
|
-
console.log('All Ax Claude Code skills are up to date.');
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Remove all Claude Code skills
|
|
233
|
-
*/
|
|
234
|
-
function removeClaude() {
|
|
235
|
-
const skillFiles = getSkillFiles();
|
|
236
|
-
|
|
237
|
-
if (skillFiles.length === 0) {
|
|
238
|
-
console.log('No skill definitions found in package.');
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
let removedCount = 0;
|
|
244
|
-
|
|
245
|
-
for (const file of skillFiles) {
|
|
246
|
-
const skillName = getSkillName(join(SKILLS_SOURCE_DIR, file));
|
|
247
|
-
const skillDir = join(SKILLS_BASE_DIR, skillName);
|
|
248
|
-
const skillTarget = join(skillDir, 'SKILL.md');
|
|
249
|
-
|
|
250
|
-
if (existsSync(skillTarget)) {
|
|
251
|
-
rmSync(skillTarget, { force: true });
|
|
252
|
-
removedCount++;
|
|
253
|
-
|
|
254
|
-
// Try to remove the directory if empty
|
|
255
|
-
try {
|
|
256
|
-
const remaining = readdirSync(skillDir);
|
|
257
|
-
if (remaining.length === 0) {
|
|
258
|
-
rmSync(skillDir, { recursive: true, force: true });
|
|
259
|
-
}
|
|
260
|
-
} catch {
|
|
261
|
-
// Ignore errors when trying to clean up directory
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Also clean up legacy flat files from old .claude/skills/ax/ structure
|
|
267
|
-
const legacyDir = join(SKILLS_BASE_DIR, 'ax');
|
|
268
|
-
if (existsSync(legacyDir)) {
|
|
269
|
-
const legacyFiles = readdirSync(legacyDir).filter(
|
|
270
|
-
(f) => f.endsWith('.md') && f !== 'SKILL.md'
|
|
271
|
-
);
|
|
272
|
-
for (const f of legacyFiles) {
|
|
273
|
-
rmSync(join(legacyDir, f), { force: true });
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (removedCount > 0) {
|
|
278
|
-
const noun = removedCount === 1 ? 'skill' : 'skills';
|
|
279
|
-
console.log(`\u2713 Removed ${removedCount} Ax Claude Code ${noun}`);
|
|
280
|
-
} else {
|
|
281
|
-
console.log('Ax Claude Code skills are not installed.');
|
|
282
|
-
}
|
|
283
|
-
} catch (err) {
|
|
284
|
-
console.error(`Error removing skills: ${err.message}`);
|
|
285
|
-
process.exit(1);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Show help
|
|
291
|
-
*/
|
|
292
11
|
function showHelp() {
|
|
293
12
|
console.log(`
|
|
294
13
|
@ax-llm/ax CLI
|
|
@@ -297,42 +16,76 @@ Usage:
|
|
|
297
16
|
npx @ax-llm/ax <command> [options]
|
|
298
17
|
|
|
299
18
|
Commands:
|
|
300
|
-
setup-claude
|
|
301
|
-
remove-claude Remove
|
|
302
|
-
|
|
19
|
+
setup-claude Install the Ax entry point and bundled guides locally
|
|
20
|
+
remove-claude Remove installed Ax skills and their bundled guides
|
|
21
|
+
skills-auto-install <on|off>
|
|
22
|
+
Save automatic setup preference in project package.json
|
|
23
|
+
help Show this help
|
|
303
24
|
|
|
304
|
-
Options:
|
|
305
|
-
--
|
|
25
|
+
Options for setup-claude:
|
|
26
|
+
--all Install every specialist as a separate skill too
|
|
27
|
+
--skill <name> Install only the named skill (repeatable)
|
|
28
|
+
--force Reinstall even if the installed version is newer
|
|
306
29
|
|
|
307
30
|
Examples:
|
|
308
|
-
npx @ax-llm/ax setup-claude
|
|
309
|
-
npx @ax-llm/ax setup-claude --
|
|
310
|
-
npx @ax-llm/ax
|
|
31
|
+
npx @ax-llm/ax setup-claude
|
|
32
|
+
npx @ax-llm/ax setup-claude --all
|
|
33
|
+
npx @ax-llm/ax setup-claude --skill ax-ai
|
|
34
|
+
npx @ax-llm/ax skills-auto-install off
|
|
35
|
+
npx @ax-llm/ax skills-auto-install on
|
|
36
|
+
|
|
37
|
+
Automatic setup installs only ax-llm in .claude/skills, outside CI.
|
|
38
|
+
Run skills-auto-install off from your project root to disable future automatic setup.
|
|
39
|
+
This saves ax.skills.autoInstall in package.json; on restores automatic setup.
|
|
40
|
+
Manual setup-claude still works while automatic setup is disabled.
|
|
41
|
+
Set AX_SKIP_SKILL_INSTALL=1 to skip setup for a single package installation.
|
|
42
|
+
Existing specialist installs remain until you run remove-claude.
|
|
311
43
|
`);
|
|
312
44
|
}
|
|
313
45
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
46
|
+
try {
|
|
47
|
+
if (command === 'setup-claude') {
|
|
48
|
+
const options = { all: false, force: false, names: [] };
|
|
49
|
+
for (let i = 0; i < flags.length; i++) {
|
|
50
|
+
const flag = flags[i];
|
|
51
|
+
if (flag === '--all') options.all = true;
|
|
52
|
+
else if (flag === '--force') options.force = true;
|
|
53
|
+
else if (
|
|
54
|
+
flag === '--skill' &&
|
|
55
|
+
flags[i + 1] &&
|
|
56
|
+
!flags[i + 1].startsWith('--')
|
|
57
|
+
)
|
|
58
|
+
options.names.push(flags[++i]);
|
|
59
|
+
else throw new Error(`Unknown or incomplete option: ${flag}`);
|
|
60
|
+
}
|
|
61
|
+
if (options.all && options.names.length)
|
|
62
|
+
throw new Error('Choose --all or --skill.');
|
|
63
|
+
const installed = installSkills(packageRoot, process.cwd(), options);
|
|
64
|
+
console.log(
|
|
65
|
+
installed.length
|
|
66
|
+
? `✓ Installed Ax skills: ${installed.join(', ')}`
|
|
67
|
+
: 'Ax skills are up to date.'
|
|
68
|
+
);
|
|
69
|
+
} else if (command === 'remove-claude' && flags.length === 0) {
|
|
70
|
+
console.log(
|
|
71
|
+
`✓ Removed ${removeSkills(packageRoot, process.cwd())} Ax skills.`
|
|
72
|
+
);
|
|
73
|
+
} else if (command === 'skills-auto-install') {
|
|
74
|
+
if (flags.length !== 1 || !['on', 'off'].includes(flags[0]))
|
|
75
|
+
throw new Error('Usage: npx @ax-llm/ax skills-auto-install <on|off>');
|
|
76
|
+
const enabled = flags[0] === 'on';
|
|
77
|
+
setAutoSkillInstall(process.cwd(), enabled);
|
|
78
|
+
console.log(
|
|
79
|
+
`✓ Automatic Ax skill installation ${enabled ? 'enabled' : 'disabled'} in package.json.`
|
|
80
|
+
);
|
|
81
|
+
} else if (['help', '--help', '-h'].includes(command)) {
|
|
332
82
|
showHelp();
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
83
|
+
} else {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Unknown command: ${command}. Run npx @ax-llm/ax help for usage.`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(error.message);
|
|
90
|
+
process.exitCode = 1;
|
|
338
91
|
}
|