aiden-runtime 4.8.0 → 4.9.0

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 (99) hide show
  1. package/README.md +88 -1
  2. package/dist/cli/v4/aidenCLI.js +35 -4
  3. package/dist/cli/v4/chatSession.js +43 -16
  4. package/dist/cli/v4/commands/daemon.js +47 -2
  5. package/dist/cli/v4/commands/daemonDoctor.js +212 -0
  6. package/dist/cli/v4/commands/daemonStatus.js +1 -1
  7. package/dist/cli/v4/commands/help.js +2 -0
  8. package/dist/cli/v4/commands/hooks.js +428 -0
  9. package/dist/cli/v4/commands/index.js +5 -1
  10. package/dist/cli/v4/commands/mcp.js +89 -1
  11. package/dist/cli/v4/commands/mcpClientInstall.js +359 -0
  12. package/dist/cli/v4/commands/memory.js +702 -0
  13. package/dist/cli/v4/commands/recovery.js +1 -1
  14. package/dist/cli/v4/commands/skin.js +7 -0
  15. package/dist/cli/v4/commands/theme.js +217 -0
  16. package/dist/cli/v4/commands/trigger.js +1 -1
  17. package/dist/cli/v4/commands/update.js +14 -2
  18. package/dist/cli/v4/design/tokens.js +52 -4
  19. package/dist/cli/v4/display.js +102 -46
  20. package/dist/cli/v4/pasteIntercept.js +214 -70
  21. package/dist/cli/v4/replyRenderer.js +145 -5
  22. package/dist/cli/v4/skinEngine.js +67 -0
  23. package/dist/core/v4/aidenAgent.js +45 -2
  24. package/dist/core/v4/daemon/api/runs.js +131 -0
  25. package/dist/core/v4/daemon/bootstrap.js +368 -13
  26. package/dist/core/v4/daemon/db/migrations.js +169 -0
  27. package/dist/core/v4/daemon/idempotency/runIdempotencyStore.js +128 -0
  28. package/dist/core/v4/daemon/incarnationStore.js +47 -0
  29. package/dist/core/v4/daemon/runs/attemptStore.js +67 -0
  30. package/dist/core/v4/daemon/runs/reclaim.js +88 -0
  31. package/dist/core/v4/daemon/runs/retryPolicy.js +73 -0
  32. package/dist/core/v4/daemon/runs/runWithRetry.js +80 -0
  33. package/dist/core/v4/daemon/runs/stuckAttemptWatchdog.js +72 -0
  34. package/dist/core/v4/daemon/spans/spanHelpers.js +272 -0
  35. package/dist/core/v4/daemon/spans/spanStore.js +113 -0
  36. package/dist/core/v4/daemon/triggerBus.js +50 -19
  37. package/dist/core/v4/hooks/auditQuery.js +67 -0
  38. package/dist/core/v4/hooks/dispatcher.js +286 -0
  39. package/dist/core/v4/hooks/index.js +46 -0
  40. package/dist/core/v4/hooks/lifecycle.js +27 -0
  41. package/dist/core/v4/hooks/manifest.js +142 -0
  42. package/dist/core/v4/hooks/registry.js +149 -0
  43. package/dist/core/v4/hooks/runtime/subprocessRunner.js +188 -0
  44. package/dist/core/v4/hooks/toolHookGate.js +76 -0
  45. package/dist/core/v4/hooks/trust.js +14 -0
  46. package/dist/core/v4/identity/contextManager.js +83 -0
  47. package/dist/core/v4/identity/daemonId.js +85 -0
  48. package/dist/core/v4/identity/enforcement.js +103 -0
  49. package/dist/core/v4/identity/executionContext.js +153 -0
  50. package/dist/core/v4/identity/hookExecution.js +62 -0
  51. package/dist/core/v4/identity/httpContext.js +68 -0
  52. package/dist/core/v4/identity/ids.js +185 -0
  53. package/dist/core/v4/identity/index.js +60 -0
  54. package/dist/core/v4/identity/subprocessContext.js +98 -0
  55. package/dist/core/v4/identity/traceparent.js +114 -0
  56. package/dist/core/v4/logger/index.js +3 -1
  57. package/dist/core/v4/logger/logger.js +28 -1
  58. package/dist/core/v4/logger/redact.js +149 -0
  59. package/dist/core/v4/logger/sinks/fileSink.js +13 -0
  60. package/dist/core/v4/logger/sinks/stdSink.js +19 -1
  61. package/dist/core/v4/mcp/install/backup.js +78 -0
  62. package/dist/core/v4/mcp/install/clientPaths.js +90 -0
  63. package/dist/core/v4/mcp/install/clients.js +203 -0
  64. package/dist/core/v4/mcp/install/healthCheck.js +83 -0
  65. package/dist/core/v4/mcp/install/jsoncMerge.js +174 -0
  66. package/dist/core/v4/mcp/install/profiles.js +109 -0
  67. package/dist/core/v4/mcp/install/wslDetect.js +62 -0
  68. package/dist/core/v4/memory/namespaceRegistry.js +117 -0
  69. package/dist/core/v4/memory/projectRoot.js +76 -0
  70. package/dist/core/v4/memory/reviewer/index.js +162 -0
  71. package/dist/core/v4/memory/reviewer/pendingStore.js +136 -0
  72. package/dist/core/v4/memory/reviewer/prompt.js +105 -0
  73. package/dist/core/v4/memory/reviewer/skipRules.js +92 -0
  74. package/dist/core/v4/memoryManager.js +57 -10
  75. package/dist/core/v4/paths.js +2 -0
  76. package/dist/core/v4/promptBuilder.js +6 -0
  77. package/dist/core/v4/subagent/spawnSubAgent.js +20 -7
  78. package/dist/core/v4/theme/bundledThemes.js +106 -0
  79. package/dist/core/v4/theme/themeLoader.js +160 -0
  80. package/dist/core/v4/theme/themeRegistry.js +97 -0
  81. package/dist/core/v4/theme/themeWatcher.js +95 -0
  82. package/dist/core/v4/toolRegistry.js +71 -8
  83. package/dist/core/v4/update/executeInstall.js +10 -6
  84. package/dist/core/v4/update/installMethodDetect.js +7 -0
  85. package/dist/core/version.js +67 -2
  86. package/dist/moat/approvalEngine.js +4 -0
  87. package/dist/moat/memoryGuard.js +8 -1
  88. package/dist/providers/v4/anthropicAdapter.js +10 -4
  89. package/dist/tools/v4/backends/local.js +19 -2
  90. package/dist/tools/v4/sessions/recallSession.js +6 -1
  91. package/package.json +3 -3
  92. package/plugins/aiden-plugin-chatgpt-plus/index.js +10 -1
  93. package/themes/default.yaml +52 -0
  94. package/themes/dracula.yaml +32 -0
  95. package/themes/light.yaml +32 -0
  96. package/themes/monochrome.yaml +31 -0
  97. package/themes/tokyo-night.yaml +32 -0
  98. package/dist/core/pluginSystem.js +0 -121
  99. package/dist/tools/v4/ui/_uiSmokeTool.js +0 -60
