auxilo-mcp 0.9.2 → 0.9.3
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 +205 -31
- package/lib/installer.js +392 -12
- package/mcp-server.js +5 -5
- package/package.json +5 -2
- package/scripts/extract-local.js +99 -14
- package/scripts/review-notice.js +145 -0
- package/scripts/runner.js +161 -44
- package/scripts/sources/cline.js +160 -0
- package/scripts/sources/continue.js +140 -0
- package/scripts/sources/roo-code.js +35 -0
package/bin/auxilo-cli.js
CHANGED
|
@@ -106,7 +106,9 @@ const CONSENT_TEXT = `
|
|
|
106
106
|
for everything) is available in your account settings. You earn 70%
|
|
107
107
|
of sales.
|
|
108
108
|
You can stop any time with \`auxilo disable\` (local kill-switch) and review
|
|
109
|
-
every run in ~/.auxilo/extract.log. Saying No installs the MCP server only
|
|
109
|
+
every run in ~/.auxilo/extract.log. Saying No installs the MCP server only —
|
|
110
|
+
no session-end capture hook is written into any client config unless you say
|
|
111
|
+
Yes, and any capture hooks left by an earlier install are removed.
|
|
110
112
|
`;
|
|
111
113
|
|
|
112
114
|
async function cmdSetup(flags) {
|
|
@@ -194,7 +196,11 @@ async function cmdSetup(flags) {
|
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
// 4. Runner install +
|
|
199
|
+
// 4. Runner install + review notice -----------------------------------------
|
|
200
|
+
// UC-1a (GOV-3): NO capture hook is written into any third-party client
|
|
201
|
+
// config here — only files under our own ~/.auxilo/bin, plus the
|
|
202
|
+
// SessionStart review notice (a count-only account-status surface, not a
|
|
203
|
+
// capture hook). Capture hooks are written ONLY after consent=Yes (step 5).
|
|
198
204
|
console.log('');
|
|
199
205
|
try {
|
|
200
206
|
const { binRoot } = installer.installRunner(HOME);
|
|
@@ -206,54 +212,95 @@ async function cmdSetup(flags) {
|
|
|
206
212
|
|
|
207
213
|
const claudeCode = chosen.find((c) => c.id === 'claude-code');
|
|
208
214
|
if (claudeCode) {
|
|
215
|
+
// LW-18: SessionStart held-count notice ("N learnings held for your
|
|
216
|
+
// review") — fail-silent, ≤1 per 4h, count-only. Installed regardless of
|
|
217
|
+
// extraction consent: the review queue also holds MCP contributions.
|
|
209
218
|
try {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
console.log(` (replaced legacy hook entr${hook.removedLegacy.length === 1 ? 'y' : 'ies'}: ${hook.removedLegacy.join(', ')})`);
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
console.log(' ✓ Claude Code SessionEnd hook already registered (no changes)');
|
|
218
|
-
}
|
|
219
|
+
const notice = installer.registerClaudeCodeSessionStartNotice(HOME);
|
|
220
|
+
console.log(notice.changed
|
|
221
|
+
? ` ✓ Claude Code SessionStart review notice registered (${notice.hookCmd})`
|
|
222
|
+
: ' ✓ Claude Code SessionStart review notice already registered (no changes)');
|
|
219
223
|
} catch (err) {
|
|
220
|
-
console.error(` ✗
|
|
224
|
+
console.error(` ✗ Review notice SKIPPED — ${err.message}`);
|
|
221
225
|
}
|
|
222
226
|
}
|
|
223
227
|
|
|
224
|
-
//
|
|
225
|
-
try {
|
|
226
|
-
for (const r of installer.installCaptureHooks(HOME, chosen)) {
|
|
227
|
-
if (r.error) {
|
|
228
|
-
console.error(` ✗ ${r.name}: SKIPPED — ${r.error}`);
|
|
229
|
-
} else if (r.changed) {
|
|
230
|
-
console.log(` ✓ ${r.name}: capture hook registered (${r.event})`);
|
|
231
|
-
if (r.notes) console.log(` ${r.notes}`);
|
|
232
|
-
} else {
|
|
233
|
-
console.log(` ✓ ${r.name}: capture hook already registered`);
|
|
234
|
-
if (r.notes) console.log(` ${r.notes}`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
} catch (err) {
|
|
238
|
-
console.error(` ✗ Capture hooks SKIPPED — ${err.message}`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// 5. Explicit consent (default No) ------------------------------------------
|
|
228
|
+
// 5. Explicit consent (default No), THEN capture hooks ----------------------
|
|
242
229
|
console.log(CONSENT_TEXT);
|
|
243
230
|
const consented = await askYesNo(' Enable background extraction?', false);
|
|
231
|
+
let extractionArmed = false;
|
|
244
232
|
if (consented) {
|
|
245
233
|
try {
|
|
246
234
|
await installer.recordConsent({ action: 'grant', apiKey: creds.api_key, baseUrl });
|
|
247
235
|
installer.enableSentinel(HOME);
|
|
236
|
+
extractionArmed = true;
|
|
248
237
|
console.log(' ✓ Consent recorded; background extraction ENABLED (~/.auxilo/autonomous-enabled)');
|
|
249
238
|
} catch (err) {
|
|
250
239
|
console.error(` ✗ Could not record consent on server: ${err.message}`);
|
|
251
|
-
console.error(' Background extraction NOT enabled (no sentinel created). Re-run `auxilo setup` to retry.');
|
|
240
|
+
console.error(' Background extraction NOT enabled (no sentinel created, no hooks written). Re-run `auxilo setup` to retry.');
|
|
252
241
|
}
|
|
253
242
|
} else {
|
|
254
243
|
console.log(' ✓ Background extraction left OFF (MCP-only install). Enable later by re-running `auxilo setup`.');
|
|
255
244
|
}
|
|
256
245
|
|
|
246
|
+
if (extractionArmed) {
|
|
247
|
+
// Consent recorded — NOW write the capture hooks (UC-1a ordering).
|
|
248
|
+
if (claudeCode) {
|
|
249
|
+
try {
|
|
250
|
+
const hook = installer.registerClaudeCodeHook(HOME);
|
|
251
|
+
if (hook.changed) {
|
|
252
|
+
console.log(` ✓ Claude Code SessionEnd hook registered (${hook.hookCmd})`);
|
|
253
|
+
if (hook.removedLegacy.length > 0) {
|
|
254
|
+
console.log(` (replaced legacy hook entr${hook.removedLegacy.length === 1 ? 'y' : 'ies'}: ${hook.removedLegacy.join(', ')})`);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
console.log(' ✓ Claude Code SessionEnd hook already registered (no changes)');
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
console.error(` ✗ Hook registration SKIPPED — ${err.message}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// UC-1: capture hooks for every other chosen client that supports one.
|
|
265
|
+
try {
|
|
266
|
+
for (const r of installer.installCaptureHooks(HOME, chosen)) {
|
|
267
|
+
if (r.error) {
|
|
268
|
+
console.error(` ✗ ${r.name}: SKIPPED — ${r.error}`);
|
|
269
|
+
} else if (r.changed) {
|
|
270
|
+
console.log(` ✓ ${r.name}: capture hook registered (${r.event})`);
|
|
271
|
+
if (r.notes) console.log(` ${r.notes}`);
|
|
272
|
+
} else {
|
|
273
|
+
console.log(` ✓ ${r.name}: capture hook already registered`);
|
|
274
|
+
if (r.notes) console.log(` ${r.notes}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.error(` ✗ Capture hooks SKIPPED — ${err.message}`);
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
// UC-1a: consent not granted (No, or consent record failed) — ensure NO
|
|
282
|
+
// capture-hook artifact remains in any client config, including ones a
|
|
283
|
+
// pre-UC-1a install wrote before the consent step.
|
|
284
|
+
try {
|
|
285
|
+
const removedHook = installer.removeClaudeCodeHook(HOME);
|
|
286
|
+
if (removedHook.changed) {
|
|
287
|
+
console.log(` ✓ Removed pre-existing Claude Code SessionEnd capture hook (${removedHook.removed.join(', ')})`);
|
|
288
|
+
}
|
|
289
|
+
} catch (err) {
|
|
290
|
+
console.error(` ✗ Claude Code hook cleanup SKIPPED — ${err.message}`);
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const allClients = installer.detectClients(HOME); // clean beyond the chosen set
|
|
294
|
+
for (const r of installer.removeCaptureHooks(HOME, allClients)) {
|
|
295
|
+
if (r.error) console.error(` ✗ ${r.name}: capture-hook cleanup SKIPPED — ${r.error}`);
|
|
296
|
+
else if (r.changed) console.log(` ✓ ${r.name}: removed pre-existing capture hook`);
|
|
297
|
+
}
|
|
298
|
+
} catch (err) {
|
|
299
|
+
console.error(` ✗ Capture-hook cleanup SKIPPED — ${err.message}`);
|
|
300
|
+
}
|
|
301
|
+
console.log(' ✓ No capture hooks are present in any client config.');
|
|
302
|
+
}
|
|
303
|
+
|
|
257
304
|
// 6. Rules-file snippet (UC-0 agent-prompted contribution; opt-in, default No)
|
|
258
305
|
const rulesClients = chosen.filter((c) => c.rulesPath);
|
|
259
306
|
if (rulesClients.length > 0) {
|
|
@@ -284,6 +331,119 @@ async function cmdSetup(flags) {
|
|
|
284
331
|
console.log('\nDone. Check `npx auxilo status` any time. Restart your client(s) to pick up the MCP server.\n');
|
|
285
332
|
}
|
|
286
333
|
|
|
334
|
+
// ─── auxilo init (NF-3, Wave 3.4) ───────────────────────────────────────────
|
|
335
|
+
//
|
|
336
|
+
// Non-interactive builder onboarding: mint a SCOPED key for CI or a second
|
|
337
|
+
// machine WITHOUT re-running full setup (no client detection, no hooks, no
|
|
338
|
+
// consent flow, and — unless --save — no touch of ~/.auxilo/credentials.json).
|
|
339
|
+
// ZERO prompts by design: every input is a flag with a default, so piped/
|
|
340
|
+
// scripted stdin can never hang (LW-17 lesson class; init never opens the
|
|
341
|
+
// shared readline at all). The only human step is the device-code approval
|
|
342
|
+
// in a browser, which can happen on any machine.
|
|
343
|
+
|
|
344
|
+
const INIT_SCOPES = ['read', 'earnings-read', 'contribute'];
|
|
345
|
+
|
|
346
|
+
async function cmdInit(flags) {
|
|
347
|
+
const baseUrl = resolveBaseUrl(flags);
|
|
348
|
+
|
|
349
|
+
const scope = flags.scope === undefined ? 'contribute' : String(flags.scope);
|
|
350
|
+
if (!INIT_SCOPES.includes(scope)) {
|
|
351
|
+
console.error(`Invalid --scope '${scope}'. Must be one of: ${INIT_SCOPES.join(', ')}.`);
|
|
352
|
+
console.error('(admin keys cannot be created via the API or device flow.)');
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
const label = (flags.label === undefined || flags.label === true
|
|
356
|
+
? `init-${os.hostname()}`
|
|
357
|
+
: String(flags.label)).trim().slice(0, 50);
|
|
358
|
+
|
|
359
|
+
if (!flags.json) {
|
|
360
|
+
console.log('\nAuxilo init — mint a scoped API key');
|
|
361
|
+
console.log('===================================');
|
|
362
|
+
console.log(`Server: ${baseUrl}`);
|
|
363
|
+
console.log(`Scope: ${scope} Label: ${label}\n`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let result;
|
|
367
|
+
try {
|
|
368
|
+
result = await installer.deviceLogin({
|
|
369
|
+
baseUrl,
|
|
370
|
+
scope,
|
|
371
|
+
label,
|
|
372
|
+
onCode: (code, url) => {
|
|
373
|
+
if (!flags.json) {
|
|
374
|
+
console.log(` Your device code: ${code}`);
|
|
375
|
+
console.log(` Approve at: ${url}`);
|
|
376
|
+
console.log(' Waiting for authorization (Ctrl-C to abort)...\n');
|
|
377
|
+
} else {
|
|
378
|
+
// --json keeps stdout machine-readable; the human-step info goes to stderr.
|
|
379
|
+
console.error(`device_code=${code} approve_at=${url}`);
|
|
380
|
+
}
|
|
381
|
+
if (!flags['no-browser']) openBrowser(url);
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
} catch (err) {
|
|
385
|
+
console.error(`✗ init failed: ${err.message}`);
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Pre-Wave-3.4 servers ignore the requested scope and mint 'contribute'
|
|
390
|
+
// (and don't echo). Detect and WARN — never silently mislabel a credential.
|
|
391
|
+
const grantedScope = result.scope || 'contribute';
|
|
392
|
+
const grantedLabel = result.label || label;
|
|
393
|
+
if (grantedScope !== scope) {
|
|
394
|
+
console.error(` ! Server granted scope '${grantedScope}' (requested '${scope}') — older server version. The key below carries '${grantedScope}'.`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (flags['env-file']) {
|
|
398
|
+
const envPath = String(flags['env-file']);
|
|
399
|
+
try {
|
|
400
|
+
const w = installer.writeEnvFile(envPath, { api_key: result.api_key, base_url: baseUrl });
|
|
401
|
+
if (!flags.json) {
|
|
402
|
+
console.log(` ✓ ${w.created ? 'Created' : 'Updated'} ${w.path} (AUXILO_API_KEY, mode 0600)`);
|
|
403
|
+
}
|
|
404
|
+
} catch (err) {
|
|
405
|
+
// The key was already minted — surface it rather than losing it.
|
|
406
|
+
console.error(` ✗ Could not write ${envPath}: ${err.message}`);
|
|
407
|
+
console.error(' Your key (shown once — store it now):');
|
|
408
|
+
console.error(` ${result.api_key}`);
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (flags.save) {
|
|
414
|
+
installer.writeCredentials(HOME, {
|
|
415
|
+
api_key: result.api_key,
|
|
416
|
+
base_url: baseUrl,
|
|
417
|
+
email: result.email,
|
|
418
|
+
account_id: result.account_id,
|
|
419
|
+
});
|
|
420
|
+
if (!flags.json) console.log(' ✓ Saved to ~/.auxilo/credentials.json (mode 0600) — this machine now uses this key.');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (flags.json) {
|
|
424
|
+
console.log(JSON.stringify({
|
|
425
|
+
api_key: result.api_key,
|
|
426
|
+
scope: grantedScope,
|
|
427
|
+
label: grantedLabel,
|
|
428
|
+
account_id: result.account_id,
|
|
429
|
+
email: result.email,
|
|
430
|
+
base_url: baseUrl,
|
|
431
|
+
env_file: flags['env-file'] ? String(flags['env-file']) : null,
|
|
432
|
+
saved_credentials: !!flags.save,
|
|
433
|
+
}));
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
console.log(` ✓ Key minted for ${result.email || result.account_id} (scope: ${grantedScope}, label: ${grantedLabel})`);
|
|
438
|
+
if (!flags['env-file']) {
|
|
439
|
+
console.log('\n Your API key (shown ONCE — store it now, e.g. in your CI secret store):');
|
|
440
|
+
console.log(` ${result.api_key}\n`);
|
|
441
|
+
console.log(' Use it as the X-API-Key header, or write it to an env file next time with --env-file <path>.');
|
|
442
|
+
}
|
|
443
|
+
console.log(' Rotate later: POST /account/api-keys/rotate with this key (self-rotate).');
|
|
444
|
+
console.log(' Revoke later: from the dashboard, or DELETE /account/api-keys/:label with this key.\n');
|
|
445
|
+
}
|
|
446
|
+
|
|
287
447
|
// ─── auxilo status ──────────────────────────────────────────────────────────
|
|
288
448
|
|
|
289
449
|
async function cmdStatus() {
|
|
@@ -595,6 +755,19 @@ Commands:
|
|
|
595
755
|
setup Interactive install: client detection, MCP registration,
|
|
596
756
|
device-code login, extraction runner + hook, consent.
|
|
597
757
|
Flags: --re-auth, --base-url <url>
|
|
758
|
+
init Mint a SCOPED API key for CI or a second machine — no full setup,
|
|
759
|
+
no prompts (device-code approval in a browser is the only human
|
|
760
|
+
step). Does NOT touch ~/.auxilo/credentials.json unless --save.
|
|
761
|
+
Flags:
|
|
762
|
+
--scope <s> read | earnings-read | contribute (default:
|
|
763
|
+
contribute; admin is never issuable)
|
|
764
|
+
--label <name> key label (default: init-<hostname>)
|
|
765
|
+
--env-file <path> write AUXILO_API_KEY to an env file (0600,
|
|
766
|
+
updated in place) instead of printing the key
|
|
767
|
+
--save also write ~/.auxilo/credentials.json
|
|
768
|
+
--json machine-readable result on stdout
|
|
769
|
+
--no-browser don't try to open the approval URL
|
|
770
|
+
--base-url <url>
|
|
598
771
|
status Show install/auth/extraction status.
|
|
599
772
|
review Review YOUR pending-review learnings (from background extraction)
|
|
600
773
|
and approve/reject before anything goes public. Default: triage
|
|
@@ -627,6 +800,7 @@ async function main() {
|
|
|
627
800
|
const flags = parseFlags(process.argv);
|
|
628
801
|
switch (cmd) {
|
|
629
802
|
case 'setup': return cmdSetup(flags);
|
|
803
|
+
case 'init': return cmdInit(flags);
|
|
630
804
|
case 'status': return cmdStatus(flags);
|
|
631
805
|
case 'review': return cmdReview(flags);
|
|
632
806
|
case 'disable': return cmdDisable(flags);
|