auxilo-mcp 0.9.21 → 0.9.22

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/auxilo-cli.js CHANGED
@@ -299,6 +299,8 @@ async function cmdSetup(flags) {
299
299
  console.log('');
300
300
  try {
301
301
  const { binRoot } = installer.installRunner(HOME);
302
+ const claudeBin = installer.recordClaudeBin(HOME, process.env.PATH);
303
+ if (claudeBin !== null) console.log(` ✓ Claude Code CLI recorded: ${claudeBin}`);
302
304
  console.log(` ✓ Extraction runner installed to ${binRoot}`);
303
305
  } catch (err) {
304
306
  console.error(` ✗ Runner install failed: ${err.message}`);
package/lib/installer.js CHANGED
@@ -1061,6 +1061,29 @@ function writeRunnerConfig(homeDir, patch) {
1061
1061
  return merged;
1062
1062
  }
1063
1063
 
1064
+ /** Find the first regular executable in PATH, preserving its symlink path. */
1065
+ function findExecutableOnPath(name, envPath, fsImpl = fs) {
1066
+ if (typeof envPath !== 'string' || !envPath) return null;
1067
+ for (const dir of envPath.split(path.delimiter)) {
1068
+ if (!dir) continue;
1069
+ const candidate = path.resolve(dir, name);
1070
+ try {
1071
+ if (!fsImpl.statSync(candidate).isFile()) continue;
1072
+ fsImpl.accessSync(candidate, fs.constants.X_OK);
1073
+ return candidate;
1074
+ } catch (_) { /* missing, inaccessible or non-executable — try the next entry */ }
1075
+ }
1076
+ return null;
1077
+ }
1078
+
1079
+ /** Setup only: record the user's CLI and confirm the best-effort write persisted. */
1080
+ function recordClaudeBin(homeDir, envPath, fsImpl = fs) {
1081
+ const bin = findExecutableOnPath('claude', envPath, fsImpl);
1082
+ if (bin === null) return null;
1083
+ writeRunnerConfig(homeDir, { claude_bin: bin });
1084
+ return readRunnerConfig(homeDir).claude_bin === bin ? bin : null;
1085
+ }
1086
+
1064
1087
  // ─── Device-code auth (spec §LW-12 step 2; server.js /auth/device) ──────────
1065
1088
 
1066
1089
  /**
@@ -2411,6 +2434,8 @@ module.exports = {
2411
2434
  runnerConfigPath,
2412
2435
  readRunnerConfig,
2413
2436
  writeRunnerConfig,
2437
+ findExecutableOnPath,
2438
+ recordClaudeBin,
2414
2439
  writeEnvFile,
2415
2440
  deviceLogin,
2416
2441
  binRootFor,
package/mcp-server.js CHANGED
@@ -198,7 +198,7 @@ async function postBulkChunks(headers, decisions) {
198
198
  }
199
199
 
200
200
  const server = new Server(
201
- { name: 'auxilo', version: '0.9.21' },
201
+ { name: 'auxilo', version: '0.9.22' },
202
202
  {
203
203
  capabilities: { tools: {} },
204
204
  instructions: `You are connected to Auxilo, a knowledge marketplace where AI agents buy and sell operational learnings.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auxilo-mcp",
3
- "version": "0.9.21",
3
+ "version": "0.9.22",
4
4
  "mcpName": "io.github.silent-architects/auxilo",
5
5
  "description": "MCP server for Auxilo. Your agent stops solving the same problem twice: auto-extracted learnings, free self-unlocks, and earnings when other agents unlock yours.",
6
6
  "main": "mcp-server.js",
@@ -22,23 +22,56 @@ const fs = require('fs');
22
22
  const path = require('path');
23
23
  const os = require('os');
24
24
 
25
- /** Resolve the `claude` binary hook/launchd env may have a minimal PATH. */
25
+ /** Leading major.minor.patch only; unreadable versions retain fallback behavior. */
26
+ function versionParts(version) {
27
+ const match = typeof version === 'string' && /^(\d+)\.(\d+)\.(\d+)/.exec(version);
28
+ return match ? match.slice(1).map(Number) : null;
29
+ }
30
+
31
+ function compareVersions(left, right) {
32
+ for (let i = 0; i < 3; i += 1) {
33
+ if (left[i] !== right[i]) return left[i] > right[i] ? 1 : -1;
34
+ }
35
+ return 0;
36
+ }
37
+
38
+ /** Resolve the newest readable CLI — hook/launchd env may have a minimal PATH. */
26
39
  function resolveClaudeBin(opts = {}) {
27
40
  const homeDir = typeof opts.homeDir === 'string' ? opts.homeDir : os.homedir();
28
41
  const existsSync = typeof opts.existsSync === 'function' ? opts.existsSync : fs.existsSync;
42
+ const readFileSyncImpl = typeof opts.readFileSyncImpl === 'function' ? opts.readFileSyncImpl : fs.readFileSync;
29
43
  const candidates = [
30
44
  path.join(homeDir, '.claude', 'local', 'claude'),
31
45
  '/usr/local/bin/claude',
32
46
  '/opt/homebrew/bin/claude',
33
47
  path.join(homeDir, '.local', 'bin', 'claude'),
48
+ path.join(homeDir, '.npm-global', 'bin', 'claude'),
34
49
  ];
50
+ // Read directly: the sweeper install does not include lib/installer.js.
51
+ try {
52
+ const config = JSON.parse(readFileSyncImpl(path.join(homeDir, '.auxilo', 'runner-config.json'), 'utf8'));
53
+ const recorded = config && config.claude_bin;
54
+ if (typeof recorded === 'string' && path.isAbsolute(recorded) && path.basename(recorded) === 'claude') {
55
+ candidates.unshift(recorded);
56
+ }
57
+ } catch (_) { /* missing/malformed config means no recorded candidate */ }
58
+
59
+ let firstExisting;
60
+ let newest;
61
+ let newestVersion;
35
62
  for (const c of candidates) {
36
63
  try {
37
- if (existsSync(c)) return c;
64
+ if (!existsSync(c)) continue;
65
+ if (!firstExisting) firstExisting = c;
66
+ const version = versionParts(getClaudeCliVersion(c, opts));
67
+ if (version && (!newestVersion || compareVersions(version, newestVersion) > 0)) {
68
+ newest = c;
69
+ newestVersion = version;
70
+ }
38
71
  } catch (_) { /* ignore */ }
39
72
  }
40
73
  // Absolute launchd fallbacks are absent; let PATH resolve the final option.
41
- return 'claude';
74
+ return newest || firstExisting || 'claude';
42
75
  }
43
76
 
44
77
  // ─── Child settings/hooks isolation (EXTRACTION-CHILD-HOOKS, PUNCH-LIST P1,
@@ -105,27 +138,35 @@ function _resetSettingSourcesCacheForTests() {
105
138
  cachedSettingSourcesUnsupported = undefined;
106
139
  }
107
140
 
108
- // ─── CLI version, for diagnostics only (no subprocess spawn) ───────────────
141
+ // ─── CLI version, for selection, auth gating and provenance (no spawn) ──────
109
142
  //
110
143
  // Resolves the installed package's own package.json version by following the
111
144
  // resolved binary's real path (e.g. `/usr/local/bin/claude` -> `.../
112
145
  // node_modules/@anthropic-ai/claude-code/cli.js`) and reading the sibling
113
- // package.json — filesystem-only, so it never adds a spawn to the extraction
146
+ // package.json, or the named parent package for the native bin/claude.exe
147
+ // layout — filesystem-only, so it never adds a spawn to the extraction
114
148
  // path (verified live: realpath + package.json read, no `claude --version`
115
149
  // call). Best-effort: any failure (bare `claude` unresolved via PATH, an
116
- // install layout that doesn't carry a sibling package.json, a fixture path in
150
+ // install layout that doesn't carry either package.json, a fixture path in
117
151
  // tests) yields null, never throws.
118
152
  function getClaudeCliVersion(bin, opts = {}) {
119
153
  const realpathSyncImpl = typeof opts.realpathSyncImpl === 'function' ? opts.realpathSyncImpl : fs.realpathSync;
120
154
  const readFileSyncImpl = typeof opts.readFileSyncImpl === 'function' ? opts.readFileSyncImpl : fs.readFileSync;
121
155
  try {
122
156
  const real = realpathSyncImpl(bin);
123
- const pkgPath = path.join(path.dirname(real), 'package.json');
124
- const pkg = JSON.parse(readFileSyncImpl(pkgPath, 'utf8'));
125
- return pkg && typeof pkg.version === 'string' ? pkg.version : null;
157
+ const dir = path.dirname(real);
158
+ for (const [pkgDir, requireName] of [[dir, false], [path.dirname(dir), true]]) {
159
+ try {
160
+ const pkg = JSON.parse(readFileSyncImpl(path.join(pkgDir, 'package.json'), 'utf8'));
161
+ if (pkg && typeof pkg.version === 'string' && (!requireName || pkg.name === '@anthropic-ai/claude-code')) {
162
+ return pkg.version;
163
+ }
164
+ } catch (_) { /* unreadable sibling may still have a valid parent */ }
165
+ }
126
166
  } catch {
127
- return null;
167
+ /* unresolved binary */
128
168
  }
169
+ return null;
129
170
  }
130
171
 
131
172
  // ─── Env scrub (EXTRACT-TOOLS-LOCK, PUNCH-LIST) ────────────────────────────
@@ -345,6 +386,10 @@ function detectBillingHelperConfigured(opts = {}) {
345
386
  function checkAuthStatus(opts = {}) {
346
387
  const spawnSyncImpl = typeof opts.spawnSyncImpl === 'function' ? opts.spawnSyncImpl : spawnSync;
347
388
  const bin = typeof opts.claudeBin === 'string' ? opts.claudeBin : resolveClaudeBin(opts);
389
+ const version = versionParts(getClaudeCliVersion(bin, opts));
390
+ // Older CLIs treat `auth status` as a model prompt. Unknown versions retain
391
+ // the existing probe; only a known-old build can safely skip it here.
392
+ if (version && compareVersions(version, [2, 1, 41]) < 0) return 'unknown';
348
393
  let res;
349
394
  try {
350
395
  res = spawnSyncImpl(bin, ['auth', 'status'], {