@@ -0,0 +1,428 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Shiva Deore (Taracod).
4
+ * Licensed under AGPL-3.0. See LICENSE for details.
5
+ *
6
+ * Aiden — local-first agent.
7
+ */
8
+ /**
9
+ * cli/v4/commands/hooks.ts — v4.9.0 Slice 12b.
10
+ *
11
+ * aiden hooks alias for `list`
12
+ * aiden hooks list [--json]
13
+ * aiden hooks show <hook_id>
14
+ * aiden hooks trust <hook_id> [--yes] (risk warning + y/N prompt unless --yes)
15
+ * aiden hooks revoke <hook_id> [--yes]
16
+ * aiden hooks rescan
17
+ * aiden hooks test <hook_id> [--event <name>] [--payload <json>]
18
+ * aiden hooks doctor [--json] [--fix]
19
+ * aiden hooks audit [--hook id] [--event n] [--since iso]
20
+ * [--status s] [--limit n] [--json]
21
+ *
22
+ * Doctor's `--fix` is intentionally conservative: it creates the hooks
23
+ * directory and runs a rescan, but NEVER auto-trusts anything (trust
24
+ * remains an explicit, deliberate user action).
25
+ */
26
+ var __importDefault = (this && this.__importDefault) || function (mod) {
27
+ return (mod && mod.__esModule) ? mod : { "default": mod };
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.runHooksSubcommand = runHooksSubcommand;
31
+ const node_fs_1 = require("node:fs");
32
+ const node_path_1 = __importDefault(require("node:path"));
33
+ const node_readline_1 = __importDefault(require("node:readline"));
34
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
35
+ const paths_1 = require("../../../core/v4/paths");
36
+ const daemonConfig_1 = require("../../../core/v4/daemon/daemonConfig");
37
+ const migrations_1 = require("../../../core/v4/daemon/db/migrations");
38
+ const registry_1 = require("../../../core/v4/hooks/registry");
39
+ const trust_1 = require("../../../core/v4/hooks/trust");
40
+ const dispatcher_1 = require("../../../core/v4/hooks/dispatcher");
41
+ const auditQuery_1 = require("../../../core/v4/hooks/auditQuery");
42
+ const projectRoot_1 = require("../../../core/v4/memory/projectRoot");
43
+ const noopOut = (s) => { process.stdout.write(s); };
44
+ const noopErr = (s) => { process.stderr.write(s); };
45
+ async function runHooksSubcommand(action, args, opts = {}) {
46
+ const out = opts.writeOut ?? noopOut;
47
+ const err = opts.writeErr ?? noopErr;
48
+ const json = args.includes('--json');
49
+ const yes = args.includes('--yes');
50
+ const positional = args.filter((a) => !a.startsWith('--'));
51
+ const paths = (0, paths_1.resolveAidenPaths)(opts.rootDir ? { rootOverride: opts.rootDir } : {});
52
+ let db;
53
+ try {
54
+ const dbp = opts.dbPath ?? (0, daemonConfig_1.daemonDbPath)(paths.root);
55
+ await node_fs_1.promises.mkdir(node_path_1.default.dirname(dbp), { recursive: true });
56
+ db = new better_sqlite3_1.default(dbp);
57
+ db.pragma('foreign_keys = ON');
58
+ (0, migrations_1.runMigrations)(db);
59
+ }
60
+ catch (e) {
61
+ err(`hooks: cannot open daemon db: ${e instanceof Error ? e.message : String(e)}\n`);
62
+ return 1;
63
+ }
64
+ const ctx = {
65
+ out, err, db, paths, json, yes, positional, args,
66
+ promptYesNo: opts.promptYesNo ?? defaultPromptYesNo,
67
+ };
68
+ const effective = action || 'list';
69
+ try {
70
+ switch (effective) {
71
+ case 'list': return await cmdList(ctx);
72
+ case 'show': return await cmdShow(ctx);
73
+ case 'trust': return await cmdTrust(ctx);
74
+ case 'revoke': return await cmdRevoke(ctx);
75
+ case 'rescan': return await cmdRescan(ctx);
76
+ case 'test': return await cmdTest(ctx);
77
+ case 'doctor': return await cmdDoctor(ctx);
78
+ case 'audit': return await cmdAudit(ctx);
79
+ case '--help':
80
+ case 'help': return cmdHelp(out);
81
+ default:
82
+ err(`Unknown hooks action: ${effective}\n`);
83
+ cmdHelp(err);
84
+ return 2;
85
+ }
86
+ }
87
+ finally {
88
+ try {
89
+ db.close();
90
+ }
91
+ catch { /* noop */ }
92
+ }
93
+ }
94
+ function cmdHelp(w) {
95
+ w('Usage: aiden hooks <action> [args...] [--json] [--yes]\n\n' +
96
+ 'Manage Aiden\'s manifest-driven hook subsystem.\n\n' +
97
+ 'Actions:\n' +
98
+ ' list All discovered hooks (default).\n' +
99
+ ' show <hook_id> Hook manifest + subscriptions + recent executions.\n' +
100
+ ' trust <hook_id> [--yes] Mark trusted + enabled (confirmation required).\n' +
101
+ ' revoke <hook_id> [--yes] Mark revoked + disabled (confirmation required).\n' +
102
+ ' rescan Walk hook dirs, surface new + drifted.\n' +
103
+ ' test <hook_id> [--event N] [--payload JSON]\n' +
104
+ ' Dry-run invocation (no counter mutation).\n' +
105
+ ' doctor [--json] [--fix] Pre-flight diagnostics.\n' +
106
+ ' audit [--hook id] [--event N] [--status S] [--since ISO] [--limit N] [--json]\n' +
107
+ ' Recent hook_executions rows.\n');
108
+ return 0;
109
+ }
110
+ function defaultPromptYesNo(question) {
111
+ return new Promise((resolve) => {
112
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stderr });
113
+ rl.question(question, (ans) => {
114
+ rl.close();
115
+ resolve(ans.trim().toLowerCase() === 'y' || ans.trim().toLowerCase() === 'yes');
116
+ });
117
+ });
118
+ }
119
+ function flagValue(args, name) {
120
+ const i = args.indexOf(name);
121
+ if (i < 0 || i + 1 >= args.length)
122
+ return undefined;
123
+ return args[i + 1];
124
+ }
125
+ function hooksWithSubs(db) {
126
+ const hooks = db.prepare(`SELECT *, consecutive_failures FROM hooks ORDER BY name`).all();
127
+ return hooks.map((h) => ({
128
+ ...h,
129
+ subs: db.prepare(`SELECT * FROM hook_subscriptions WHERE hook_id = ? ORDER BY event, priority DESC`).all(h.hook_id),
130
+ }));
131
+ }
132
+ async function cmdList(ctx) {
133
+ const rows = hooksWithSubs(ctx.db);
134
+ if (ctx.json) {
135
+ ctx.out(JSON.stringify(rows, null, 2) + '\n');
136
+ return 0;
137
+ }
138
+ if (rows.length === 0) {
139
+ ctx.out('no hooks discovered (drop a HOOK.yaml under ~/.aiden/hooks/<name>/ then run `aiden hooks rescan`)\n');
140
+ return 0;
141
+ }
142
+ for (const h of rows) {
143
+ const stateLabel = `${h.trust_state}, ${h.enabled ? 'enabled' : 'disabled'}`;
144
+ ctx.out(`${h.hook_id} ${h.name.padEnd(24)} ${stateLabel.padEnd(24)} ${h.source} (${h.subs.length} subscription${h.subs.length === 1 ? '' : 's'})\n`);
145
+ for (const s of h.subs) {
146
+ ctx.out(` ${s.event.padEnd(20)} authority=${s.authority.padEnd(18)} mode=${s.mode}\n`);
147
+ }
148
+ }
149
+ return 0;
150
+ }
151
+ async function cmdShow(ctx) {
152
+ const hookId = ctx.positional[0];
153
+ if (!hookId) {
154
+ ctx.err('show: missing <hook_id>\n');
155
+ return 2;
156
+ }
157
+ const h = ctx.db.prepare(`SELECT *, consecutive_failures FROM hooks WHERE hook_id = ?`).get(hookId);
158
+ if (!h) {
159
+ ctx.err(`show: hook not found: ${hookId}\n`);
160
+ return 1;
161
+ }
162
+ const subs = ctx.db.prepare(`SELECT * FROM hook_subscriptions WHERE hook_id = ? ORDER BY event, priority DESC`)
163
+ .all(hookId);
164
+ const grants = ctx.db.prepare(`SELECT capability, scope_json FROM hook_capability_grants WHERE hook_id = ?`)
165
+ .all(hookId);
166
+ const recent = (0, auditQuery_1.queryHookExecutions)(ctx.db, { hookId, limit: 10 });
167
+ if (ctx.json) {
168
+ ctx.out(JSON.stringify({ hook: h, subscriptions: subs, capabilities: grants, recent }, null, 2) + '\n');
169
+ return 0;
170
+ }
171
+ ctx.out(`Hook: ${h.name} (${h.hook_id})\n`);
172
+ ctx.out(` state: ${h.trust_state}, ${h.enabled ? 'enabled' : 'disabled'}\n`);
173
+ ctx.out(` source: ${h.source}\n`);
174
+ ctx.out(` manifest: ${h.manifest_path}\n`);
175
+ ctx.out(` hash: ${h.code_hash.slice(0, 16)}...\n`);
176
+ ctx.out(` consecutive failures: ${h.consecutive_failures}\n`);
177
+ ctx.out('Subscriptions:\n');
178
+ for (const s of subs) {
179
+ ctx.out(` ${s.event} authority=${s.authority} mode=${s.mode} priority=${s.priority}\n`);
180
+ ctx.out(` timeout=${s.timeout_ms}ms on_error=${s.on_error} on_timeout=${s.on_timeout}\n`);
181
+ if (s.matcher_json)
182
+ ctx.out(` matcher=${s.matcher_json}\n`);
183
+ }
184
+ if (grants.length > 0) {
185
+ ctx.out('Capabilities (declared, not enforced):\n');
186
+ for (const g of grants)
187
+ ctx.out(` ${g.capability} ${g.scope_json}\n`);
188
+ }
189
+ ctx.out(`Recent executions (last ${recent.length}):\n`);
190
+ for (const r of recent) {
191
+ ctx.out(` ${r.started_at} ${r.event.padEnd(20)} ${r.status.padEnd(8)} ${r.elapsed_ms}ms\n`);
192
+ }
193
+ return 0;
194
+ }
195
+ async function cmdTrust(ctx) {
196
+ const hookId = ctx.positional[0];
197
+ if (!hookId) {
198
+ ctx.err('trust: missing <hook_id>\n');
199
+ return 2;
200
+ }
201
+ const h = ctx.db.prepare(`SELECT * FROM hooks WHERE hook_id = ?`).get(hookId);
202
+ if (!h) {
203
+ ctx.err(`trust: hook not found: ${hookId}\n`);
204
+ return 1;
205
+ }
206
+ if (!ctx.yes) {
207
+ ctx.err(`Hook: ${h.name} (${h.hook_id})\n manifest: ${h.manifest_path}\n current state: ${h.trust_state}\n`);
208
+ ctx.err('RISK WARNING: This hook can affect tool execution. Only trust hooks you\'ve reviewed.\n');
209
+ const ok = await ctx.promptYesNo('Trust this hook? [y/N] ');
210
+ if (!ok) {
211
+ if (ctx.json)
212
+ ctx.out(JSON.stringify({ ok: false, reason: 'declined' }) + '\n');
213
+ else
214
+ ctx.err('trust: declined\n');
215
+ return 1;
216
+ }
217
+ }
218
+ (0, trust_1.markTrusted)(ctx.db, hookId);
219
+ if (ctx.json)
220
+ ctx.out(JSON.stringify({ ok: true, hook_id: hookId, trust_state: 'trusted', enabled: 1 }) + '\n');
221
+ else
222
+ ctx.out(`trusted ${h.name} (${hookId})\n`);
223
+ return 0;
224
+ }
225
+ async function cmdRevoke(ctx) {
226
+ const hookId = ctx.positional[0];
227
+ if (!hookId) {
228
+ ctx.err('revoke: missing <hook_id>\n');
229
+ return 2;
230
+ }
231
+ const h = ctx.db.prepare(`SELECT * FROM hooks WHERE hook_id = ?`).get(hookId);
232
+ if (!h) {
233
+ ctx.err(`revoke: hook not found: ${hookId}\n`);
234
+ return 1;
235
+ }
236
+ if (!ctx.yes) {
237
+ ctx.err(`Hook: ${h.name} (${h.hook_id}) current state: ${h.trust_state}\n`);
238
+ const ok = await ctx.promptYesNo('Revoke this hook? [y/N] ');
239
+ if (!ok) {
240
+ if (ctx.json)
241
+ ctx.out(JSON.stringify({ ok: false, reason: 'declined' }) + '\n');
242
+ else
243
+ ctx.err('revoke: declined\n');
244
+ return 1;
245
+ }
246
+ }
247
+ (0, trust_1.markRevoked)(ctx.db, hookId);
248
+ if (ctx.json)
249
+ ctx.out(JSON.stringify({ ok: true, hook_id: hookId, trust_state: 'revoked', enabled: 0 }) + '\n');
250
+ else
251
+ ctx.out(`revoked ${h.name} (${hookId})\n`);
252
+ return 0;
253
+ }
254
+ async function cmdRescan(ctx) {
255
+ const projectRoot = (0, projectRoot_1.findProjectRoot)(process.cwd());
256
+ const r = await (0, registry_1.scanAndLoadHooks)(ctx.db, { aidenRoot: ctx.paths.root, projectRoot });
257
+ if (ctx.json) {
258
+ ctx.out(JSON.stringify(r, null, 2) + '\n');
259
+ return 0;
260
+ }
261
+ ctx.out(`scanned: loaded=${r.loaded} errored=${r.errored} drifted=${r.drifted}\n`);
262
+ for (const e of r.errors)
263
+ ctx.out(` ERR ${e.path}: ${e.message}\n`);
264
+ return 0;
265
+ }
266
+ async function cmdTest(ctx) {
267
+ const hookId = ctx.positional[0];
268
+ if (!hookId) {
269
+ ctx.err('test: missing <hook_id>\n');
270
+ return 2;
271
+ }
272
+ const h = ctx.db.prepare(`SELECT * FROM hooks WHERE hook_id = ?`).get(hookId);
273
+ if (!h) {
274
+ ctx.err(`test: hook not found: ${hookId}\n`);
275
+ return 1;
276
+ }
277
+ // Temporarily ignore enabled/trust_state by flipping to trusted+enabled
278
+ // ONLY in-memory for the test dispatch? Cleaner: dispatch reads its
279
+ // own subs filter; we'll invoke against a synthetic event matching
280
+ // the hook's first subscription. The CLI is opinionated: pick the
281
+ // hook's first subscription as the test target.
282
+ const sub = ctx.db.prepare(`SELECT event FROM hook_subscriptions WHERE hook_id = ? ORDER BY priority DESC LIMIT 1`)
283
+ .get(hookId);
284
+ const event = flagValue(ctx.args, '--event') ?? sub?.event ?? 'tool.call.pre';
285
+ let payload = { tool_name: 'echo', synthetic: true };
286
+ const payloadRaw = flagValue(ctx.args, '--payload');
287
+ if (payloadRaw) {
288
+ try {
289
+ payload = JSON.parse(payloadRaw);
290
+ }
291
+ catch (e) {
292
+ ctx.err(`test: invalid --payload JSON: ${e instanceof Error ? e.message : String(e)}\n`);
293
+ return 2;
294
+ }
295
+ }
296
+ // To test even when untrusted/disabled, briefly flip state in-memory.
297
+ // We use a savepoint to ensure the flip is rolled back after.
298
+ const restoreState = h.trust_state;
299
+ const restoreEnabled = h.enabled;
300
+ ctx.db.prepare(`UPDATE hooks SET trust_state='trusted', enabled=1 WHERE hook_id=?`).run(hookId);
301
+ try {
302
+ const result = await (0, dispatcher_1.dispatchHook)(ctx.db, event, payload, { testMode: true, runId: 'test', traceId: 'test' });
303
+ const fired = result.fired.find((f) => f.hookId === hookId);
304
+ const exec = ctx.db.prepare(`SELECT * FROM hook_executions WHERE hook_id = ? ORDER BY started_at DESC LIMIT 1`).get(hookId);
305
+ if (ctx.json) {
306
+ ctx.out(JSON.stringify({ event, payload, dispatched: result, exec, fired }, null, 2) + '\n');
307
+ }
308
+ else {
309
+ ctx.out(`Test fired ${h.name} on ${event}:\n`);
310
+ ctx.out(` status: ${fired?.status ?? 'n/a'}\n`);
311
+ ctx.out(` decision: ${fired?.decision ?? 'n/a'}\n`);
312
+ ctx.out(` elapsed: ${fired?.elapsedMs ?? 0}ms\n`);
313
+ if (exec?.error_message)
314
+ ctx.out(` error: ${exec.error_message}\n`);
315
+ if (result.decision === 'block')
316
+ ctx.out(` AGGREGATE: block (reason=${result.reason ?? 'n/a'})\n`);
317
+ }
318
+ }
319
+ finally {
320
+ ctx.db.prepare(`UPDATE hooks SET trust_state=?, enabled=? WHERE hook_id=?`).run(restoreState, restoreEnabled, hookId);
321
+ }
322
+ return 0;
323
+ }
324
+ async function cmdDoctor(ctx) {
325
+ const fix = ctx.args.includes('--fix');
326
+ const checks = [];
327
+ const hooksDir = node_path_1.default.join(ctx.paths.root, 'hooks');
328
+ // 1. hooks dir exists
329
+ let dirExists = false;
330
+ try {
331
+ await node_fs_1.promises.access(hooksDir);
332
+ dirExists = true;
333
+ }
334
+ catch { /* missing */ }
335
+ if (!dirExists && fix) {
336
+ await node_fs_1.promises.mkdir(hooksDir, { recursive: true });
337
+ dirExists = true;
338
+ }
339
+ checks.push({
340
+ name: 'hooks_dir_exists',
341
+ status: dirExists ? 'ok' : 'warn',
342
+ detail: dirExists ? hooksDir : `missing: ${hooksDir} (run with --fix to create)`,
343
+ fixable: !dirExists,
344
+ });
345
+ // 2. schema v12
346
+ const v = ctx.db.prepare(`SELECT version FROM schema_version`).get();
347
+ checks.push({
348
+ name: 'schema_v12_current',
349
+ status: v && v.version >= 12 ? 'ok' : 'error',
350
+ detail: `db schema version = ${v?.version ?? 0}, latest = ${migrations_1.LATEST_SCHEMA_VERSION}`,
351
+ fixable: false,
352
+ });
353
+ // 3. parseable hooks
354
+ const rows = (0, registry_1.listHooks)(ctx.db);
355
+ const drifted = rows.filter((r) => r.trust_state === 'drifted');
356
+ const untrusted = rows.filter((r) => r.trust_state === 'untrusted');
357
+ const trustedEnabled = rows.filter((r) => r.trust_state === 'trusted' && r.enabled === 1);
358
+ checks.push({
359
+ name: 'drift_count',
360
+ status: drifted.length === 0 ? 'ok' : 'warn',
361
+ detail: `${drifted.length} drifted hook(s) (re-trust to re-enable)`,
362
+ fixable: false,
363
+ });
364
+ checks.push({
365
+ name: 'untrusted_count',
366
+ status: untrusted.length === 0 ? 'ok' : 'warn',
367
+ detail: `${untrusted.length} untrusted hook(s) installed but not yet enabled`,
368
+ fixable: false,
369
+ });
370
+ checks.push({
371
+ name: 'trusted_enabled_count',
372
+ status: 'ok',
373
+ detail: `${trustedEnabled.length} trusted + enabled hook(s)`,
374
+ fixable: false,
375
+ });
376
+ // 4. auto-disabled last 24h — revoked + non-zero recent executions
377
+ const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
378
+ const recentStatus = (0, auditQuery_1.countByStatus)(ctx.db, since);
379
+ const recentNonOk = (recentStatus.crash ?? 0) + (recentStatus.timeout ?? 0) + (recentStatus.malformed_output ?? 0);
380
+ const recentRevoked = ctx.db.prepare(`SELECT COUNT(*) AS n FROM hooks WHERE trust_state='revoked' AND updated_at >= ?`).get(since);
381
+ checks.push({
382
+ name: 'auto_disabled_24h',
383
+ status: recentRevoked.n === 0 ? 'ok' : 'warn',
384
+ detail: `${recentRevoked.n} hook(s) revoked in last 24h (${recentNonOk} non-ok executions)`,
385
+ fixable: false,
386
+ });
387
+ // 5. high-failure-rate hooks
388
+ const fr = (0, auditQuery_1.failureRates)(ctx.db, 100).filter((f) => f.failureRate > 0.1);
389
+ checks.push({
390
+ name: 'high_failure_rate',
391
+ status: fr.length === 0 ? 'ok' : 'warn',
392
+ detail: fr.length === 0 ? 'no hooks > 10% failure last 100 executions' : `${fr.length} hook(s) > 10% failure: ${fr.map((r) => r.hook_id).join(', ')}`,
393
+ fixable: false,
394
+ });
395
+ if (ctx.json) {
396
+ ctx.out(JSON.stringify({ checks, fix }, null, 2) + '\n');
397
+ return checks.some((c) => c.status === 'error') ? 1 : 0;
398
+ }
399
+ for (const c of checks) {
400
+ const icon = c.status === 'ok' ? '[ok] ' : c.status === 'warn' ? '[warn] ' : '[ERR] ';
401
+ ctx.out(`${icon}${c.name.padEnd(24)} ${c.detail}\n`);
402
+ }
403
+ return checks.some((c) => c.status === 'error') ? 1 : 0;
404
+ }
405
+ async function cmdAudit(ctx) {
406
+ const limitRaw = flagValue(ctx.args, '--limit');
407
+ const q = {
408
+ hookId: flagValue(ctx.args, '--hook'),
409
+ event: flagValue(ctx.args, '--event'),
410
+ status: flagValue(ctx.args, '--status'),
411
+ since: flagValue(ctx.args, '--since'),
412
+ limit: limitRaw ? Number.parseInt(limitRaw, 10) : 50,
413
+ };
414
+ const rows = (0, auditQuery_1.queryHookExecutions)(ctx.db, q);
415
+ if (ctx.json) {
416
+ ctx.out(JSON.stringify(rows, null, 2) + '\n');
417
+ return 0;
418
+ }
419
+ if (rows.length === 0) {
420
+ ctx.out('no executions match\n');
421
+ return 0;
422
+ }
423
+ ctx.out(`${'started_at'.padEnd(26)} ${'hook'.padEnd(18)} ${'event'.padEnd(20)} ${'status'.padEnd(10)} ${'decision'.padEnd(10)} elapsed\n`);
424
+ for (const r of rows) {
425
+ ctx.out(`${r.started_at.padEnd(26)} ${(r.hook_name ?? r.hook_id).slice(0, 18).padEnd(18)} ${r.event.padEnd(20)} ${r.status.padEnd(10)} ${(r.decision ?? '-').padEnd(10)} ${r.elapsed_ms}ms\n`);
426
+ }
427
+ return 0;
428
+ }
@@ -12,7 +12,7 @@
12
12
  * and registers each on the global CommandRegistry at boot.
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.allCommands = exports.walkthrough = exports.recovery = exports.spawnPause = exports.plannerGuard = exports.suggestions = exports.daemonStatus = exports.browserDepth = exports.tce = exports.sandbox = exports.update = exports.reloadSoul = exports.history = exports.show = exports.status = exports.voice = exports.channel = exports.setup = exports.cron = exports.doctor = exports.license = exports.auth = exports.plugins = exports.streaming = exports.debugPrompt = exports.identity = exports.providers = exports.quit = exports.clear = exports.verbose = exports.reasoning = exports.reloadMcp = exports.skills = exports.skin = exports.yolo = exports.usage = exports.compress = exports.title = exports.save = exports.personality = exports.model = exports.tools = exports.help = void 0;
15
+ exports.allCommands = exports.walkthrough = exports.recovery = exports.spawnPause = exports.plannerGuard = exports.suggestions = exports.daemonStatus = exports.browserDepth = exports.tce = exports.sandbox = exports.update = exports.reloadSoul = exports.history = exports.show = exports.status = exports.voice = exports.channel = exports.setup = exports.cron = exports.doctor = exports.license = exports.auth = exports.plugins = exports.streaming = exports.debugPrompt = exports.identity = exports.providers = exports.quit = exports.clear = exports.verbose = exports.reasoning = exports.reloadMcp = exports.skills = exports.theme = exports.skin = exports.yolo = exports.usage = exports.compress = exports.title = exports.save = exports.personality = exports.model = exports.tools = exports.help = void 0;
16
16
  const help_1 = require("./help");
