@aaac/observability 0.1.7 → 0.1.9

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/dist/cli/index.js CHANGED
@@ -3,13 +3,18 @@ import {
3
3
  DEFAULT_DB_PATH,
4
4
  SqliteQueryAdapter,
5
5
  createPipeline,
6
+ emitHumanInstruction,
7
+ emitPromotionPr,
8
+ emitQualityGateResult,
9
+ evaluateMapping,
6
10
  generateId,
7
- isoToUnixNano
8
- } from "../chunk-M3KM2QTL.js";
11
+ isoToUnixNano,
12
+ loadEventMappingConfig
13
+ } from "../chunk-GRL7RHMS.js";
9
14
 
10
15
  // src/cli/index.ts
11
16
  import { readFileSync } from "fs";
12
- import { dirname, resolve } from "path";
17
+ import { dirname as dirname2, resolve } from "path";
13
18
  import { fileURLToPath } from "url";
14
19
 
15
20
  // src/generated/program.ts
@@ -88,6 +93,34 @@ command_sets:
88
93
  stderr:
89
94
  format: text
90
95
 
96
+ record-hook:
97
+ summary: Record a Cursor/git hook event \u2014 parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)
98
+ arguments:
99
+ - name: hook-name
100
+ index: 0
101
+ required: true
102
+ schema: { type: string }
103
+ description: "Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)"
104
+ options:
105
+ - name: mapping-config
106
+ schema: { type: string }
107
+ description: "Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)"
108
+ - name: db
109
+ schema: { type: string }
110
+ description: Path to observability SQLite database (overrides default)
111
+ exits:
112
+ '0':
113
+ description: Hook recorded (or no-op when stdin/mapping unavailable) \u2014 always fail-open
114
+ stdout:
115
+ format: json
116
+ schema:
117
+ type: object
118
+ properties:
119
+ recorded:
120
+ type: integer
121
+ fallback:
122
+ type: boolean
123
+
91
124
  query:
92
125
  summary: Query the observability store via QueryAdapter
93
126
  options:
@@ -137,7 +170,7 @@ command_sets:
137
170
  stderr:
138
171
  format: text
