@ax-llm/ax 16.0.11 → 16.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ax-llm/ax",
3
- "version": "16.0.11",
3
+ "version": "16.0.13",
4
4
  "type": "module",
5
5
  "description": "The best library to work with LLMs",
6
6
  "repository": {
@@ -56,5 +56,11 @@
56
56
  "import": "./*.js",
57
57
  "require": "./*.cjs"
58
58
  }
59
+ },
60
+ "bin": {
61
+ "ax": "./cli/index.mjs"
62
+ },
63
+ "scripts": {
64
+ "postinstall": "node ./scripts/postinstall.mjs"
59
65
  }
60
66
  }
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Postinstall script for @ax-llm/ax
5
+ *
6
+ * Auto-installs/upgrades the Claude Code skill on package install.
7
+ *
8
+ * Features:
9
+ * - Skips in CI environments (CI, CONTINUOUS_INTEGRATION env vars)
10
+ * - Skips if AX_SKIP_SKILL_INSTALL=1
11
+ * - Silent failure (never breaks package installation)
12
+ * - Cross-platform (macOS, Linux, Windows)
13
+ * - Only logs in interactive terminals (process.stdout.isTTY)
14
+ * - Version-aware upgrades: only upgrades if newer version available
15
+ */
16
+
17
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
18
+ import { dirname, join } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = dirname(__filename);
23
+
24
+ // Check if we should skip installation
25
+ function shouldSkip() {
26
+ // Skip in CI environments
27
+ if (process.env.CI === 'true' || process.env.CI === '1') {
28
+ return true;
29
+ }
30
+ if (
31
+ process.env.CONTINUOUS_INTEGRATION === 'true' ||
32
+ process.env.CONTINUOUS_INTEGRATION === '1'
33
+ ) {
34
+ return true;
35
+ }
36
+
37
+ // Skip if explicitly disabled
38
+ if (
39
+ process.env.AX_SKIP_SKILL_INSTALL === '1' ||
40
+ process.env.AX_SKIP_SKILL_INSTALL === 'true'
41
+ ) {
42
+ return true;
43
+ }
44
+
45
+ // Common CI environment variables
46
+ const ciEnvVars = [
47
+ 'GITHUB_ACTIONS',
48
+ 'GITLAB_CI',
49
+ 'CIRCLECI',
50
+ 'TRAVIS',
51
+ 'JENKINS_URL',
52
+ 'BUILDKITE',
53
+ 'DRONE',
54
+ 'TEAMCITY_VERSION',
55
+ 'BITBUCKET_BUILD_NUMBER',
56
+ 'CODEBUILD_BUILD_ID',
57
+ 'TF_BUILD', // Azure Pipelines
58
+ ];
59
+
60
+ for (const envVar of ciEnvVars) {
61
+ if (process.env[envVar]) {
62
+ return true;
63
+ }
64
+ }
65
+
66
+ return false;
67
+ }
68
+
69
+ // Check if we're in an interactive terminal
70
+ function isInteractive() {
71
+ return process.stdout.isTTY === true;
72
+ }
73
+
74
+ /**
75
+ * Compare semver versions
76
+ * Returns: 1 if a > b, -1 if a < b, 0 if equal
77
+ */
78
+ function compareSemver(a, b) {
79
+ const parseVersion = (v) => {
80
+ const match = v.match(/^(\d+)\.(\d+)\.(\d+)/);
81
+ if (!match) return [0, 0, 0];
82
+ return [
83
+ Number.parseInt(match[1], 10),
84
+ Number.parseInt(match[2], 10),
85
+ Number.parseInt(match[3], 10),
86
+ ];
87
+ };
88
+
89
+ const [aMajor, aMinor, aPatch] = parseVersion(a);
90
+ const [bMajor, bMinor, bPatch] = parseVersion(b);
91
+
92
+ if (aMajor !== bMajor) return aMajor > bMajor ? 1 : -1;
93
+ if (aMinor !== bMinor) return aMinor > bMinor ? 1 : -1;
94
+ if (aPatch !== bPatch) return aPatch > bPatch ? 1 : -1;
95
+ return 0;
96
+ }
97
+
98
+ /**
99
+ * Get the installed skill version from the file
100
+ */
101
+ function getInstalledVersion(targetPath) {
102
+ if (!existsSync(targetPath)) {
103
+ return null;
104
+ }
105
+
106
+ try {
107
+ const content = readFileSync(targetPath, 'utf8');
108
+ const match = content.match(/^version:\s*["']?([^"'\n\r]+)/m);
109
+ return match ? match[1].trim() : null;
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Get the package version from the skill source file
117
+ */
118
+ function getPackageVersion(sourcePath) {
119
+ if (!existsSync(sourcePath)) {
120
+ return null;
121
+ }
122
+
123
+ try {
124
+ const content = readFileSync(sourcePath, 'utf8');
125
+ const match = content.match(/^version:\s*["']?([^"'\n\r]+)/m);
126
+ return match ? match[1].trim() : null;
127
+ } catch {
128
+ return null;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Main installation function
134
+ */
135
+ function install() {
136
+ // Skip if needed
137
+ if (shouldSkip()) {
138
+ return;
139
+ }
140
+
141
+ try {
142
+ // Paths
143
+ const skillSource = join(__dirname, '..', 'skills', 'ax-llm.md');
144
+ // When installed via npm, script runs from: node_modules/@ax-llm/ax/scripts/postinstall.mjs
145
+ // Project root is 4 directories up: ../../../../
146
+ const projectRoot = join(__dirname, '..', '..', '..', '..');
147
+ const skillTargetDir = join(projectRoot, '.claude', 'skills', 'ax');
148
+ const skillTarget = join(skillTargetDir, 'ax-llm.md');
149
+
150
+ // Check if source exists
151
+ if (!existsSync(skillSource)) {
152
+ // Skill file not found - this can happen during development
153
+ // Silently exit
154
+ return;
155
+ }
156
+
157
+ const packageVersion = getPackageVersion(skillSource);
158
+ const installedVersion = getInstalledVersion(skillTarget);
159
+
160
+ // Determine if we should install
161
+ let shouldInstall = false;
162
+ let action = 'Installed';
163
+ let versionInfo = '';
164
+
165
+ if (!existsSync(skillTarget)) {
166
+ // Fresh install
167
+ shouldInstall = true;
168
+ versionInfo = packageVersion ? ` (v${packageVersion})` : '';
169
+ } else if (installedVersion && packageVersion) {
170
+ // Compare versions
171
+ const comparison = compareSemver(packageVersion, installedVersion);
172
+ if (comparison > 0) {
173
+ // New version is higher - upgrade
174
+ shouldInstall = true;
175
+ action = 'Upgraded';
176
+ versionInfo = ` (v${installedVersion} \u2192 v${packageVersion})`;
177
+ }
178
+ // If same or lower version, don't install
179
+ } else if (!installedVersion && existsSync(skillTarget)) {
180
+ // File exists but no version - upgrade it
181
+ shouldInstall = true;
182
+ action = 'Upgraded';
183
+ versionInfo = packageVersion ? ` (v${packageVersion})` : '';
184
+ }
185
+
186
+ if (!shouldInstall) {
187
+ // Already up to date, silently exit
188
+ return;
189
+ }
190
+
191
+ // Create target directory
192
+ if (!existsSync(skillTargetDir)) {
193
+ mkdirSync(skillTargetDir, { recursive: true });
194
+ }
195
+
196
+ // Copy skill file
197
+ const content = readFileSync(skillSource, 'utf8');
198
+ writeFileSync(skillTarget, content, 'utf8');
199
+
200
+ // Only log in interactive terminals
201
+ if (isInteractive()) {
202
+ console.log(`\u2713 ${action} Ax Claude Code skill${versionInfo}`);
203
+ }
204
+ } catch {
205
+ // Silent failure - never break npm install
206
+ // The CLI command can be used for manual installation
207
+ }
208
+ }
209
+
210
+ // Run installation
211
+ install();