17
17
  Object.defineProperty(exports, "help", { enumerable: true, get: function () { return help_1.help; } });
18
18
  const tools_1 = require("./tools");
@@ -33,6 +33,9 @@ const yolo_1 = require("./yolo");
33
33
  Object.defineProperty(exports, "yolo", { enumerable: true, get: function () { return yolo_1.yolo; } });
34
34
  const skin_1 = require("./skin");
35
35
  Object.defineProperty(exports, "skin", { enumerable: true, get: function () { return skin_1.skin; } });
36
+ // v4.9.0 Slice 1a — unified theme system.
37
+ const theme_1 = require("./theme");
38
+ Object.defineProperty(exports, "theme", { enumerable: true, get: function () { return theme_1.theme; } });
36
39
  const skills_1 = require("./skills");
37
40
  Object.defineProperty(exports, "skills", { enumerable: true, get: function () { return skills_1.skills; } });
38
41
  const reloadMcp_1 = require("./reloadMcp");
@@ -119,6 +122,7 @@ exports.allCommands = [
119
122
  usage_1.usage,
120
123
  yolo_1.yolo,
121
124
  skin_1.skin,
125
+ theme_1.theme,
122
126
  skills_1.skills,
123
127
  plugins_1.plugins,
124
128
  auth_1.auth,
@@ -32,6 +32,39 @@
32
32
  * tools by default; opting in means the user accepted server-side
33
33
  * execution risk at config time.
34
34
  */
35
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
36
+ if (k2 === undefined) k2 = k;
37
+ var desc = Object.getOwnPropertyDescriptor(m, k);
38
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
39
+ desc = { enumerable: true, get: function() { return m[k]; } };
40
+ }
41
+ Object.defineProperty(o, k2, desc);
42
+ }) : (function(o, m, k, k2) {
43
+ if (k2 === undefined) k2 = k;
44
+ o[k2] = m[k];
45
+ }));
46
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
47
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
48
+ }) : function(o, v) {
49
+ o["default"] = v;
50
+ });
51
+ var __importStar = (this && this.__importStar) || (function () {
52
+ var ownKeys = function(o) {
53
+ ownKeys = Object.getOwnPropertyNames || function (o) {
54
+ var ar = [];
55
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
56
+ return ar;
57
+ };
58
+ return ownKeys(o);
59
+ };
60
+ return function (mod) {
61
+ if (mod && mod.__esModule) return mod;
62
+ var result = {};
63
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
64
+ __setModuleDefault(result, mod);
65
+ return result;
66
+ };
67
+ })();
35
68
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
69
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
70
  };