139
172
  `;
140
- var CONTRACT_JSON_STR = '{\n "cli_contracts": "0.1.0",\n "info": {\n "title": "AaaC Observability CLI",\n "version": "0.1.0",\n "description": "aaac-observ \u2014 external event registration and query for @aaac/observability"\n },\n "command_sets": {\n "aaac-observ": {\n "summary": "aaac-observ \u2014 register external events and query the observability store",\n "executable": "aaac-observ",\n "commands": {\n "record": {\n "summary": "Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)",\n "options": [\n {\n "name": "event-type",\n "aliases": [\n "t"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Event type (e.g. promotion.commit, process.edit, agent.session)"\n },\n {\n "name": "lifecycle",\n "aliases": [\n "l"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "open",\n "close",\n "event",\n "instant"\n ]\n },\n "description": "Event lifecycle phase: open | close | event | instant"\n },\n {\n "name": "span-id",\n "aliases": [\n "s"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Span ID (auto-generated if omitted)"\n },\n {\n "name": "parent-span-id",\n "schema": {\n "type": "string"\n },\n "description": "Parent span ID for hierarchy"\n },\n {\n "name": "session-id",\n "schema": {\n "type": "string"\n },\n "description": "Session ID to associate with the event"\n },\n {\n "name": "trace-id",\n "schema": {\n "type": "string"\n },\n "description": "Trace ID for distributed tracing"\n },\n {\n "name": "source",\n "schema": {\n "type": "string"\n },\n "description": "Event source identifier (default: external)"\n },\n {\n "name": "attr",\n "schema": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "description": "Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)"\n },\n {\n "name": "link",\n "schema": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "description": "Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)"\n },\n {\n "name": "db",\n "schema": {\n "type": "string"\n },\n "description": "Path to observability SQLite database (overrides default)"\n }\n ],\n "exits": {\n "0": {\n "description": "Event registered successfully",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "properties": {\n "eventId": {\n "type": "string"\n },\n "spanId": {\n "type": "string"\n }\n }\n }\n }\n },\n "1": {\n "description": "Registration failed (validation error or write error)",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "query": {\n "summary": "Query the observability store via QueryAdapter",\n "options": [\n {\n "name": "kind",\n "aliases": [\n "k"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "trace",\n "span",\n "search",\n "links"\n ]\n },\n "description": "Query kind: trace | span | search | links"\n },\n {\n "name": "trace-id",\n "schema": {\n "type": "string"\n },\n "description": "Trace ID for trace queries (--kind trace)"\n },\n {\n "name": "span-id",\n "aliases": [\n "s"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Span ID for span/links queries (--kind span or --kind links)"\n },\n {\n "name": "event-type",\n "schema": {\n "type": "string"\n },\n "description": "Event type filter for search queries (--kind search)"\n },\n {\n "name": "task-id",\n "schema": {\n "type": "string"\n },\n "description": "Task ID filter for search queries (--kind search)"\n },\n {\n "name": "from",\n "schema": {\n "type": "string"\n },\n "description": "Start time in ISO 8601 format for search queries (--kind search)"\n },\n {\n "name": "to",\n "schema": {\n "type": "string"\n },\n "description": "End time in ISO 8601 format for search queries (--kind search)"\n },\n {\n "name": "direction",\n "aliases": [\n "d"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "forward",\n "reverse",\n "both"\n ]\n },\n "description": "Link traversal direction for links queries (default: both)"\n },\n {\n "name": "db",\n "schema": {\n "type": "string"\n },\n "description": "Path to observability SQLite database (overrides default)"\n }\n ],\n "exits": {\n "0": {\n "description": "Query succeeded \u2014 results as JSON array",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object"\n }\n }\n },\n "1": {\n "description": "Query failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n }\n }\n }\n }\n}';
173
+ var CONTRACT_JSON_STR = '{\n "cli_contracts": "0.1.0",\n "info": {\n "title": "AaaC Observability CLI",\n "version": "0.1.0",\n "description": "aaac-observ \u2014 external event registration and query for @aaac/observability"\n },\n "command_sets": {\n "aaac-observ": {\n "summary": "aaac-observ \u2014 register external events and query the observability store",\n "executable": "aaac-observ",\n "commands": {\n "record": {\n "summary": "Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)",\n "options": [\n {\n "name": "event-type",\n "aliases": [\n "t"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Event type (e.g. promotion.commit, process.edit, agent.session)"\n },\n {\n "name": "lifecycle",\n "aliases": [\n "l"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "open",\n "close",\n "event",\n "instant"\n ]\n },\n "description": "Event lifecycle phase: open | close | event | instant"\n },\n {\n "name": "span-id",\n "aliases": [\n "s"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Span ID (auto-generated if omitted)"\n },\n {\n "name": "parent-span-id",\n "schema": {\n "type": "string"\n },\n "description": "Parent span ID for hierarchy"\n },\n {\n "name": "session-id",\n "schema": {\n "type": "string"\n },\n "description": "Session ID to associate with the event"\n },\n {\n "name": "trace-id",\n "schema": {\n "type": "string"\n },\n "description": "Trace ID for distributed tracing"\n },\n {\n "name": "source",\n "schema": {\n "type": "string"\n },\n "description": "Event source identifier (default: external)"\n },\n {\n "name": "attr",\n "schema": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "description": "Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)"\n },\n {\n "name": "link",\n "schema": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "description": "Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)"\n },\n {\n "name": "db",\n "schema": {\n "type": "string"\n },\n "description": "Path to observability SQLite database (overrides default)"\n }\n ],\n "exits": {\n "0": {\n "description": "Event registered successfully",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "properties": {\n "eventId": {\n "type": "string"\n },\n "spanId": {\n "type": "string"\n }\n }\n }\n }\n },\n "1": {\n "description": "Registration failed (validation error or write error)",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "record-hook": {\n "summary": "Record a Cursor/git hook event \u2014 parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)",\n "arguments": [\n {\n "name": "hook-name",\n "index": 0,\n "required": true,\n "schema": {\n "type": "string"\n },\n "description": "Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)"\n }\n ],\n "options": [\n {\n "name": "mapping-config",\n "schema": {\n "type": "string"\n },\n "description": "Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)"\n },\n {\n "name": "db",\n "schema": {\n "type": "string"\n },\n "description": "Path to observability SQLite database (overrides default)"\n }\n ],\n "exits": {\n "0": {\n "description": "Hook recorded (or no-op when stdin/mapping unavailable) \u2014 always fail-open",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "properties": {\n "recorded": {\n "type": "integer"\n },\n "fallback": {\n "type": "boolean"\n }\n }\n }\n }\n }\n }\n },\n "query": {\n "summary": "Query the observability store via QueryAdapter",\n "options": [\n {\n "name": "kind",\n "aliases": [\n "k"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "trace",\n "span",\n "search",\n "links"\n ]\n },\n "description": "Query kind: trace | span | search | links"\n },\n {\n "name": "trace-id",\n "schema": {\n "type": "string"\n },\n "description": "Trace ID for trace queries (--kind trace)"\n },\n {\n "name": "span-id",\n "aliases": [\n "s"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Span ID for span/links queries (--kind span or --kind links)"\n },\n {\n "name": "event-type",\n "schema": {\n "type": "string"\n },\n "description": "Event type filter for search queries (--kind search)"\n },\n {\n "name": "task-id",\n "schema": {\n "type": "string"\n },\n "description": "Task ID filter for search queries (--kind search)"\n },\n {\n "name": "from",\n "schema": {\n "type": "string"\n },\n "description": "Start time in ISO 8601 format for search queries (--kind search)"\n },\n {\n "name": "to",\n "schema": {\n "type": "string"\n },\n "description": "End time in ISO 8601 format for search queries (--kind search)"\n },\n {\n "name": "direction",\n "aliases": [\n "d"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "forward",\n "reverse",\n "both"\n ]\n },\n "description": "Link traversal direction for links queries (default: both)"\n },\n {\n "name": "db",\n "schema": {\n "type": "string"\n },\n "description": "Path to observability SQLite database (overrides default)"\n }\n ],\n "exits": {\n "0": {\n "description": "Query succeeded \u2014 results as JSON array",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object"\n }\n }\n },\n "1": {\n "description": "Query failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n }\n }\n }\n }\n}';
141
174
 
142
175
  // src/generated/program.ts
143
176
  function createProgram(handlers2, version) {
@@ -146,6 +179,9 @@ function createProgram(handlers2, version) {
146
179
  program2.command("record").description("Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)").option("-t, --event-type <value>", "Event type (e.g. promotion.commit, process.edit, agent.session)").option("-l, --lifecycle <value>", "Event lifecycle phase: open | close | event | instant").option("-s, --span-id <value>", "Span ID (auto-generated if omitted)").option("--parent-span-id <value>", "Parent span ID for hierarchy").option("--session-id <value>", "Session ID to associate with the event").option("--trace-id <value>", "Trace ID for distributed tracing").option("--source <value>", "Event source identifier (default: external)").option("--attr <value>", "Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)").option("--link <value>", "Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)").option("--db <value>", "Path to observability SQLite database (overrides default)").action(async (opts, cmd) => {
147
180
  await handlers2.record(opts, cmd.optsWithGlobals());
148
181
  });
182
+ program2.command("record-hook").description("Record a Cursor/git hook event \u2014 parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)").argument("<hook-name>", "Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)").option("--mapping-config <value>", "Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)").option("--db <value>", "Path to observability SQLite database (overrides default)").action(async (hookName, opts, cmd) => {
183
+ await handlers2.recordHook(hookName, opts, cmd.optsWithGlobals());
184
+ });
149
185
  program2.command("query").description("Query the observability store via QueryAdapter").option("-k, --kind <value>", "Query kind: trace | span | search | links").option("--trace-id <value>", "Trace ID for trace queries (--kind trace)").option("-s, --span-id <value>", "Span ID for span/links queries (--kind span or --kind links)").option("--event-type <value>", "Event type filter for search queries (--kind search)").option("--task-id <value>", "Task ID filter for search queries (--kind search)").option("--from <value>", "Start time in ISO 8601 format for search queries (--kind search)").option("--to <value>", "End time in ISO 8601 format for search queries (--kind search)").option("-d, --direction <value>", "Link traversal direction for links queries (default: both)").option("--db <value>", "Path to observability SQLite database (overrides default)").action(async (opts, cmd) => {
150
186
  await handlers2.query(opts, cmd.optsWithGlobals());
151
187
  });
@@ -166,7 +202,7 @@ function createProgram(handlers2, version) {
166
202
  type: "cli-contracts/extract",
167
203
  extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
168
204
  specVersion: doc.cli_contracts ?? "0.1.0",
169
- commands: ["aaac-observ.record", "aaac-observ.query"]
205
+ commands: ["aaac-observ.record", "aaac-observ.record-hook", "aaac-observ.query"]
170
206
  };
171
207
  }
172
208
  Object.assign(out, doc);
@@ -183,7 +219,7 @@ function createProgram(handlers2, version) {
183
219
  yamlLines.push("extractedAt: " + (/* @__PURE__ */ new Date()).toISOString());
184
220
  yamlLines.push("spec_version: " + (doc.cli_contracts ?? "0.1.0"));
185
221
  yamlLines.push("commands:");
186
- for (const id of ["aaac-observ.record", "aaac-observ.query"]) {
222
+ for (const id of ["aaac-observ.record", "aaac-observ.record-hook", "aaac-observ.query"]) {
187
223
  yamlLines.push(" - " + id);
188
224
  }
189
225
  }
@@ -324,6 +360,197 @@ async function handleRecord(options, _parentOpts) {
324
360
  process.stdout.write(JSON.stringify(result) + "\n");
325
361
  }
326
362
 
363
+ // src/cli/record-hook.ts
364
+ import { mkdir, rename, unlink, writeFile } from "fs/promises";
365
+ import { dirname, join } from "path";
366
+ var GIT_CONTEXT_COMMAND_RE = /\bgit\s+(?:commit|push|merge)\b/;
367
+ var GIT_HOOK_NAMES = /* @__PURE__ */ new Set([
368
+ "pre-commit",
369
+ "post-commit",
370
+ "pre-push",
371
+ "post-merge",
372
+ "post-checkout"
373
+ ]);
374
+ function defaultMappingConfigPath(dbPath) {
375
+ return join(dirname(dbPath), "config", "event-mapping.json");
376
+ }
377
+ function contextFilePath(dbPath) {
378
+ return join(dirname(dbPath), ".observ-context.json");
379
+ }
380
+ async function readStdin() {
381
+ if (process.stdin.isTTY) return "";
382
+ return new Promise((resolve2) => {
383
+ let data = "";
384
+ let settled = false;
385
+ const finish = () => {
386
+ if (settled) return;
387
+ settled = true;
388
+ resolve2(data);
389
+ };
390
+ try {
391
+ process.stdin.setEncoding("utf8");
392
+ process.stdin.on("data", (chunk) => {
393
+ data += chunk;
394
+ });
395
+ process.stdin.on("end", finish);
396
+ process.stdin.on("error", finish);
397
+ process.stdin.on("close", finish);
398
+ const t = setTimeout(finish, 1e3);
399
+ if (typeof t.unref === "function") t.unref();
400
+ } catch {
401
+ finish();
402
+ }
403
+ });
404
+ }
405
+ function parseHookInput(raw) {
406
+ const trimmed = raw.trim();
407
+ if (!trimmed) return {};
408
+ try {
409
+ const parsed = JSON.parse(trimmed);
410
+ return parsed !== null && typeof parsed === "object" ? parsed : {};
411
+ } catch {
412
+ return {};
413
+ }
414
+ }
415
+ function asString(value) {
416
+ return typeof value === "string" && value.length > 0 ? value : void 0;
417
+ }
418
+ function asNumber(value) {
419
+ if (typeof value === "number" && Number.isFinite(value)) return value;
420
+ if (typeof value === "string" && value.trim() !== "") {
421
+ const n = Number(value);
422
+ if (Number.isFinite(n)) return n;
423
+ }
424
+ return void 0;
425
+ }
426
+ function emitMappedSpans(collector, hook, rule, hookInput, sessionId) {
427
+ const { spans, links } = evaluateMapping(rule, hookInput);
428
+ const linksBySpan = /* @__PURE__ */ new Map();
429
+ for (const link of links) {
430
+ const arr = linksBySpan.get(link.fromSpanId) ?? [];
431
+ arr.push({ targetSpanId: link.toSpanId, linkType: link.linkType });
432
+ linksBySpan.set(link.fromSpanId, arr);
433
+ }
434
+ const parentConversationId = hook === "subagentStart" ? asString(hookInput.parent_conversation_id) : void 0;
435
+ for (const span of spans) {
436
+ const attributes = { ...span.attributes };
437
+ if (sessionId) attributes["session_id"] = sessionId;
438
+ if (parentConversationId) {
439
+ attributes["parent_conversation_id"] = parentConversationId;
440
+ }
441
+ collector.emit({
442
+ source: "cursor-hook",
443
+ eventType: span.eventType,
444
+ lifecycle: span.lifecycle,
445
+ spanId: span.spanId,
446
+ parentSpanId: span.parentSpanId,
447
+ attributes,
448
+ links: linksBySpan.get(span.spanId) ?? []
449
+ });
450
+ }
451
+ return spans.length;
452
+ }
453
+ function emitGenericFallback(collector, hook, sessionId) {
454
+ const eventType = (GIT_HOOK_NAMES.has(hook) ? "git." : "cursor.") + hook;
455
+ const attributes = { "hook.name": hook };
456
+ if (sessionId) attributes["session_id"] = sessionId;
457
+ collector.emit({
458
+ source: "cursor-hook",
459
+ eventType,
460
+ lifecycle: "instant",
461
+ attributes
462
+ });
463
+ return 1;
464
+ }
465
+ function emitHumanEvents(collector, hook, hookInput, sessionId) {
466
+ let count = 0;
467
+ if (hook === "beforeSubmitPrompt") {
468
+ const prompt = asString(hookInput.prompt);
469
+ if (prompt !== void 0) {
470
+ const attachments = Array.isArray(hookInput.attachments) ? hookInput.attachments : [];
471
+ emitHumanInstruction(collector, { sessionId, prompt, attachments });
472
+ count += 1;
473
+ }
474
+ } else if (hook === "afterShellExecution") {
475
+ const command = asString(hookInput.command);
476
+ if (command !== void 0) {
477
+ const exitCode = asNumber(hookInput.exit_code) ?? asNumber(hookInput.exitCode) ?? 0;
478
+ const durationMs = asNumber(hookInput.duration);
479
+ if (emitQualityGateResult(collector, { sessionId, command, exitCode, durationMs })) {
480
+ count += 1;
481
+ }
482
+ const output = asString(hookInput.output) ?? "";
483
+ if (emitPromotionPr(collector, { sessionId, command, output })) {
484
+ count += 1;
485
+ }
486
+ }
487
+ }
488
+ return count;
489
+ }
490
+ async function manageContextFile(hook, hookInput, sessionId, dbPath) {
491
+ const command = asString(hookInput.command) ?? "";
492
+ const isGitContextCommand = GIT_CONTEXT_COMMAND_RE.test(command);
493
+ const file = contextFilePath(dbPath);
494
+ if (hook === "beforeShellExecution" && isGitContextCommand) {
495
+ await mkdir(dirname(file), { recursive: true });
496
+ const payload = JSON.stringify({
497
+ session_id: sessionId || null,
498
+ nonce: generateId(),
499
+ created_at_ms: Date.now()
500
+ }) + "\n";
501
+ const tmp = `${file}.${process.pid}.${generateId()}.tmp`;
502
+ await writeFile(tmp, payload, "utf8");
503
+ await rename(tmp, file);
504
+ } else if (hook === "afterShellExecution" && isGitContextCommand) {
505
+ await unlink(file).catch(() => {
506
+ });
507
+ }
508
+ }
509
+ async function runRecordHook(args) {
510
+ const { hook, hookInput, dbPath, mappingConfigPath } = args;
511
+ const sessionId = asString(hookInput.conversation_id) ?? "";
512
+ await manageContextFile(hook, hookInput, sessionId, dbPath).catch(() => {
513
+ });
514
+ let config;
515
+ try {
516
+ config = await loadEventMappingConfig(mappingConfigPath);
517
+ } catch {
518
+ config = void 0;
519
+ }
520
+ const rule = config?.mappings?.[hook];
521
+ let recorded = 0;
522
+ let fallback = false;
523
+ const { collector, sink } = createPipeline({ dbPath, enrichRules: [] });
524
+ try {
525
+ if (rule) {
526
+ recorded += emitMappedSpans(collector, hook, rule, hookInput, sessionId);
527
+ } else {
528
+ recorded += emitGenericFallback(collector, hook, sessionId);
529
+ fallback = true;
530
+ }
531
+ recorded += emitHumanEvents(collector, hook, hookInput, sessionId);
532
+ } finally {
533
+ sink.close();
534
+ }
535
+ return { recorded, fallback };
536
+ }
537
+ async function handleRecordHook(hookName, options, _parentOpts) {
538
+ const hook = hookName ?? "unknown";
539
+ const dbPath = options.db ?? DEFAULT_DB_PATH;
540
+ const mappingConfigPath = options.mappingConfig ?? defaultMappingConfigPath(dbPath);
541
+ let result = { recorded: 0, fallback: false };
542
+ try {
543
+ const hookInput = parseHookInput(await readStdin());
544
+ result = await runRecordHook({ hook, hookInput, dbPath, mappingConfigPath });
545
+ } catch (err) {
546
+ process.stderr.write(
547
+ `record-hook: ${err instanceof Error ? err.message : String(err)}
548
+ `
549
+ );
550
+ }
551
+ process.stdout.write(JSON.stringify(result) + "\n");
552
+ }
553
+
327
554
  // src/cli/query.ts
328
555
  function bigIntReplacer(_key, value) {
329
556
  return typeof value === "bigint" ? value.toString() : value;
@@ -412,11 +639,12 @@ async function handleQuery(options, _parentOpts) {
412
639
  // src/cli/handlers.ts
413
640
  var handlers = {
414
641
  record: handleRecord,
642
+ recordHook: handleRecordHook,
415
643
  query: handleQuery
416
644
  };
417
645
 
418
646
  // src/cli/index.ts
419
- var __dirname = dirname(fileURLToPath(import.meta.url));
647
+ var __dirname = dirname2(fileURLToPath(import.meta.url));
420
648
  var pkg = JSON.parse(
421
649
  readFileSync(resolve(__dirname, "../../package.json"), "utf8")
422
650
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/generated/program.ts","../../src/generated/contract.ts","../../src/cli/record.ts","../../src/cli/query.ts","../../src/cli/handlers.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * aaac-observ CLI entry point.\n *\n * Reads the version from package.json, creates the generated Commander\n * program with hand-written handlers, and executes it.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createProgram } from \"../generated/program.js\";\nimport { handlers } from \"./handlers.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(\n readFileSync(resolve(__dirname, \"../../package.json\"), \"utf8\"),\n) as { version: string };\n\nconst program = createProgram(handlers, pkg.version);\nawait program.parseAsync();\n","// Auto-generated by cli-contracts. Do not edit.\nimport { Command } from \"commander\";\nimport { CONTRACT_YAML, CONTRACT_JSON_STR } from \"./contract.js\";\n\nexport interface CommandHandlers {\n record: (options: { eventType?: string; lifecycle?: string; spanId?: string; parentSpanId?: string; sessionId?: string; traceId?: string; source?: string; attr?: string; link?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n query: (options: { kind?: string; traceId?: string; spanId?: string; eventType?: string; taskId?: string; from?: string; to?: string; direction?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n}\n\nexport function createProgram(\n handlers: CommandHandlers,\n version: string,\n): Command {\n const program = new Command();\n program\n .name(\"aaac-observ\")\n .version(version, \"-V, --version\")\n .description(\"aaac-observ — register external events and query the observability store\");\n\n\n program\n .command(\"record\")\n .description(\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\")\n .option(\"-t, --event-type <value>\", \"Event type (e.g. promotion.commit, process.edit, agent.session)\")\n .option(\"-l, --lifecycle <value>\", \"Event lifecycle phase: open | close | event | instant\")\n .option(\"-s, --span-id <value>\", \"Span ID (auto-generated if omitted)\")\n .option(\"--parent-span-id <value>\", \"Parent span ID for hierarchy\")\n .option(\"--session-id <value>\", \"Session ID to associate with the event\")\n .option(\"--trace-id <value>\", \"Trace ID for distributed tracing\")\n .option(\"--source <value>\", \"Event source identifier (default: external)\")\n .option(\"--attr <value>\", \"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\")\n .option(\"--link <value>\", \"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.record(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"query\")\n .description(\"Query the observability store via QueryAdapter\")\n .option(\"-k, --kind <value>\", \"Query kind: trace | span | search | links\")\n .option(\"--trace-id <value>\", \"Trace ID for trace queries (--kind trace)\")\n .option(\"-s, --span-id <value>\", \"Span ID for span/links queries (--kind span or --kind links)\")\n .option(\"--event-type <value>\", \"Event type filter for search queries (--kind search)\")\n .option(\"--task-id <value>\", \"Task ID filter for search queries (--kind search)\")\n .option(\"--from <value>\", \"Start time in ISO 8601 format for search queries (--kind search)\")\n .option(\"--to <value>\", \"End time in ISO 8601 format for search queries (--kind search)\")\n .option(\"-d, --direction <value>\", \"Link traversal direction for links queries (default: both)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.query(opts, cmd.optsWithGlobals());\n });\n\n\n // Built-in extract command (auto-injected by cli-contracts)\n program\n .command(\"extract\")\n .description(\"Extract contract specification for this CLI tool.\")\n .argument(\"[commands...]\", \"Command IDs to extract. Use dot notation.\")\n .option(\"-a, --all\", \"Extract all commands.\", false)\n .option(\"--include-meta\", \"Include extraction metadata.\", true)\n .option(\"-F, --format <format>\", \"Output format (yaml or json).\", \"yaml\")\n .action(async (commands: string[], opts: { all?: boolean; includeMeta?: boolean; format?: string }, cmd: Command) => {\n if (commands.length === 0 && !opts.all) {\n process.stderr.write(JSON.stringify({ code: \"INVALID_ARGS\", message: \"Specify command IDs or use --all\" }) + \"\\n\");\n process.exit(2);\n }\n\n const format = opts.format || \"yaml\";\n const doc = JSON.parse(CONTRACT_JSON_STR);\n const cmdIds = opts.all ? [] : commands;\n\n if (cmdIds.length === 0) {\n // --all: output full contract\n if (format === \"json\") {\n const out: Record<string, unknown> = {};\n if (opts.includeMeta) {\n out._meta = {\n source: \"embedded\",\n type: \"cli-contracts/extract\",\n extractedAt: new Date().toISOString(),\n specVersion: doc.cli_contracts ?? \"0.1.0\",\n commands: [\"aaac-observ.record\",\"aaac-observ.query\"],\n };\n }\n Object.assign(out, doc);\n process.stdout.write(JSON.stringify(out, null, 2) + \"\\n\");\n } else {\n // YAML output\n const yamlLines: string[] = [];\n yamlLines.push(\"# aaac-observ extract\");\n yamlLines.push(\"# source: embedded\");\n yamlLines.push(\"# type: cli-contracts/command-extract\");\n if (opts.includeMeta) {\n yamlLines.push(\"---\");\n yamlLines.push(\"source: embedded\");\n yamlLines.push(\"type: cli-contracts/command-extract\");\n yamlLines.push(\"extractedAt: \" + new Date().toISOString());\n yamlLines.push(\"spec_version: \" + (doc.cli_contracts ?? \"0.1.0\"));\n yamlLines.push(\"commands:\");\n for (const id of [\"aaac-observ.record\",\"aaac-observ.query\"]) {\n yamlLines.push(\" - \" + id);\n }\n }\n yamlLines.push(\"---\");\n yamlLines.push(CONTRACT_YAML);\n process.stdout.write(yamlLines.join(\"\\n\") + \"\\n\");\n }\n } else {\n // Filter specific commands\n const filtered: Record<string, unknown> = {\n cli_contracts: doc.cli_contracts,\n info: doc.info,\n command_sets: {},\n };\n const fcs = filtered.command_sets as Record<string, Record<string, unknown>>;\n for (const [setId, cs] of Object.entries(doc.command_sets ?? {})) {\n const cmds = (cs as Record<string, unknown>).commands as Record<string, unknown> | undefined;\n if (!cmds) continue;\n const matched: Record<string, unknown> = {};\n for (const [cmdId, cmdDef] of Object.entries(cmds)) {\n const fullId = setId + \".\" + cmdId;\n if (cmdIds.some((id) => id === cmdId || id === fullId || cmdId.startsWith(id + \".\"))) {\n matched[cmdId] = cmdDef;\n }\n }\n if (Object.keys(matched).length > 0) {\n const setCopy = { ...(cs as Record<string, unknown>) };\n setCopy.commands = matched;\n fcs[setId] = setCopy;\n }\n }\n if (doc.components) filtered.components = doc.components;\n process.stdout.write(JSON.stringify(filtered, null, 2) + \"\\n\");\n }\n process.exit(0);\n });\n return program;\n}\n","// Auto-generated by cli-contracts. Do not edit.\n// Embedded contract for the extract subcommand.\n\nexport const CONTRACT_YAML: string = \"# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json\\ncli_contracts: 0.1.0\\n\\ninfo:\\n title: AaaC Observability CLI\\n version: 0.1.0\\n description: aaac-observ — external event registration and query for @aaac/observability\\n\\ncommand_sets:\\n aaac-observ:\\n summary: aaac-observ — register external events and query the observability store\\n executable: aaac-observ\\n commands:\\n record:\\n summary: Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\n options:\\n - name: event-type\\n aliases: [t]\\n schema: { type: string }\\n description: \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n - name: lifecycle\\n aliases: [l]\\n schema:\\n type: string\\n enum: [open, close, event, instant]\\n description: \\\"Event lifecycle phase: open | close | event | instant\\\"\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID (auto-generated if omitted)\\n - name: parent-span-id\\n schema: { type: string }\\n description: Parent span ID for hierarchy\\n - name: session-id\\n schema: { type: string }\\n description: Session ID to associate with the event\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for distributed tracing\\n - name: source\\n schema: { type: string }\\n description: \\\"Event source identifier (default: external)\\\"\\n - name: attr\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n - name: link\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Event registered successfully\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n eventId:\\n type: string\\n spanId:\\n type: string\\n '1':\\n description: Registration failed (validation error or write error)\\n stderr:\\n format: text\\n\\n query:\\n summary: Query the observability store via QueryAdapter\\n options:\\n - name: kind\\n aliases: [k]\\n schema:\\n type: string\\n enum: [trace, span, search, links]\\n description: \\\"Query kind: trace | span | search | links\\\"\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for trace queries (--kind trace)\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID for span/links queries (--kind span or --kind links)\\n - name: event-type\\n schema: { type: string }\\n description: Event type filter for search queries (--kind search)\\n - name: task-id\\n schema: { type: string }\\n description: Task ID filter for search queries (--kind search)\\n - name: from\\n schema: { type: string }\\n description: Start time in ISO 8601 format for search queries (--kind search)\\n - name: to\\n schema: { type: string }\\n description: End time in ISO 8601 format for search queries (--kind search)\\n - name: direction\\n aliases: [d]\\n schema:\\n type: string\\n enum: [forward, reverse, both]\\n description: \\\"Link traversal direction for links queries (default: both)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Query succeeded — results as JSON array\\n stdout:\\n format: json\\n schema:\\n type: object\\n '1':\\n description: Query failed\\n stderr:\\n format: text\\n\";\n\nexport const CONTRACT_JSON_STR: string = \"{\\n \\\"cli_contracts\\\": \\\"0.1.0\\\",\\n \\\"info\\\": {\\n \\\"title\\\": \\\"AaaC Observability CLI\\\",\\n \\\"version\\\": \\\"0.1.0\\\",\\n \\\"description\\\": \\\"aaac-observ — external event registration and query for @aaac/observability\\\"\\n },\\n \\\"command_sets\\\": {\\n \\\"aaac-observ\\\": {\\n \\\"summary\\\": \\\"aaac-observ — register external events and query the observability store\\\",\\n \\\"executable\\\": \\\"aaac-observ\\\",\\n \\\"commands\\\": {\\n \\\"record\\\": {\\n \\\"summary\\\": \\\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"aliases\\\": [\\n \\\"t\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n },\\n {\\n \\\"name\\\": \\\"lifecycle\\\",\\n \\\"aliases\\\": [\\n \\\"l\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"open\\\",\\n \\\"close\\\",\\n \\\"event\\\",\\n \\\"instant\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Event lifecycle phase: open | close | event | instant\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID (auto-generated if omitted)\\\"\\n },\\n {\\n \\\"name\\\": \\\"parent-span-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Parent span ID for hierarchy\\\"\\n },\\n {\\n \\\"name\\\": \\\"session-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Session ID to associate with the event\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for distributed tracing\\\"\\n },\\n {\\n \\\"name\\\": \\\"source\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event source identifier (default: external)\\\"\\n },\\n {\\n \\\"name\\\": \\\"attr\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n },\\n {\\n \\\"name\\\": \\\"link\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Event registered successfully\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"eventId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"spanId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Registration failed (validation error or write error)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"query\\\": {\\n \\\"summary\\\": \\\"Query the observability store via QueryAdapter\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"kind\\\",\\n \\\"aliases\\\": [\\n \\\"k\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"trace\\\",\\n \\\"span\\\",\\n \\\"search\\\",\\n \\\"links\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Query kind: trace | span | search | links\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for trace queries (--kind trace)\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID for span/links queries (--kind span or --kind links)\\\"\\n },\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"task-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Task ID filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Start time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"End time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"direction\\\",\\n \\\"aliases\\\": [\\n \\\"d\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"forward\\\",\\n \\\"reverse\\\",\\n \\\"both\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Link traversal direction for links queries (default: both)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Query succeeded — results as JSON array\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\"\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Query failed\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n}\";\n","/**\n * record handler — thin entrypoint for EventCollector.registerExternalEvent.\n *\n * Wires the CLI options to the write pipeline:\n * options → EventCollector.registerExternalEvent\n * → Normalizer → Correlator → Enricher → SqliteSink + OtelEmitter\n *\n * architecture.md §9 / OBSERVABILITY_TASKS.md Phase 3-2\n * shift-left-event-mapping.md: aaac-observ record is the thin entrypoint\n */\nimport { createPipeline, generateId, DEFAULT_DB_PATH } from \"../index.js\";\nimport type { CanonicalLink, AttrValue, Lifecycle } from \"../types/canonical-event.js\";\n\n// ── Parsers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse comma-separated key=value attribute string into a Record.\n * Also accepts a JSON object string.\n *\n * Examples:\n * \"git.commit=abc123,git.branch=main\"\n * '{\"git.commit\":\"abc123\",\"git.branch\":\"main\"}'\n */\nexport function parseAttr(attrStr: string | undefined): Record<string, AttrValue> {\n if (!attrStr) return {};\n const trimmed = attrStr.trim();\n if (trimmed.startsWith(\"{\")) {\n try {\n return JSON.parse(trimmed) as Record<string, AttrValue>;\n } catch {\n // fall through to key=value parsing\n }\n }\n const result: Record<string, AttrValue> = {};\n for (const pair of trimmed.split(\",\")) {\n const eqIdx = pair.indexOf(\"=\");\n if (eqIdx > 0) {\n const key = pair.slice(0, eqIdx).trim();\n const val = pair.slice(eqIdx + 1).trim();\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * Parse comma-separated link string into a CanonicalLink array.\n * Format per item: targetSpanId:linkType[:targetTraceId]\n *\n * Example: \"spanId123:materializes_as_commit,spanId456:contains_change:traceId789\"\n */\nexport function parseLinks(linkStr: string | undefined): CanonicalLink[] {\n if (!linkStr) return [];\n const links: CanonicalLink[] = [];\n for (const item of linkStr.split(\",\")) {\n const parts = item.trim().split(\":\");\n if (parts.length >= 2) {\n const targetSpanId = parts[0].trim();\n const linkType = parts[1].trim();\n const targetTraceId = parts[2]?.trim() || undefined;\n if (targetSpanId && linkType) {\n links.push({ targetSpanId, linkType, targetTraceId });\n }\n }\n }\n return links;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n * NOTE: attr and link are strings (last value from --option) not arrays,\n * because the generated Commander program does not use a collector.\n * Comma-separate multiple values: --attr \"key1=val1,key2=val2\"\n */\nexport interface RecordHandlerOptions {\n eventType?: string;\n lifecycle?: string;\n spanId?: string;\n parentSpanId?: string;\n sessionId?: string;\n traceId?: string;\n source?: string;\n attr?: string;\n link?: string;\n db?: string;\n}\n\n/**\n * Handle `aaac-observ record`.\n *\n * Required options: --event-type, --lifecycle\n * On success: writes the event through the full pipeline and prints\n * { eventId, spanId } to stdout.\n * On error: writes to stderr and exits with code 1.\n */\nexport async function handleRecord(\n options: RecordHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n // ── Validate required options ──────────────────────────────────────────────\n if (!options.eventType) {\n process.stderr.write(\"Error: --event-type (-t) is required\\n\");\n process.exit(1);\n }\n const validLifecycles: readonly string[] = [\"open\", \"close\", \"event\", \"instant\"];\n if (!options.lifecycle) {\n process.stderr.write(\n `Error: --lifecycle (-l) is required (${validLifecycles.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validLifecycles.includes(options.lifecycle)) {\n process.stderr.write(\n `Error: --lifecycle must be one of: ${validLifecycles.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n // ── Build event payload ────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const spanId = options.spanId ?? generateId();\n const attributes = parseAttr(options.attr);\n const links = parseLinks(options.link);\n\n // Inject well-known scalars into attributes so the Normalizer picks them up\n if (options.sessionId) {\n attributes[\"session_id\"] = options.sessionId;\n }\n // trace_id stored as attribute for user reference; canonical traceId is\n // managed by Normalizer/Correlator (propagated via parentSpanId chain)\n if (options.traceId) {\n attributes[\"trace_id\"] = options.traceId;\n }\n\n // ── Build & execute pipeline ───────────────────────────────────────────────\n let capturedEventId: string | undefined;\n\n const { collector, sink } = createPipeline({\n dbPath,\n // Use afterCorrelate to capture the event id before it is written\n afterCorrelate: (event) => {\n capturedEventId = event.id;\n return event;\n },\n // Disable enrichment rules for external CLI events to keep the write\n // path lightweight (no Enricher cache, no artifact lookups, no derived events)\n enrichRules: [],\n });\n\n try {\n collector.registerExternalEvent({\n source: options.source ?? \"external\",\n eventType: options.eventType,\n lifecycle: options.lifecycle as Lifecycle,\n spanId,\n parentSpanId: options.parentSpanId,\n attributes,\n links,\n });\n } catch (err) {\n process.stderr.write(\n `Error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n sink.close();\n process.exit(1);\n }\n\n sink.close();\n\n const result = { eventId: capturedEventId, spanId };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * query handler — read path via QueryAdapter.\n *\n * Routes --kind to the appropriate SqliteQueryAdapter method and emits\n * results as JSON to stdout.\n *\n * Supported kinds:\n * trace → QueryAdapter.getTrace(--trace-id)\n * span → QueryAdapter.getSpan(--span-id) [+ QueryAdapter.getSpanEvents]\n * search → QueryAdapter.querySpans({ eventType, taskId, from, to })\n * links → QueryAdapter.getLinks(--span-id, --direction)\n *\n * architecture.md §12 / OBSERVABILITY_TASKS.md Phase 3-2 (record CLI + query)\n */\nimport { SqliteQueryAdapter, DEFAULT_DB_PATH } from \"../index.js\";\nimport { isoToUnixNano } from \"../types/ids.js\";\nimport type { LinkDirection } from \"../query/models.js\";\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n */\nexport interface QueryHandlerOptions {\n kind?: string;\n traceId?: string;\n spanId?: string;\n eventType?: string;\n taskId?: string;\n from?: string;\n to?: string;\n direction?: string;\n db?: string;\n}\n\n/** JSON replacer that serialises BigInt as a string. */\nfunction bigIntReplacer(_key: string, value: unknown): unknown {\n return typeof value === \"bigint\" ? value.toString() : value;\n}\n\n/**\n * Handle `aaac-observ query`.\n *\n * Required option: --kind (trace|span|search|links)\n * On success: writes a JSON array/object to stdout.\n * On error: writes to stderr and exits with code 1.\n *\n * DESIGN NOTE: all required-arg validation happens BEFORE the adapter is\n * created, so that when process.exit is mocked to throw in tests there is no\n * risk of a double-close on the database connection.\n */\nexport async function handleQuery(\n options: QueryHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const validKinds = [\"trace\", \"span\", \"search\", \"links\"] as const;\n type QueryKind = (typeof validKinds)[number];\n\n // ── Validate options (before opening the adapter) ─────────────────────────\n if (!options.kind) {\n process.stderr.write(\n `Error: --kind (-k) is required (${validKinds.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validKinds.includes(options.kind as QueryKind)) {\n process.stderr.write(\n `Error: --kind must be one of: ${validKinds.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n const kind = options.kind as QueryKind;\n\n // Validate sub-command required args (before adapter creation)\n if (kind === \"trace\" && !options.traceId) {\n process.stderr.write(\"Error: --trace-id is required for --kind trace\\n\");\n process.exit(1);\n }\n if (kind === \"span\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind span\\n\");\n process.exit(1);\n }\n if (kind === \"links\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind links\\n\");\n process.exit(1);\n }\n\n // ── Open adapter ──────────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const adapter = new SqliteQueryAdapter(dbPath);\n\n try {\n let result: unknown;\n\n switch (kind) {\n // ── trace: all spans in a trace ─────────────────────────────────────\n case \"trace\": {\n // options.traceId is guaranteed by pre-validation above\n result = adapter.getTrace(options.traceId!);\n break;\n }\n\n // ── span: single span + its events ──────────────────────────────────\n case \"span\": {\n // options.spanId is guaranteed by pre-validation above\n const span = adapter.getSpan(options.spanId!);\n const events = adapter.getSpanEvents(options.spanId!);\n result = span !== undefined ? { span, events } : null;\n break;\n }\n\n // ── search: spans matching event-type / task-id / time range ─────────\n case \"search\": {\n const filter: Parameters<typeof adapter.querySpans>[0] = {};\n if (options.eventType) filter.eventType = options.eventType;\n if (options.taskId) filter.taskId = options.taskId;\n if (options.from) filter.fromTimeUnixNano = isoToUnixNano(options.from);\n if (options.to) filter.toTimeUnixNano = isoToUnixNano(options.to);\n result = adapter.querySpans(filter);\n break;\n }\n\n // ── links: link traversal ─────────────────────────────────────────────\n case \"links\": {\n const validDirections = [\"forward\", \"reverse\", \"both\"] as const;\n type Dir = (typeof validDirections)[number];\n const dir: Dir =\n options.direction !== undefined &&\n validDirections.includes(options.direction as Dir)\n ? (options.direction as Dir)\n : \"both\";\n result = adapter.getLinks(options.spanId!, dir as LinkDirection);\n break;\n }\n }\n\n process.stdout.write(JSON.stringify(result, bigIntReplacer) + \"\\n\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n\n // Graceful handling: an un-initialised (fresh empty) database has no\n // tables yet. Return an empty / null result rather than an error so that\n // `aaac-observ query` on a machine with no recorded events behaves\n // predictably.\n if (msg.includes(\"no such table\") || msg.includes(\"no such column\")) {\n const emptyResult = kind === \"span\" ? null : [];\n process.stdout.write(JSON.stringify(emptyResult, bigIntReplacer) + \"\\n\");\n adapter.close();\n return;\n }\n\n process.stderr.write(`Error: ${msg}\\n`);\n adapter.close();\n process.exit(1);\n }\n\n adapter.close();\n}\n","/**\n * CLI command handlers for aaac-observ.\n *\n * Implements the CommandHandlers interface generated by cli-contracts and\n * delegates to the domain-specific handler modules.\n *\n * Usage (in index.ts):\n * import { handlers } from './handlers.js';\n * import { createProgram } from '../generated/program.js';\n * const program = createProgram(handlers, version);\n * await program.parseAsync();\n */\nimport type { CommandHandlers } from \"../generated/program.js\";\nimport { handleRecord } from \"./record.js\";\nimport { handleQuery } from \"./query.js\";\n\nexport const handlers: CommandHandlers = {\n record: handleRecord,\n query: handleQuery,\n};\n"],"mappings":"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","names":["handlers","program"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/generated/program.ts","../../src/generated/contract.ts","../../src/cli/record.ts","../../src/cli/record-hook.ts","../../src/cli/query.ts","../../src/cli/handlers.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * aaac-observ CLI entry point.\n *\n * Reads the version from package.json, creates the generated Commander\n * program with hand-written handlers, and executes it.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createProgram } from \"../generated/program.js\";\nimport { handlers } from \"./handlers.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(\n readFileSync(resolve(__dirname, \"../../package.json\"), \"utf8\"),\n) as { version: string };\n\nconst program = createProgram(handlers, pkg.version);\nawait program.parseAsync();\n","// Auto-generated by cli-contracts. Do not edit.\nimport { Command } from \"commander\";\nimport { CONTRACT_YAML, CONTRACT_JSON_STR } from \"./contract.js\";\n\nexport interface CommandHandlers {\n record: (options: { eventType?: string; lifecycle?: string; spanId?: string; parentSpanId?: string; sessionId?: string; traceId?: string; source?: string; attr?: string; link?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n recordHook: (hookName: string | undefined, options: { mappingConfig?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n query: (options: { kind?: string; traceId?: string; spanId?: string; eventType?: string; taskId?: string; from?: string; to?: string; direction?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n}\n\nexport function createProgram(\n handlers: CommandHandlers,\n version: string,\n): Command {\n const program = new Command();\n program\n .name(\"aaac-observ\")\n .version(version, \"-V, --version\")\n .description(\"aaac-observ — register external events and query the observability store\");\n\n\n program\n .command(\"record\")\n .description(\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\")\n .option(\"-t, --event-type <value>\", \"Event type (e.g. promotion.commit, process.edit, agent.session)\")\n .option(\"-l, --lifecycle <value>\", \"Event lifecycle phase: open | close | event | instant\")\n .option(\"-s, --span-id <value>\", \"Span ID (auto-generated if omitted)\")\n .option(\"--parent-span-id <value>\", \"Parent span ID for hierarchy\")\n .option(\"--session-id <value>\", \"Session ID to associate with the event\")\n .option(\"--trace-id <value>\", \"Trace ID for distributed tracing\")\n .option(\"--source <value>\", \"Event source identifier (default: external)\")\n .option(\"--attr <value>\", \"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\")\n .option(\"--link <value>\", \"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.record(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"record-hook\")\n .description(\"Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\")\n .argument(\"<hook-name>\", \"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\")\n .option(\"--mapping-config <value>\", \"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (hookName, opts, cmd) => {\n await handlers.recordHook(hookName, opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"query\")\n .description(\"Query the observability store via QueryAdapter\")\n .option(\"-k, --kind <value>\", \"Query kind: trace | span | search | links\")\n .option(\"--trace-id <value>\", \"Trace ID for trace queries (--kind trace)\")\n .option(\"-s, --span-id <value>\", \"Span ID for span/links queries (--kind span or --kind links)\")\n .option(\"--event-type <value>\", \"Event type filter for search queries (--kind search)\")\n .option(\"--task-id <value>\", \"Task ID filter for search queries (--kind search)\")\n .option(\"--from <value>\", \"Start time in ISO 8601 format for search queries (--kind search)\")\n .option(\"--to <value>\", \"End time in ISO 8601 format for search queries (--kind search)\")\n .option(\"-d, --direction <value>\", \"Link traversal direction for links queries (default: both)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.query(opts, cmd.optsWithGlobals());\n });\n\n\n // Built-in extract command (auto-injected by cli-contracts)\n program\n .command(\"extract\")\n .description(\"Extract contract specification for this CLI tool.\")\n .argument(\"[commands...]\", \"Command IDs to extract. Use dot notation.\")\n .option(\"-a, --all\", \"Extract all commands.\", false)\n .option(\"--include-meta\", \"Include extraction metadata.\", true)\n .option(\"-F, --format <format>\", \"Output format (yaml or json).\", \"yaml\")\n .action(async (commands: string[], opts: { all?: boolean; includeMeta?: boolean; format?: string }, cmd: Command) => {\n if (commands.length === 0 && !opts.all) {\n process.stderr.write(JSON.stringify({ code: \"INVALID_ARGS\", message: \"Specify command IDs or use --all\" }) + \"\\n\");\n process.exit(2);\n }\n\n const format = opts.format || \"yaml\";\n const doc = JSON.parse(CONTRACT_JSON_STR);\n const cmdIds = opts.all ? [] : commands;\n\n if (cmdIds.length === 0) {\n // --all: output full contract\n if (format === \"json\") {\n const out: Record<string, unknown> = {};\n if (opts.includeMeta) {\n out._meta = {\n source: \"embedded\",\n type: \"cli-contracts/extract\",\n extractedAt: new Date().toISOString(),\n specVersion: doc.cli_contracts ?? \"0.1.0\",\n commands: [\"aaac-observ.record\",\"aaac-observ.record-hook\",\"aaac-observ.query\"],\n };\n }\n Object.assign(out, doc);\n process.stdout.write(JSON.stringify(out, null, 2) + \"\\n\");\n } else {\n // YAML output\n const yamlLines: string[] = [];\n yamlLines.push(\"# aaac-observ extract\");\n yamlLines.push(\"# source: embedded\");\n yamlLines.push(\"# type: cli-contracts/command-extract\");\n if (opts.includeMeta) {\n yamlLines.push(\"---\");\n yamlLines.push(\"source: embedded\");\n yamlLines.push(\"type: cli-contracts/command-extract\");\n yamlLines.push(\"extractedAt: \" + new Date().toISOString());\n yamlLines.push(\"spec_version: \" + (doc.cli_contracts ?? \"0.1.0\"));\n yamlLines.push(\"commands:\");\n for (const id of [\"aaac-observ.record\",\"aaac-observ.record-hook\",\"aaac-observ.query\"]) {\n yamlLines.push(\" - \" + id);\n }\n }\n yamlLines.push(\"---\");\n yamlLines.push(CONTRACT_YAML);\n process.stdout.write(yamlLines.join(\"\\n\") + \"\\n\");\n }\n } else {\n // Filter specific commands\n const filtered: Record<string, unknown> = {\n cli_contracts: doc.cli_contracts,\n info: doc.info,\n command_sets: {},\n };\n const fcs = filtered.command_sets as Record<string, Record<string, unknown>>;\n for (const [setId, cs] of Object.entries(doc.command_sets ?? {})) {\n const cmds = (cs as Record<string, unknown>).commands as Record<string, unknown> | undefined;\n if (!cmds) continue;\n const matched: Record<string, unknown> = {};\n for (const [cmdId, cmdDef] of Object.entries(cmds)) {\n const fullId = setId + \".\" + cmdId;\n if (cmdIds.some((id) => id === cmdId || id === fullId || cmdId.startsWith(id + \".\"))) {\n matched[cmdId] = cmdDef;\n }\n }\n if (Object.keys(matched).length > 0) {\n const setCopy = { ...(cs as Record<string, unknown>) };\n setCopy.commands = matched;\n fcs[setId] = setCopy;\n }\n }\n if (doc.components) filtered.components = doc.components;\n process.stdout.write(JSON.stringify(filtered, null, 2) + \"\\n\");\n }\n process.exit(0);\n });\n return program;\n}\n","// Auto-generated by cli-contracts. Do not edit.\n// Embedded contract for the extract subcommand.\n\nexport const CONTRACT_YAML: string = \"# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json\\ncli_contracts: 0.1.0\\n\\ninfo:\\n title: AaaC Observability CLI\\n version: 0.1.0\\n description: aaac-observ — external event registration and query for @aaac/observability\\n\\ncommand_sets:\\n aaac-observ:\\n summary: aaac-observ — register external events and query the observability store\\n executable: aaac-observ\\n commands:\\n record:\\n summary: Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\n options:\\n - name: event-type\\n aliases: [t]\\n schema: { type: string }\\n description: \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n - name: lifecycle\\n aliases: [l]\\n schema:\\n type: string\\n enum: [open, close, event, instant]\\n description: \\\"Event lifecycle phase: open | close | event | instant\\\"\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID (auto-generated if omitted)\\n - name: parent-span-id\\n schema: { type: string }\\n description: Parent span ID for hierarchy\\n - name: session-id\\n schema: { type: string }\\n description: Session ID to associate with the event\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for distributed tracing\\n - name: source\\n schema: { type: string }\\n description: \\\"Event source identifier (default: external)\\\"\\n - name: attr\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n - name: link\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Event registered successfully\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n eventId:\\n type: string\\n spanId:\\n type: string\\n '1':\\n description: Registration failed (validation error or write error)\\n stderr:\\n format: text\\n\\n record-hook:\\n summary: Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\\n arguments:\\n - name: hook-name\\n index: 0\\n required: true\\n schema: { type: string }\\n description: \\\"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\\\"\\n options:\\n - name: mapping-config\\n schema: { type: string }\\n description: \\\"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Hook recorded (or no-op when stdin/mapping unavailable) — always fail-open\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n recorded:\\n type: integer\\n fallback:\\n type: boolean\\n\\n query:\\n summary: Query the observability store via QueryAdapter\\n options:\\n - name: kind\\n aliases: [k]\\n schema:\\n type: string\\n enum: [trace, span, search, links]\\n description: \\\"Query kind: trace | span | search | links\\\"\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for trace queries (--kind trace)\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID for span/links queries (--kind span or --kind links)\\n - name: event-type\\n schema: { type: string }\\n description: Event type filter for search queries (--kind search)\\n - name: task-id\\n schema: { type: string }\\n description: Task ID filter for search queries (--kind search)\\n - name: from\\n schema: { type: string }\\n description: Start time in ISO 8601 format for search queries (--kind search)\\n - name: to\\n schema: { type: string }\\n description: End time in ISO 8601 format for search queries (--kind search)\\n - name: direction\\n aliases: [d]\\n schema:\\n type: string\\n enum: [forward, reverse, both]\\n description: \\\"Link traversal direction for links queries (default: both)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Query succeeded — results as JSON array\\n stdout:\\n format: json\\n schema:\\n type: object\\n '1':\\n description: Query failed\\n stderr:\\n format: text\\n\";\n\nexport const CONTRACT_JSON_STR: string = \"{\\n \\\"cli_contracts\\\": \\\"0.1.0\\\",\\n \\\"info\\\": {\\n \\\"title\\\": \\\"AaaC Observability CLI\\\",\\n \\\"version\\\": \\\"0.1.0\\\",\\n \\\"description\\\": \\\"aaac-observ — external event registration and query for @aaac/observability\\\"\\n },\\n \\\"command_sets\\\": {\\n \\\"aaac-observ\\\": {\\n \\\"summary\\\": \\\"aaac-observ — register external events and query the observability store\\\",\\n \\\"executable\\\": \\\"aaac-observ\\\",\\n \\\"commands\\\": {\\n \\\"record\\\": {\\n \\\"summary\\\": \\\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"aliases\\\": [\\n \\\"t\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n },\\n {\\n \\\"name\\\": \\\"lifecycle\\\",\\n \\\"aliases\\\": [\\n \\\"l\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"open\\\",\\n \\\"close\\\",\\n \\\"event\\\",\\n \\\"instant\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Event lifecycle phase: open | close | event | instant\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID (auto-generated if omitted)\\\"\\n },\\n {\\n \\\"name\\\": \\\"parent-span-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Parent span ID for hierarchy\\\"\\n },\\n {\\n \\\"name\\\": \\\"session-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Session ID to associate with the event\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for distributed tracing\\\"\\n },\\n {\\n \\\"name\\\": \\\"source\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event source identifier (default: external)\\\"\\n },\\n {\\n \\\"name\\\": \\\"attr\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n },\\n {\\n \\\"name\\\": \\\"link\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Event registered successfully\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"eventId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"spanId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Registration failed (validation error or write error)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"record-hook\\\": {\\n \\\"summary\\\": \\\"Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\\\",\\n \\\"arguments\\\": [\\n {\\n \\\"name\\\": \\\"hook-name\\\",\\n \\\"index\\\": 0,\\n \\\"required\\\": true,\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\\\"\\n }\\n ],\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"mapping-config\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Hook recorded (or no-op when stdin/mapping unavailable) — always fail-open\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"recorded\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"fallback\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n },\\n \\\"query\\\": {\\n \\\"summary\\\": \\\"Query the observability store via QueryAdapter\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"kind\\\",\\n \\\"aliases\\\": [\\n \\\"k\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"trace\\\",\\n \\\"span\\\",\\n \\\"search\\\",\\n \\\"links\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Query kind: trace | span | search | links\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for trace queries (--kind trace)\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID for span/links queries (--kind span or --kind links)\\\"\\n },\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"task-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Task ID filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Start time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"End time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"direction\\\",\\n \\\"aliases\\\": [\\n \\\"d\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"forward\\\",\\n \\\"reverse\\\",\\n \\\"both\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Link traversal direction for links queries (default: both)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Query succeeded — results as JSON array\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\"\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Query failed\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n}\";\n","/**\n * record handler — thin entrypoint for EventCollector.registerExternalEvent.\n *\n * Wires the CLI options to the write pipeline:\n * options → EventCollector.registerExternalEvent\n * → Normalizer → Correlator → Enricher → SqliteSink + OtelEmitter\n *\n * architecture.md §9 / OBSERVABILITY_TASKS.md Phase 3-2\n * shift-left-event-mapping.md: aaac-observ record is the thin entrypoint\n */\nimport { createPipeline, generateId, DEFAULT_DB_PATH } from \"../index.js\";\nimport type { CanonicalLink, AttrValue, Lifecycle } from \"../types/canonical-event.js\";\n\n// ── Parsers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse comma-separated key=value attribute string into a Record.\n * Also accepts a JSON object string.\n *\n * Examples:\n * \"git.commit=abc123,git.branch=main\"\n * '{\"git.commit\":\"abc123\",\"git.branch\":\"main\"}'\n */\nexport function parseAttr(attrStr: string | undefined): Record<string, AttrValue> {\n if (!attrStr) return {};\n const trimmed = attrStr.trim();\n if (trimmed.startsWith(\"{\")) {\n try {\n return JSON.parse(trimmed) as Record<string, AttrValue>;\n } catch {\n // fall through to key=value parsing\n }\n }\n const result: Record<string, AttrValue> = {};\n for (const pair of trimmed.split(\",\")) {\n const eqIdx = pair.indexOf(\"=\");\n if (eqIdx > 0) {\n const key = pair.slice(0, eqIdx).trim();\n const val = pair.slice(eqIdx + 1).trim();\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * Parse comma-separated link string into a CanonicalLink array.\n * Format per item: targetSpanId:linkType[:targetTraceId]\n *\n * Example: \"spanId123:materializes_as_commit,spanId456:contains_change:traceId789\"\n */\nexport function parseLinks(linkStr: string | undefined): CanonicalLink[] {\n if (!linkStr) return [];\n const links: CanonicalLink[] = [];\n for (const item of linkStr.split(\",\")) {\n const parts = item.trim().split(\":\");\n if (parts.length >= 2) {\n const targetSpanId = parts[0].trim();\n const linkType = parts[1].trim();\n const targetTraceId = parts[2]?.trim() || undefined;\n if (targetSpanId && linkType) {\n links.push({ targetSpanId, linkType, targetTraceId });\n }\n }\n }\n return links;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n * NOTE: attr and link are strings (last value from --option) not arrays,\n * because the generated Commander program does not use a collector.\n * Comma-separate multiple values: --attr \"key1=val1,key2=val2\"\n */\nexport interface RecordHandlerOptions {\n eventType?: string;\n lifecycle?: string;\n spanId?: string;\n parentSpanId?: string;\n sessionId?: string;\n traceId?: string;\n source?: string;\n attr?: string;\n link?: string;\n db?: string;\n}\n\n/**\n * Handle `aaac-observ record`.\n *\n * Required options: --event-type, --lifecycle\n * On success: writes the event through the full pipeline and prints\n * { eventId, spanId } to stdout.\n * On error: writes to stderr and exits with code 1.\n */\nexport async function handleRecord(\n options: RecordHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n // ── Validate required options ──────────────────────────────────────────────\n if (!options.eventType) {\n process.stderr.write(\"Error: --event-type (-t) is required\\n\");\n process.exit(1);\n }\n const validLifecycles: readonly string[] = [\"open\", \"close\", \"event\", \"instant\"];\n if (!options.lifecycle) {\n process.stderr.write(\n `Error: --lifecycle (-l) is required (${validLifecycles.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validLifecycles.includes(options.lifecycle)) {\n process.stderr.write(\n `Error: --lifecycle must be one of: ${validLifecycles.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n // ── Build event payload ────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const spanId = options.spanId ?? generateId();\n const attributes = parseAttr(options.attr);\n const links = parseLinks(options.link);\n\n // Inject well-known scalars into attributes so the Normalizer picks them up\n if (options.sessionId) {\n attributes[\"session_id\"] = options.sessionId;\n }\n // trace_id stored as attribute for user reference; canonical traceId is\n // managed by Normalizer/Correlator (propagated via parentSpanId chain)\n if (options.traceId) {\n attributes[\"trace_id\"] = options.traceId;\n }\n\n // ── Build & execute pipeline ───────────────────────────────────────────────\n let capturedEventId: string | undefined;\n\n const { collector, sink } = createPipeline({\n dbPath,\n // Use afterCorrelate to capture the event id before it is written\n afterCorrelate: (event) => {\n capturedEventId = event.id;\n return event;\n },\n // Disable enrichment rules for external CLI events to keep the write\n // path lightweight (no Enricher cache, no artifact lookups, no derived events)\n enrichRules: [],\n });\n\n try {\n collector.registerExternalEvent({\n source: options.source ?? \"external\",\n eventType: options.eventType,\n lifecycle: options.lifecycle as Lifecycle,\n spanId,\n parentSpanId: options.parentSpanId,\n attributes,\n links,\n });\n } catch (err) {\n process.stderr.write(\n `Error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n sink.close();\n process.exit(1);\n }\n\n sink.close();\n\n const result = { eventId: capturedEventId, spanId };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * record-hook handler — stdin-aware Cursor/git hook recorder.\n *\n * This is the runtime entrypoint for `.cursor/hooks/observ-record.sh`. Unlike the\n * thin `record` command (scalar flags only), `record-hook`:\n *\n * 1. parses the hook JSON payload from stdin (conversation_id + hook-specific fields)\n * 2. loads `.agent-logs/config/event-mapping.json` and resolves the matching\n * mapping rule into 3-axis spans + cross-axis links (evaluateMapping)\n * 3. injects session_id = conversation_id on every emitted event (correlation key\n * with @aaac/runtime in-process events)\n * 4. emits human-interaction events (human.instruction / quality_gate.result /\n * promotion.pr) for the relevant hooks\n * 5. manages the short-lived git context file `.agent-logs/.observ-context.json`\n * (write on beforeShellExecution for git commit/push/merge, delete on\n * afterShellExecution) so git hooks can correlate (read side: #115)\n *\n * It is strictly fail-open: any parse/IO/config error degrades to the generic\n * fallback recording (or a no-op) and never throws — recording must NEVER block\n * the underlying hook operation.\n *\n * Issue #114 / shift-left-event-mapping.md §6 (option B).\n */\nimport { mkdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n createPipeline,\n generateId,\n DEFAULT_DB_PATH,\n loadEventMappingConfig,\n evaluateMapping,\n emitHumanInstruction,\n emitQualityGateResult,\n emitPromotionPr,\n} from \"../index.js\";\nimport type { EventCollector } from \"../collector/event-collector.js\";\nimport type { AttrValue, CanonicalLink } from \"../types/canonical-event.js\";\nimport type { EventMappingConfig, EventMappingRule } from \"../event-mapping/types.js\";\n\n// ── Constants ──────────────────────────────────────────────────────────────────\n\n/** git subcommands that warrant propagating session context to git hooks. */\nconst GIT_CONTEXT_COMMAND_RE = /\\bgit\\s+(?:commit|push|merge)\\b/;\n\n/** Hook names that map to git.* event types in the generic fallback. */\nconst GIT_HOOK_NAMES = new Set([\n \"pre-commit\",\n \"post-commit\",\n \"pre-push\",\n \"post-merge\",\n \"post-checkout\",\n]);\n\n// ── Options ──────────────────────────────────────────────────────────────────\n\nexport interface RecordHookOptions {\n mappingConfig?: string;\n db?: string;\n}\n\n/** Parsed result of running the hook recorder (also written to stdout as JSON). */\nexport interface RecordHookResult {\n /** Number of events emitted into the pipeline. */\n recorded: number;\n /** True when no mapping rule matched and the generic fallback was used. */\n fallback: boolean;\n}\n\n// ── Path helpers ───────────────────────────────────────────────────────────────\n\n/** Default event-mapping config path, derived from the db directory. */\nexport function defaultMappingConfigPath(dbPath: string): string {\n return join(dirname(dbPath), \"config\", \"event-mapping.json\");\n}\n\n/** Short-lived git context file path, derived from the db directory. */\nexport function contextFilePath(dbPath: string): string {\n return join(dirname(dbPath), \".observ-context.json\");\n}\n\n// ── stdin parsing ────────────────────────────────────────────────────────────\n\n/**\n * Read the entire stdin stream as a string.\n * Resolves \"\" when stdin is a TTY (no payload) or on any error — the git-hook\n * path frequently provides no stdin, which must be handled gracefully (AC-1).\n */\nexport async function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n return new Promise<string>((resolve) => {\n let data = \"\";\n let settled = false;\n const finish = (): void => {\n if (settled) return;\n settled = true;\n resolve(data);\n };\n try {\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", finish);\n process.stdin.on(\"error\", finish);\n process.stdin.on(\"close\", finish);\n // Safety net: never hang the hook if stdin is left open.\n const t = setTimeout(finish, 1000);\n if (typeof t.unref === \"function\") t.unref();\n } catch {\n finish();\n }\n });\n}\n\n/** Parse a stdin payload into an object; returns {} for empty/invalid JSON. */\nexport function parseHookInput(raw: string): Record<string, unknown> {\n const trimmed = raw.trim();\n if (!trimmed) return {};\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n return parsed !== null && typeof parsed === \"object\"\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n// ── Field coercion ───────────────────────────────────────────────────────────\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n;\n }\n return undefined;\n}\n\n// ── Emission ─────────────────────────────────────────────────────────────────\n\n/**\n * Emit the 3-axis spans + cross-axis links resolved from a mapping rule.\n * session_id (and parent_conversation_id on subagentStart) are injected on every\n * span so all hook-derived events share the correlation key.\n */\nfunction emitMappedSpans(\n collector: EventCollector,\n hook: string,\n rule: EventMappingRule,\n hookInput: Record<string, unknown>,\n sessionId: string,\n): number {\n const { spans, links } = evaluateMapping(rule, hookInput);\n\n // Group resolved links by their source spanId so they can be attached to the\n // emitting (source) event as CanonicalLink entries.\n const linksBySpan = new Map<string, CanonicalLink[]>();\n for (const link of links) {\n const arr = linksBySpan.get(link.fromSpanId) ?? [];\n arr.push({ targetSpanId: link.toSpanId, linkType: link.linkType });\n linksBySpan.set(link.fromSpanId, arr);\n }\n\n const parentConversationId =\n hook === \"subagentStart\" ? asString(hookInput.parent_conversation_id) : undefined;\n\n for (const span of spans) {\n const attributes: Record<string, AttrValue> = { ...span.attributes };\n if (sessionId) attributes[\"session_id\"] = sessionId;\n if (parentConversationId) {\n attributes[\"parent_conversation_id\"] = parentConversationId;\n }\n collector.emit({\n source: \"cursor-hook\",\n eventType: span.eventType,\n lifecycle: span.lifecycle,\n spanId: span.spanId,\n parentSpanId: span.parentSpanId,\n attributes,\n links: linksBySpan.get(span.spanId) ?? [],\n });\n }\n\n return spans.length;\n}\n\n/**\n * Emit a single generic instant event (current pre-#114 behaviour) when no\n * mapping rule matches — preserves fail-open recording (AC-3).\n */\nfunction emitGenericFallback(\n collector: EventCollector,\n hook: string,\n sessionId: string,\n): number {\n const eventType = (GIT_HOOK_NAMES.has(hook) ? \"git.\" : \"cursor.\") + hook;\n const attributes: Record<string, AttrValue> = { \"hook.name\": hook };\n if (sessionId) attributes[\"session_id\"] = sessionId;\n collector.emit({\n source: \"cursor-hook\",\n eventType,\n lifecycle: \"instant\",\n attributes,\n });\n return 1;\n}\n\n/** Emit human-interaction events for the relevant hooks (AC-5). */\nfunction emitHumanEvents(\n collector: EventCollector,\n hook: string,\n hookInput: Record<string, unknown>,\n sessionId: string,\n): number {\n let count = 0;\n\n if (hook === \"beforeSubmitPrompt\") {\n const prompt = asString(hookInput.prompt);\n if (prompt !== undefined) {\n const attachments = Array.isArray(hookInput.attachments)\n ? (hookInput.attachments as unknown[])\n : [];\n emitHumanInstruction(collector, { sessionId, prompt, attachments });\n count += 1;\n }\n } else if (hook === \"afterShellExecution\") {\n const command = asString(hookInput.command);\n if (command !== undefined) {\n const exitCode = asNumber(hookInput.exit_code) ?? asNumber(hookInput.exitCode) ?? 0;\n const durationMs = asNumber(hookInput.duration);\n if (emitQualityGateResult(collector, { sessionId, command, exitCode, durationMs })) {\n count += 1;\n }\n const output = asString(hookInput.output) ?? \"\";\n if (emitPromotionPr(collector, { sessionId, command, output })) {\n count += 1;\n }\n }\n }\n\n return count;\n}\n\n// ── Context file lifecycle ──────────────────────────────────────────────────\n\n/**\n * Manage the short-lived git context file (AC-4).\n *\n * beforeShellExecution + git commit/push/merge → atomic write\n * afterShellExecution + git commit/push/merge → delete (fail-open)\n *\n * Atomic write uses write-to-temp + rename so a git hook never observes a\n * partially written file. Non-git commands never write the file.\n */\nasync function manageContextFile(\n hook: string,\n hookInput: Record<string, unknown>,\n sessionId: string,\n dbPath: string,\n): Promise<void> {\n const command = asString(hookInput.command) ?? \"\";\n const isGitContextCommand = GIT_CONTEXT_COMMAND_RE.test(command);\n const file = contextFilePath(dbPath);\n\n if (hook === \"beforeShellExecution\" && isGitContextCommand) {\n await mkdir(dirname(file), { recursive: true });\n const payload =\n JSON.stringify({\n session_id: sessionId || null,\n nonce: generateId(),\n created_at_ms: Date.now(),\n }) + \"\\n\";\n const tmp = `${file}.${process.pid}.${generateId()}.tmp`;\n await writeFile(tmp, payload, \"utf8\");\n await rename(tmp, file);\n } else if (hook === \"afterShellExecution\" && isGitContextCommand) {\n await unlink(file).catch(() => {\n // rm -f semantics: missing file is not an error.\n });\n }\n}\n\n// ── Core (testable) ────────────────────────────────────────────────────────────\n\n/**\n * Run the hook recorder against an already-parsed hook input.\n *\n * Separated from {@link handleRecordHook} (which reads stdin) so it can be unit\n * tested without mocking process.stdin.\n */\nexport async function runRecordHook(args: {\n hook: string;\n hookInput: Record<string, unknown>;\n dbPath: string;\n mappingConfigPath: string;\n}): Promise<RecordHookResult> {\n const { hook, hookInput, dbPath, mappingConfigPath } = args;\n const sessionId = asString(hookInput.conversation_id) ?? \"\";\n\n // Context-file lifecycle is independent of recording and must not block it.\n await manageContextFile(hook, hookInput, sessionId, dbPath).catch(() => {\n // fail-open\n });\n\n // Load mapping config; absence/parse-error → generic fallback (AC-3).\n let config: EventMappingConfig | undefined;\n try {\n config = await loadEventMappingConfig(mappingConfigPath);\n } catch {\n config = undefined;\n }\n const rule = config?.mappings?.[hook];\n\n let recorded = 0;\n let fallback = false;\n\n const { collector, sink } = createPipeline({ dbPath, enrichRules: [] });\n try {\n if (rule) {\n recorded += emitMappedSpans(collector, hook, rule, hookInput, sessionId);\n } else {\n recorded += emitGenericFallback(collector, hook, sessionId);\n fallback = true;\n }\n recorded += emitHumanEvents(collector, hook, hookInput, sessionId);\n } finally {\n sink.close();\n }\n\n return { recorded, fallback };\n}\n\n// ── Handler ─────────────────────────────────────────────────────────────────\n\n/**\n * Handle `aaac-observ record-hook <hook-name>`.\n *\n * Reads the hook JSON from stdin, records the resolved events, and prints\n * `{ recorded, fallback }` to stdout. Strictly fail-open: never throws and\n * always returns; the calling shell wrapper additionally guarantees exit 0.\n */\nexport async function handleRecordHook(\n hookName: string | undefined,\n options: RecordHookOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const hook = hookName ?? \"unknown\";\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const mappingConfigPath = options.mappingConfig ?? defaultMappingConfigPath(dbPath);\n\n let result: RecordHookResult = { recorded: 0, fallback: false };\n try {\n const hookInput = parseHookInput(await readStdin());\n result = await runRecordHook({ hook, hookInput, dbPath, mappingConfigPath });\n } catch (err) {\n // Recorder is fail-open: report to stderr but never block the operation.\n process.stderr.write(\n `record-hook: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * query handler — read path via QueryAdapter.\n *\n * Routes --kind to the appropriate SqliteQueryAdapter method and emits\n * results as JSON to stdout.\n *\n * Supported kinds:\n * trace → QueryAdapter.getTrace(--trace-id)\n * span → QueryAdapter.getSpan(--span-id) [+ QueryAdapter.getSpanEvents]\n * search → QueryAdapter.querySpans({ eventType, taskId, from, to })\n * links → QueryAdapter.getLinks(--span-id, --direction)\n *\n * architecture.md §12 / OBSERVABILITY_TASKS.md Phase 3-2 (record CLI + query)\n */\nimport { SqliteQueryAdapter, DEFAULT_DB_PATH } from \"../index.js\";\nimport { isoToUnixNano } from \"../types/ids.js\";\nimport type { LinkDirection } from \"../query/models.js\";\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n */\nexport interface QueryHandlerOptions {\n kind?: string;\n traceId?: string;\n spanId?: string;\n eventType?: string;\n taskId?: string;\n from?: string;\n to?: string;\n direction?: string;\n db?: string;\n}\n\n/** JSON replacer that serialises BigInt as a string. */\nfunction bigIntReplacer(_key: string, value: unknown): unknown {\n return typeof value === \"bigint\" ? value.toString() : value;\n}\n\n/**\n * Handle `aaac-observ query`.\n *\n * Required option: --kind (trace|span|search|links)\n * On success: writes a JSON array/object to stdout.\n * On error: writes to stderr and exits with code 1.\n *\n * DESIGN NOTE: all required-arg validation happens BEFORE the adapter is\n * created, so that when process.exit is mocked to throw in tests there is no\n * risk of a double-close on the database connection.\n */\nexport async function handleQuery(\n options: QueryHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const validKinds = [\"trace\", \"span\", \"search\", \"links\"] as const;\n type QueryKind = (typeof validKinds)[number];\n\n // ── Validate options (before opening the adapter) ─────────────────────────\n if (!options.kind) {\n process.stderr.write(\n `Error: --kind (-k) is required (${validKinds.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validKinds.includes(options.kind as QueryKind)) {\n process.stderr.write(\n `Error: --kind must be one of: ${validKinds.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n const kind = options.kind as QueryKind;\n\n // Validate sub-command required args (before adapter creation)\n if (kind === \"trace\" && !options.traceId) {\n process.stderr.write(\"Error: --trace-id is required for --kind trace\\n\");\n process.exit(1);\n }\n if (kind === \"span\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind span\\n\");\n process.exit(1);\n }\n if (kind === \"links\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind links\\n\");\n process.exit(1);\n }\n\n // ── Open adapter ──────────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const adapter = new SqliteQueryAdapter(dbPath);\n\n try {\n let result: unknown;\n\n switch (kind) {\n // ── trace: all spans in a trace ─────────────────────────────────────\n case \"trace\": {\n // options.traceId is guaranteed by pre-validation above\n result = adapter.getTrace(options.traceId!);\n break;\n }\n\n // ── span: single span + its events ──────────────────────────────────\n case \"span\": {\n // options.spanId is guaranteed by pre-validation above\n const span = adapter.getSpan(options.spanId!);\n const events = adapter.getSpanEvents(options.spanId!);\n result = span !== undefined ? { span, events } : null;\n break;\n }\n\n // ── search: spans matching event-type / task-id / time range ─────────\n case \"search\": {\n const filter: Parameters<typeof adapter.querySpans>[0] = {};\n if (options.eventType) filter.eventType = options.eventType;\n if (options.taskId) filter.taskId = options.taskId;\n if (options.from) filter.fromTimeUnixNano = isoToUnixNano(options.from);\n if (options.to) filter.toTimeUnixNano = isoToUnixNano(options.to);\n result = adapter.querySpans(filter);\n break;\n }\n\n // ── links: link traversal ─────────────────────────────────────────────\n case \"links\": {\n const validDirections = [\"forward\", \"reverse\", \"both\"] as const;\n type Dir = (typeof validDirections)[number];\n const dir: Dir =\n options.direction !== undefined &&\n validDirections.includes(options.direction as Dir)\n ? (options.direction as Dir)\n : \"both\";\n result = adapter.getLinks(options.spanId!, dir as LinkDirection);\n break;\n }\n }\n\n process.stdout.write(JSON.stringify(result, bigIntReplacer) + \"\\n\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n\n // Graceful handling: an un-initialised (fresh empty) database has no\n // tables yet. Return an empty / null result rather than an error so that\n // `aaac-observ query` on a machine with no recorded events behaves\n // predictably.\n if (msg.includes(\"no such table\") || msg.includes(\"no such column\")) {\n const emptyResult = kind === \"span\" ? null : [];\n process.stdout.write(JSON.stringify(emptyResult, bigIntReplacer) + \"\\n\");\n adapter.close();\n return;\n }\n\n process.stderr.write(`Error: ${msg}\\n`);\n adapter.close();\n process.exit(1);\n }\n\n adapter.close();\n}\n","/**\n * CLI command handlers for aaac-observ.\n *\n * Implements the CommandHandlers interface generated by cli-contracts and\n * delegates to the domain-specific handler modules.\n *\n * Usage (in index.ts):\n * import { handlers } from './handlers.js';\n * import { createProgram } from '../generated/program.js';\n * const program = createProgram(handlers, version);\n * await program.parseAsync();\n */\nimport type { CommandHandlers } from \"../generated/program.js\";\nimport { handleRecord } from \"./record.js\";\nimport { handleRecordHook } from \"./record-hook.js\";\nimport { handleQuery } from \"./query.js\";\n\nexport const handlers: CommandHandlers = {\n record: handleRecord,\n recordHook: handleRecordHook,\n query: handleQuery,\n};\n"],"mappings":"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","names":["dirname","handlers","program","resolve","dirname"]}