@@ -319,8 +352,55 @@ async function wireSubagentFanout(opts) {
319
352
  async function runMcpSubcommand(action, opts = {}) {
320
353
  const writeOut = opts.writeOut ?? ((t) => process.stdout.write(t));
321
354
  const writeErr = opts.writeErr ?? ((t) => process.stderr.write(t));
355
+ const extraArgs = opts.args ?? [];
356
+ const target = opts.target;
322
357
  switch (action) {
323
358
  case 'serve': {
359
+ // v4.9.0 Slice 2a — `--health-check` flag: spawn the runtime,
360
+ // emit a single JSON status line on stdout, exit. Used by
361
+ // `aiden mcp init <client>` to confirm the wired entry actually
362
+ // launches successfully on the user's machine.
363
+ if (extraArgs.includes('--health-check') || target === '--health-check') {
364
+ try {
365
+ const { registry, skillLoader } = await buildMcpRuntime(opts);
366
+ const diag = await (0, diagnostics_1.collectMcpDiagnostics)(registry, skillLoader);
367
+ writeOut(JSON.stringify({
368
+ status: 'ok',
369
+ tools: diag.toolsExposed,
370
+ skills: diag.skillsTotal,
371
+ version: diag.build,
372
+ }) + '\n');
373
+ return 0;
374
+ }
375
+ catch (err) {
376
+ writeOut(JSON.stringify({
377
+ status: 'error',
378
+ error: err.message,
379
+ }) + '\n');
380
+ return 1;
381
+ }
382
+ }
383
+ // v4.9.0 Slice 2b — `--profile <name>` resolves a tool allowlist
384
+ // and installs it into the env BEFORE buildMcpRuntime runs (which
385
+ // internally calls readToolBridgeEnv). Flag wins over inherited
386
+ // env vars so the client-config-pinned profile is authoritative.
387
+ const profileIdx = extraArgs.indexOf('--profile');
388
+ if (profileIdx !== -1) {
389
+ const profileName = extraArgs[profileIdx + 1];
390
+ if (!profileName) {
391
+ writeErr('--profile requires a name.\n');
392
+ return 1;
393
+ }
394
+ try {
395
+ const { resolveProfile, applyProfileToEnv } = await Promise.resolve().then(() => __importStar(require('../../../core/v4/mcp/install/profiles')));
396
+ const profile = resolveProfile(profileName, '');
397
+ applyProfileToEnv(profile);
398
+ }
399
+ catch (err) {
400
+ writeErr(`${err.message}\n`);
401
+ return 1;
402
+ }
403
+ }
324
404
  const { registry, skillLoader, toolContext, logger } = await buildMcpRuntime(opts);
325
405
  await (0, stdioServer_1.startStdioMcpServer)({
326
406
  registry,
@@ -375,9 +455,17 @@ async function runMcpSubcommand(action, opts = {}) {
375
455
  }
376
456
  return 0;
377
457
  }
458
+ case 'init':
459
+ case 'doctor':
460
+ case 'repair':
461
+ case 'uninstall': {
462
+ // v4.9.0 Slice 2a / 2b — client-config install / diagnose / fix / remove.
463
+ const { runClientCommand } = await Promise.resolve().then(() => __importStar(require('./mcpClientInstall')));
464
+ return runClientCommand(action, target, extraArgs, { writeOut, writeErr });
465
+ }
378
466
  default: {
379
467
  writeErr(`Unknown 'aiden mcp' action: ${action}\n`);
380
- writeErr(`Actions: serve | status | tools\n`);
468
+ writeErr(`Actions: serve | status | tools | init <client> | doctor <client> | repair <client> | uninstall <client>\n`);
381
469
  return 1;
382
470
  }
383
471
  }