autotel-cli 0.8.7 → 0.8.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/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command } from "commander";
4
+ import { Command as Command11 } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import * as path6 from "path";
7
+ import * as fs4 from "fs";
8
+ import * as path9 from "path";
8
9
  import { execSync } from "child_process";
9
10
 
10
11
  // src/lib/project.ts
@@ -330,11 +331,25 @@ function createCodeFile() {
330
331
  backendImports: [],
331
332
  pluginImports: [],
332
333
  subscriberImports: [],
334
+ loggerImports: [],
335
+ loggerSetup: null,
336
+ loggerExpr: null,
337
+ autoInstrumentations: [],
333
338
  backendConfig: null,
334
339
  subscribersConfig: [],
335
340
  pluginInit: []
336
341
  };
337
342
  }
343
+ function setPinoLogger(file) {
344
+ file.loggerImports.push({ source: "pino", default: "pino" });
345
+ file.loggerSetup = `const logger = pino({ name: 'app', level: process.env.LOG_LEVEL ?? 'info' });`;
346
+ file.loggerExpr = "logger";
347
+ }
348
+ function addAutoInstrumentationLogger(file, name) {
349
+ if (!file.autoInstrumentations.includes(name)) {
350
+ file.autoInstrumentations.push(name);
351
+ }
352
+ }
338
353
  function addImport(file, imp, section) {
339
354
  switch (section) {
340
355
  case "backend":
@@ -362,7 +377,7 @@ function addPluginInit(file, init) {
362
377
  function sortImports(imports) {
363
378
  const sideEffect = [];
364
379
  const external = [];
365
- const relative5 = [];
380
+ const relative6 = [];
366
381
  for (const imp of imports) {
367
382
  if (imp.sideEffect) {
368
383
  if (imp.source === "autotel/register") {
@@ -371,15 +386,15 @@ function sortImports(imports) {
371
386
  sideEffect.push(imp);
372
387
  }
373
388
  } else if (imp.source.startsWith(".") || imp.source.startsWith("/")) {
374
- relative5.push(imp);
389
+ relative6.push(imp);
375
390
  } else {
376
391
  external.push(imp);
377
392
  }
378
393
  }
379
394
  const sortBySource = (a, b) => a.source.localeCompare(b.source);
380
395
  external.sort(sortBySource);
381
- relative5.sort(sortBySource);
382
- return [...sideEffect, ...external, ...relative5];
396
+ relative6.sort(sortBySource);
397
+ return [...sideEffect, ...external, ...relative6];
383
398
  }
384
399
  function renderImport(imp) {
385
400
  if (imp.sideEffect) {
@@ -427,7 +442,23 @@ function renderCodeFile(file) {
427
442
  lines.push(renderImports(file.subscriberImports));
428
443
  lines.push("");
429
444
  }
445
+ if (file.loggerImports.length > 0) {
446
+ lines.push(SECTION_MARKER("LOGGER"));
447
+ lines.push(renderImports(file.loggerImports));
448
+ lines.push("");
449
+ }
450
+ if (file.loggerSetup !== null) {
451
+ lines.push(file.loggerSetup);
452
+ lines.push("");
453
+ }
430
454
  lines.push("init({");
455
+ if (file.loggerExpr !== null) {
456
+ lines.push(` logger: ${file.loggerExpr},`);
457
+ }
458
+ if (file.autoInstrumentations.length > 0) {
459
+ const list = file.autoInstrumentations.map((n) => `'${n}'`).join(", ");
460
+ lines.push(` autoInstrumentations: [${list}],`);
461
+ }
431
462
  if (file.backendConfig) {
432
463
  lines.push(" " + SECTION_MARKER("BACKEND_CONFIG"));
433
464
  lines.push(` ${file.backendConfig}`);
@@ -638,60 +669,192 @@ async function checkEnvVarsPresent(packageRoot, varNames, specificFile) {
638
669
  return results;
639
670
  }
640
671
 
641
- // src/lib/dependency-planner.ts
642
- function createDependencyPlan() {
643
- return {
644
- required: [],
645
- optional: [],
646
- devOnly: []
647
- };
648
- }
649
- function mergePackages(target, source) {
650
- for (const pkg of source) {
651
- if (!target.includes(pkg)) {
652
- target.push(pkg);
672
+ // src/lib/dep-detector.ts
673
+ import * as fs2 from "fs";
674
+ import * as path6 from "path";
675
+ var DEP_TO_PRESET = {
676
+ // First-party autotel wrappers
677
+ "@sentry/node": "sentry",
678
+ "@sentry/bun": "sentry",
679
+ hono: "hono",
680
+ "@modelcontextprotocol/sdk": "mcp",
681
+ "@tanstack/start": "tanstack",
682
+ "@tanstack/start-server": "tanstack",
683
+ "@tanstack/start-client": "tanstack",
684
+ // Subscribers
685
+ "posthog-node": "posthog",
686
+ "@posthog/node": "posthog",
687
+ mixpanel: "mixpanel",
688
+ "@amplitude/analytics-node": "amplitude",
689
+ "@segment/analytics-node": "segment",
690
+ "@slack/web-api": "slack",
691
+ "@slack/webhook": "slack",
692
+ // Plugins (existing presets)
693
+ mongoose: "mongoose",
694
+ "drizzle-orm": "drizzle",
695
+ // Auto-instrumented (no first-party preset needed)
696
+ express: "auto-instr",
697
+ fastify: "auto-instr",
698
+ "@nestjs/core": "auto-instr",
699
+ next: "auto-instr",
700
+ pg: "auto-instr",
701
+ mysql: "auto-instr",
702
+ mysql2: "auto-instr",
703
+ redis: "auto-instr",
704
+ ioredis: "auto-instr",
705
+ "@aws-sdk/client-s3": "auto-instr",
706
+ graphql: "auto-instr"
707
+ };
708
+ var LOGGER_DEPS = {
709
+ pino: "pino",
710
+ winston: "winston",
711
+ bunyan: "bunyan"
712
+ };
713
+ var ENV_KEY_TO_BACKEND = [
714
+ { key: "DD_API_KEY", slug: "datadog" },
715
+ { key: "DATADOG_API_KEY", slug: "datadog" },
716
+ { key: "HONEYCOMB_API_KEY", slug: "honeycomb" },
717
+ { key: "HONEYCOMB_WRITE_KEY", slug: "honeycomb" },
718
+ { key: "GOOGLE_CLOUD_PROJECT", slug: "google-cloud" },
719
+ // OTEL endpoint is generic enough that we pick otlp-http
720
+ { key: "OTEL_EXPORTER_OTLP_ENDPOINT", slug: "otlp-http" }
721
+ ];
722
+ function mergeDeps(target, root) {
723
+ const out = /* @__PURE__ */ new Map();
724
+ if (root) {
725
+ for (const [name, version] of Object.entries({
726
+ ...root.dependencies,
727
+ ...root.devDependencies
728
+ })) {
729
+ out.set(name, { version, resolution: "workspace-root" });
653
730
  }
654
731
  }
732
+ for (const [name, version] of Object.entries({
733
+ ...target.dependencies,
734
+ ...target.devDependencies
735
+ })) {
736
+ out.set(name, { version, resolution: "target" });
737
+ }
738
+ return out;
655
739
  }
656
- function addPresetToPlan(plan, preset) {
657
- mergePackages(plan.required, preset.packages.required);
658
- mergePackages(plan.optional, preset.packages.optional);
659
- mergePackages(plan.devOnly, preset.packages.devOnly);
660
- }
661
- function addPresetsToPlan(plan, presets) {
662
- for (const preset of presets) {
663
- addPresetToPlan(plan, preset);
740
+ function detectLoggers(deps) {
741
+ const present = [];
742
+ for (const [name, kind] of Object.entries(LOGGER_DEPS)) {
743
+ if (deps.has(name)) present.push(kind);
744
+ }
745
+ if (present.length === 0) return { primary: null, autoInstrument: [] };
746
+ if (present.includes("pino")) {
747
+ return {
748
+ primary: "pino",
749
+ autoInstrument: present.filter((l) => l !== "pino")
750
+ };
664
751
  }
752
+ return {
753
+ primary: present[0] ?? null,
754
+ autoInstrument: present.slice(1)
755
+ };
665
756
  }
666
- function getProdPackages(plan) {
667
- return [...plan.required, ...plan.optional];
757
+ function parseEnvKeys(content) {
758
+ const out = /* @__PURE__ */ new Set();
759
+ for (const rawLine of content.split("\n")) {
760
+ const line = rawLine.trim();
761
+ if (line.length === 0 || line.startsWith("#")) continue;
762
+ const eq = line.indexOf("=");
763
+ if (eq === -1) continue;
764
+ const key = line.slice(0, eq).trim();
765
+ if (key.length > 0) out.add(key);
766
+ }
767
+ return out;
668
768
  }
669
- function getDevPackages(plan) {
670
- return [...plan.devOnly];
769
+ function collectEnvKeys(opts) {
770
+ const keys = /* @__PURE__ */ new Set();
771
+ const sources = [];
772
+ const tryRead = (name, requiresConsent) => {
773
+ if (requiresConsent && !opts.envConsent) return;
774
+ const p = path6.join(opts.packageRoot, name);
775
+ const content = readFileSafe(p);
776
+ if (content === null) return;
777
+ sources.push(name);
778
+ for (const k of parseEnvKeys(content)) keys.add(k);
779
+ };
780
+ tryRead(".env.example", false);
781
+ tryRead(".env.sample", false);
782
+ tryRead(".env", true);
783
+ tryRead(".env.local", true);
784
+ return { keys, sources };
671
785
  }
672
- function addCorePackages(plan) {
673
- mergePackages(plan.required, ["autotel"]);
786
+ function detectBackend(opts) {
787
+ for (const { key, slug } of ENV_KEY_TO_BACKEND) {
788
+ if (opts.envKeys.has(key)) {
789
+ return { slug, source: "env", detail: key };
790
+ }
791
+ }
792
+ if (opts.deps.has("dd-trace")) {
793
+ return { slug: "datadog", source: "deps", detail: "dd-trace" };
794
+ }
795
+ return { slug: "local", source: "default" };
674
796
  }
675
- function addAutoInstrumentationPackages(plan, selection) {
676
- if (selection === "none") {
677
- return;
797
+ function detectPlatform(packageRoot) {
798
+ if (fileExists(path6.join(packageRoot, "wrangler.toml")) || fileExists(path6.join(packageRoot, "wrangler.jsonc")) || fileExists(path6.join(packageRoot, "wrangler.json"))) {
799
+ return "cloudflare";
678
800
  }
679
- if (selection === "all") {
680
- mergePackages(plan.required, ["@opentelemetry/auto-instrumentations-node"]);
681
- return;
801
+ return null;
802
+ }
803
+ function detectInProject(opts) {
804
+ const { project } = opts;
805
+ let rootPkg = null;
806
+ if (project.workspace.workspaceRoot !== null && project.workspace.workspaceRoot !== project.packageRoot) {
807
+ rootPkg = readJsonSafe(
808
+ path6.join(project.workspace.workspaceRoot, "package.json")
809
+ );
682
810
  }
683
- for (const name of selection) {
684
- mergePackages(plan.required, [`@opentelemetry/instrumentation-${name}`]);
811
+ const deps = mergeDeps(project.packageJson, rootPkg);
812
+ const detectedPackages = [];
813
+ const presetSet = /* @__PURE__ */ new Set();
814
+ const autoInstrDeps = [];
815
+ for (const [name, info2] of deps.entries()) {
816
+ const mapping = DEP_TO_PRESET[name];
817
+ if (mapping === void 0 && LOGGER_DEPS[name] === void 0) continue;
818
+ detectedPackages.push({
819
+ name,
820
+ version: info2.version,
821
+ resolution: info2.resolution
822
+ });
823
+ if (mapping === "auto-instr") {
824
+ autoInstrDeps.push(name);
825
+ } else if (mapping !== void 0) {
826
+ presetSet.add(mapping);
827
+ }
685
828
  }
829
+ const loggers = detectLoggers(deps);
830
+ const platform = detectPlatform(project.packageRoot);
831
+ if (platform !== null) presetSet.add(platform);
832
+ const { keys: envKeys } = collectEnvKeys({
833
+ packageRoot: project.packageRoot,
834
+ envConsent: opts.envConsent
835
+ });
836
+ const backend = detectBackend({
837
+ envKeys,
838
+ packageRoot: project.packageRoot,
839
+ deps
840
+ });
841
+ return {
842
+ packages: detectedPackages,
843
+ presets: [...presetSet],
844
+ primaryLogger: loggers.primary,
845
+ autoInstrumentLoggers: loggers.autoInstrument,
846
+ autoInstrumentedDeps: autoInstrDeps,
847
+ backend,
848
+ platform
849
+ };
686
850
  }
687
- function buildDependencyPlan(options) {
688
- const plan = createDependencyPlan();
689
- addCorePackages(plan);
690
- addPresetsToPlan(plan, options.presets);
691
- addAutoInstrumentationPackages(plan, options.autoInstrumentations);
692
- return plan;
851
+ function envFilesRequireConsent(packageRoot) {
852
+ return fileExists(path6.join(packageRoot, ".env")) || fileExists(path6.join(packageRoot, ".env.local"));
693
853
  }
694
854
 
855
+ // src/lib/plan-builder.ts
856
+ import * as path7 from "path";
857
+
695
858
  // src/presets/backends/datadog.ts
696
859
  var datadogDirect = {
697
860
  name: "Datadog (Direct)",
@@ -1475,6 +1638,183 @@ var drizzle = {
1475
1638
  ]
1476
1639
  };
1477
1640
 
1641
+ // src/presets/plugins/sentry.ts
1642
+ var sentry = {
1643
+ name: "Sentry",
1644
+ slug: "sentry",
1645
+ type: "plugin",
1646
+ description: "Send traces to Sentry via OTLP and link captured errors to active traces",
1647
+ packages: {
1648
+ required: ["autotel-sentry", "@sentry/node"],
1649
+ optional: [],
1650
+ devOnly: []
1651
+ },
1652
+ env: {
1653
+ required: [
1654
+ {
1655
+ name: "SENTRY_DSN",
1656
+ description: "Sentry Data Source Name (project ingest URL)",
1657
+ example: "https://<key>@o<org>.ingest.sentry.io/<project>",
1658
+ sensitive: true
1659
+ }
1660
+ ],
1661
+ optional: []
1662
+ },
1663
+ imports: [
1664
+ {
1665
+ source: "@sentry/node",
1666
+ specifiers: ["*"],
1667
+ default: "* as Sentry"
1668
+ },
1669
+ {
1670
+ source: "autotel-sentry",
1671
+ specifiers: ["sentryOtlpConfig", "linkSentryErrors"]
1672
+ }
1673
+ ],
1674
+ configBlock: {
1675
+ type: "plugin",
1676
+ code: `// Sentry OTLP setup
1677
+ const sentryConfig = sentryOtlpConfig(process.env.SENTRY_DSN!);
1678
+ Sentry.init({ dsn: sentryConfig.dsn, skipOpenTelemetrySetup: true });
1679
+ linkSentryErrors(Sentry);`,
1680
+ section: "PLUGIN_INIT"
1681
+ },
1682
+ nextSteps: [
1683
+ "Set SENTRY_DSN in .env (from Sentry \u2192 Settings \u2192 Projects \u2192 Client Keys)",
1684
+ "Captured Sentry errors will now be linked to active OTel traces",
1685
+ "Pass sentryConfig.endpoint and sentryConfig.headers to init() if you want Sentry as the trace backend"
1686
+ ]
1687
+ };
1688
+
1689
+ // src/presets/plugins/hono.ts
1690
+ var hono = {
1691
+ name: "Hono",
1692
+ slug: "hono",
1693
+ type: "plugin",
1694
+ description: "HTTP tracing + metrics middleware for Hono apps",
1695
+ packages: {
1696
+ required: ["autotel-hono"],
1697
+ optional: ["autotel-adapters"],
1698
+ devOnly: []
1699
+ },
1700
+ env: {
1701
+ required: [],
1702
+ optional: []
1703
+ },
1704
+ imports: [
1705
+ {
1706
+ source: "autotel-hono",
1707
+ specifiers: ["otel"]
1708
+ }
1709
+ ],
1710
+ configBlock: {
1711
+ type: "plugin",
1712
+ code: `// Register the otel() middleware on your Hono app:
1713
+ // app.use('*', otel({
1714
+ // serviceName: 'my-service',
1715
+ // captureRequestHeaders: ['user-agent'],
1716
+ // captureResponseHeaders: ['content-type'],
1717
+ // }));`,
1718
+ section: "PLUGIN_INIT"
1719
+ },
1720
+ nextSteps: [
1721
+ "Add otel() middleware to your Hono app: app.use('*', otel({ serviceName: '<name>' }))",
1722
+ "Optionally install autotel-adapters and use useLogger() to thread the request logger",
1723
+ "See packages/autotel-hono README for header/metric configuration"
1724
+ ]
1725
+ };
1726
+
1727
+ // src/presets/plugins/mcp.ts
1728
+ var mcp = {
1729
+ name: "MCP (Model Context Protocol)",
1730
+ slug: "mcp",
1731
+ type: "plugin",
1732
+ description: "Distributed tracing for MCP servers and clients (W3C trace context via _meta)",
1733
+ packages: {
1734
+ required: ["autotel-mcp-instrumentation"],
1735
+ optional: [],
1736
+ devOnly: []
1737
+ },
1738
+ env: {
1739
+ required: [],
1740
+ optional: []
1741
+ },
1742
+ imports: [
1743
+ {
1744
+ source: "autotel-mcp-instrumentation",
1745
+ specifiers: ["instrumentMcpServer"]
1746
+ }
1747
+ ],
1748
+ configBlock: {
1749
+ type: "plugin",
1750
+ code: `// Wrap your MCP server after constructing it:
1751
+ // const instrumented = instrumentMcpServer(server, {
1752
+ // networkTransport: 'pipe',
1753
+ // captureToolArgs: true,
1754
+ // captureToolResults: false,
1755
+ // captureErrors: true,
1756
+ // });
1757
+ //
1758
+ // For clients, import { instrumentMcpClient } and wrap the client.`,
1759
+ section: "PLUGIN_INIT"
1760
+ },
1761
+ nextSteps: [
1762
+ "Wrap your MCP server with instrumentMcpServer(server, { ... })",
1763
+ "For clients, use instrumentMcpClient from autotel-mcp-instrumentation",
1764
+ "Traces follow OTel MCP semantic conventions and propagate via the _meta field"
1765
+ ]
1766
+ };
1767
+
1768
+ // src/presets/plugins/tanstack.ts
1769
+ var tanstack = {
1770
+ name: "TanStack Start",
1771
+ slug: "tanstack",
1772
+ type: "plugin",
1773
+ description: "Tracing middleware for TanStack Start (SSR requests + server functions)",
1774
+ packages: {
1775
+ required: ["autotel-tanstack"],
1776
+ optional: [],
1777
+ devOnly: []
1778
+ },
1779
+ env: {
1780
+ required: [],
1781
+ optional: []
1782
+ },
1783
+ imports: [
1784
+ {
1785
+ source: "autotel-tanstack/middleware",
1786
+ specifiers: ["createTracingServerHandler"]
1787
+ }
1788
+ ],
1789
+ configBlock: {
1790
+ type: "plugin",
1791
+ code: `// In your start.ts, register tracing middleware on createStart():
1792
+ // import { createMiddleware, createStart } from '@tanstack/react-start';
1793
+ //
1794
+ // const requestTracing = createMiddleware().server(
1795
+ // createTracingServerHandler({
1796
+ // captureHeaders: ['x-request-id', 'user-agent'],
1797
+ // excludePaths: ['/health', '/metrics'],
1798
+ // }),
1799
+ // );
1800
+ //
1801
+ // const functionTracing = createMiddleware({ type: 'function' }).server(
1802
+ // createTracingServerHandler({ type: 'function', captureArgs: true }),
1803
+ // );
1804
+ //
1805
+ // export const startInstance = createStart(() => ({
1806
+ // requestMiddleware: [requestTracing],
1807
+ // functionMiddleware: [functionTracing],
1808
+ // }));`,
1809
+ section: "PLUGIN_INIT"
1810
+ },
1811
+ nextSteps: [
1812
+ "Add tracing middleware to your createStart() call in src/start.ts",
1813
+ 'Alternative: import "autotel-tanstack/auto" for zero-config init via OTEL_* env vars',
1814
+ "See packages/autotel-tanstack README for loader/server-function tracing helpers"
1815
+ ]
1816
+ };
1817
+
1478
1818
  // src/presets/platforms/aws.ts
1479
1819
  var awsLambda = {
1480
1820
  name: "AWS Lambda",
@@ -1601,7 +1941,11 @@ var subscribers = /* @__PURE__ */ new Map([
1601
1941
  ]);
1602
1942
  var plugins = /* @__PURE__ */ new Map([
1603
1943
  ["mongoose", mongoose],
1604
- ["drizzle", drizzle]
1944
+ ["drizzle", drizzle],
1945
+ ["sentry", sentry],
1946
+ ["hono", hono],
1947
+ ["mcp", mcp],
1948
+ ["tanstack", tanstack]
1605
1949
  ]);
1606
1950
  var platforms = /* @__PURE__ */ new Map([
1607
1951
  ["aws-lambda", awsLambda],
@@ -1693,120 +2037,399 @@ function listPresetSlugs(type) {
1693
2037
  return [...getPresetsByType(type).keys()];
1694
2038
  }
1695
2039
 
1696
- // src/ui/prompts.ts
1697
- import { select, checkbox, confirm, input } from "@inquirer/prompts";
1698
- async function promptRuntime() {
1699
- return await select({
1700
- message: "What runtime are you using?",
1701
- choices: [
1702
- { value: "node", name: "Node.js" },
1703
- { value: "lambda", name: "AWS Lambda" },
1704
- { value: "cloudflare", name: "Cloudflare Workers" },
1705
- { value: "edge", name: "Edge Runtime (Vercel Edge, etc.)" }
1706
- ],
1707
- default: "node"
2040
+ // src/lib/plan-builder.ts
2041
+ function buildPlanFromDetection(opts) {
2042
+ const { project, detection } = opts;
2043
+ const presets = [];
2044
+ const presetSlugs = [];
2045
+ const backendPreset = findPreset(detection.backend.slug);
2046
+ if (backendPreset !== null) {
2047
+ presets.push(backendPreset);
2048
+ presetSlugs.push(detection.backend.slug);
2049
+ }
2050
+ for (const slug of detection.presets) {
2051
+ if (slug === detection.backend.slug) continue;
2052
+ const p = findPreset(slug);
2053
+ if (p !== null) {
2054
+ presets.push(p);
2055
+ presetSlugs.push(slug);
2056
+ }
2057
+ }
2058
+ const prod = /* @__PURE__ */ new Set(["autotel"]);
2059
+ const dev = /* @__PURE__ */ new Set();
2060
+ for (const p of presets) {
2061
+ for (const pkg of p.packages.required) prod.add(pkg);
2062
+ for (const pkg of p.packages.optional) prod.add(pkg);
2063
+ for (const pkg of p.packages.devOnly) dev.add(pkg);
2064
+ }
2065
+ if (detection.primaryLogger === "pino") prod.add("pino");
2066
+ if (detection.autoInstrumentedDeps.length > 0 || detection.autoInstrumentLoggers.length > 0 || detection.primaryLogger === "winston" || detection.primaryLogger === "bunyan") {
2067
+ prod.add("@opentelemetry/auto-instrumentations-node");
2068
+ }
2069
+ const envVars = [];
2070
+ const seenEnv = /* @__PURE__ */ new Set();
2071
+ for (const p of presets) {
2072
+ for (const ev of [...p.env.required, ...p.env.optional]) {
2073
+ if (seenEnv.has(ev.name)) continue;
2074
+ seenEnv.add(ev.name);
2075
+ envVars.push({
2076
+ name: ev.name,
2077
+ sensitive: ev.sensitive,
2078
+ action: "add-to-.env.example"
2079
+ });
2080
+ }
2081
+ }
2082
+ const instrumentationPath = getInstrumentationPath(
2083
+ project.packageRoot,
2084
+ project.hasTypeScript
2085
+ );
2086
+ const filesToWrite = [
2087
+ {
2088
+ path: path7.relative(project.cwd, instrumentationPath),
2089
+ action: fileExists(instrumentationPath) ? "merge" : "create"
2090
+ }
2091
+ ];
2092
+ const envExamplePath = path7.join(project.packageRoot, ".env.example");
2093
+ if (envVars.length > 0 && !fileExists(envExamplePath)) {
2094
+ filesToWrite.push({
2095
+ path: path7.relative(project.cwd, envExamplePath),
2096
+ action: "create"
2097
+ });
2098
+ }
2099
+ const nextSteps = presets.flatMap((p) => p.nextSteps);
2100
+ const plan = {
2101
+ v: 1,
2102
+ presets: presetSlugs,
2103
+ packagesToInstall: { prod: [...prod], dev: [...dev] },
2104
+ filesToWrite,
2105
+ envVars,
2106
+ nextSteps,
2107
+ detected: {
2108
+ packages: detection.packages,
2109
+ primaryLogger: detection.primaryLogger,
2110
+ autoInstrumentLoggers: detection.autoInstrumentLoggers,
2111
+ autoInstrumentedDeps: detection.autoInstrumentedDeps,
2112
+ backend: detection.backend,
2113
+ platform: detection.platform
2114
+ }
2115
+ };
2116
+ return { plan, presets, instrumentationPath };
2117
+ }
2118
+ function findPreset(slug) {
2119
+ for (const type of ["backend", "subscriber", "plugin", "platform"]) {
2120
+ const p = getPreset(type, slug);
2121
+ if (p !== void 0) return p;
2122
+ }
2123
+ return null;
2124
+ }
2125
+
2126
+ // src/lib/errors.ts
2127
+ var AutotelErrorCodes = {
2128
+ // validation
2129
+ E_NO_PACKAGE_JSON: "AUTOTEL_E_NO_PACKAGE_JSON",
2130
+ E_UNKNOWN_PRESET: "AUTOTEL_E_UNKNOWN_PRESET",
2131
+ E_INVALID_PLAN: "AUTOTEL_E_INVALID_PLAN",
2132
+ E_INVALID_INPUT: "AUTOTEL_E_INVALID_INPUT",
2133
+ E_INVALID_FLAG: "AUTOTEL_E_INVALID_FLAG",
2134
+ // environment
2135
+ E_NO_WORKSPACE_PACKAGES: "AUTOTEL_E_NO_WORKSPACE_PACKAGES",
2136
+ E_ENV_CONSENT_REQUIRED: "AUTOTEL_E_ENV_CONSENT_REQUIRED",
2137
+ // conflict
2138
+ E_EXISTING_CONFIG: "AUTOTEL_E_EXISTING_CONFIG",
2139
+ E_AMBIGUOUS_LOGGER: "AUTOTEL_E_AMBIGUOUS_LOGGER",
2140
+ // install
2141
+ E_INSTALL_FAILED: "AUTOTEL_E_INSTALL_FAILED",
2142
+ // io
2143
+ E_WRITE_FAILED: "AUTOTEL_E_WRITE_FAILED",
2144
+ E_READ_FAILED: "AUTOTEL_E_READ_FAILED",
2145
+ // runtime
2146
+ E_UNKNOWN: "AUTOTEL_E_UNKNOWN"
2147
+ };
2148
+ var AutotelError = class extends Error {
2149
+ type;
2150
+ code;
2151
+ retryable;
2152
+ fix;
2153
+ expected;
2154
+ suggestions;
2155
+ constructor(opts) {
2156
+ super(opts.message);
2157
+ this.name = "AutotelError";
2158
+ this.type = opts.type;
2159
+ this.code = opts.code;
2160
+ this.retryable = opts.retryable ?? false;
2161
+ this.fix = opts.fix;
2162
+ this.expected = opts.expected;
2163
+ this.suggestions = opts.suggestions;
2164
+ }
2165
+ toEnvelope(command) {
2166
+ return {
2167
+ ok: false,
2168
+ ...command !== void 0 ? { command } : {},
2169
+ error: {
2170
+ type: this.type,
2171
+ code: this.code,
2172
+ message: this.message,
2173
+ retryable: this.retryable,
2174
+ ...this.fix !== void 0 ? { fix: this.fix } : {},
2175
+ ...this.expected !== void 0 ? { expected: this.expected } : {},
2176
+ ...this.suggestions !== void 0 ? { suggestions: this.suggestions } : {}
2177
+ }
2178
+ };
2179
+ }
2180
+ };
2181
+ function exitCodeForError(err) {
2182
+ if (err.type === "validation" || err.type === "conflict") {
2183
+ return 2;
2184
+ }
2185
+ return 1;
2186
+ }
2187
+ function toAutotelError(value) {
2188
+ if (value instanceof AutotelError) return value;
2189
+ const message = value instanceof Error ? value.message : String(value);
2190
+ return new AutotelError({
2191
+ type: "runtime",
2192
+ code: AutotelErrorCodes.E_UNKNOWN,
2193
+ message
1708
2194
  });
1709
2195
  }
1710
- async function promptBackend(backends2) {
1711
- const choices = [
1712
- { value: "local", name: "Local/Console (development only)" },
1713
- ...[...backends2.entries()].map(([slug, preset]) => ({
1714
- value: slug,
1715
- name: `${preset.name} - ${preset.description}`
1716
- }))
2196
+
2197
+ // src/lib/plan.ts
2198
+ function parsePlan(input2) {
2199
+ if (typeof input2 !== "object" || input2 === null || Array.isArray(input2)) {
2200
+ throw new AutotelError({
2201
+ type: "validation",
2202
+ code: AutotelErrorCodes.E_INVALID_PLAN,
2203
+ message: "Plan must be a JSON object",
2204
+ expected: { v: 1, presets: "string[]" }
2205
+ });
2206
+ }
2207
+ const obj = input2;
2208
+ if (obj["v"] !== 1) {
2209
+ throw new AutotelError({
2210
+ type: "validation",
2211
+ code: AutotelErrorCodes.E_INVALID_PLAN,
2212
+ message: `Unsupported plan version: ${String(obj["v"])}`,
2213
+ fix: "Regenerate the plan with `autotel init --json --dry-run`",
2214
+ expected: { v: 1 }
2215
+ });
2216
+ }
2217
+ if (!Array.isArray(obj["presets"])) {
2218
+ throw new AutotelError({
2219
+ type: "validation",
2220
+ code: AutotelErrorCodes.E_INVALID_PLAN,
2221
+ message: "plan.presets must be an array of preset slugs"
2222
+ });
2223
+ }
2224
+ const pkgs = obj["packagesToInstall"];
2225
+ if (typeof pkgs !== "object" || pkgs === null || !Array.isArray(pkgs["prod"]) || !Array.isArray(pkgs["dev"])) {
2226
+ throw new AutotelError({
2227
+ type: "validation",
2228
+ code: AutotelErrorCodes.E_INVALID_PLAN,
2229
+ message: "plan.packagesToInstall must be { prod: string[], dev: string[] }"
2230
+ });
2231
+ }
2232
+ return obj;
2233
+ }
2234
+
2235
+ // src/lib/instrumentation-parser.ts
2236
+ function parseInstrumentation(content) {
2237
+ const cliOwned = hasCliOwnershipHeader(content);
2238
+ const importedSources = collectImportSources(content);
2239
+ const detectedLogger = pickLogger(importedSources);
2240
+ const autoInstrumentations = parseAutoInstrumentations(content);
2241
+ return { cliOwned, importedSources, detectedLogger, autoInstrumentations };
2242
+ }
2243
+ function collectImportSources(content) {
2244
+ const out = /* @__PURE__ */ new Set();
2245
+ const re = /import\s+(?:[^'"]*\s+from\s+)?['"]([^'"]+)['"]/g;
2246
+ let match;
2247
+ while ((match = re.exec(content)) !== null) {
2248
+ if (match[1] !== void 0) out.add(match[1]);
2249
+ }
2250
+ return out;
2251
+ }
2252
+ function pickLogger(imports) {
2253
+ if (imports.has("pino")) return "pino";
2254
+ if (imports.has("winston")) return "winston";
2255
+ if (imports.has("bunyan")) return "bunyan";
2256
+ return null;
2257
+ }
2258
+ function parseAutoInstrumentations(content) {
2259
+ const re = /autoInstrumentations\s*:\s*\[([^\]]*)\]/;
2260
+ const match = re.exec(content);
2261
+ if (!match || match[1] === void 0) return [];
2262
+ return [...match[1].matchAll(/['"]([^'"]+)['"]/g)].map((m) => m[1]).filter((s) => s !== void 0);
2263
+ }
2264
+ function diffImportSources(existing, planImportSources) {
2265
+ return planImportSources.filter((s) => !existing.importedSources.has(s));
2266
+ }
2267
+ function diffAutoInstrumentations(existing, planEntries) {
2268
+ return planEntries.filter((e) => !existing.autoInstrumentations.includes(e));
2269
+ }
2270
+
2271
+ // src/ui/preview.ts
2272
+ import chalk from "chalk";
2273
+ import { checkbox, select } from "@inquirer/prompts";
2274
+ function renderPlanPreview(plan) {
2275
+ const lines = [
2276
+ chalk.bold("autotel init \u2014 proposed plan"),
2277
+ ""
1717
2278
  ];
1718
- return await select({
1719
- message: "Where do you want to send telemetry?",
1720
- choices,
1721
- default: "local"
1722
- });
2279
+ if (plan.detected) {
2280
+ const det = plan.detected;
2281
+ const pkgList = det.packages.map((p) => `${p.name}@${p.version}`).join(", ");
2282
+ lines.push(
2283
+ `${chalk.dim("Detected packages:")} ${pkgList || chalk.dim("(none)")}`
2284
+ );
2285
+ if (det.primaryLogger !== null) {
2286
+ lines.push(
2287
+ `${chalk.dim("Logger:")} ${chalk.bold(det.primaryLogger)} ${chalk.dim("(first-class)")}` + (det.autoInstrumentLoggers.length > 0 ? `, ${chalk.dim("+ auto-instrumented:")} ${det.autoInstrumentLoggers.join(", ")}` : "")
2288
+ );
2289
+ }
2290
+ if (det.autoInstrumentedDeps.length > 0) {
2291
+ lines.push(
2292
+ `${chalk.dim("Covered by auto-instrumentations-node:")} ${det.autoInstrumentedDeps.join(", ")}`
2293
+ );
2294
+ }
2295
+ lines.push(
2296
+ `${chalk.dim("Backend:")} ${chalk.bold(det.backend.slug)} ${chalk.dim(`(${det.backend.source}${det.backend.detail ? `: ${det.backend.detail}` : ""})`)}`
2297
+ );
2298
+ if (det.platform !== null) {
2299
+ lines.push(`${chalk.dim("Platform:")} ${chalk.bold(det.platform)}`);
2300
+ }
2301
+ }
2302
+ lines.push("");
2303
+ lines.push(chalk.bold("Will wire:"));
2304
+ for (const slug of plan.presets) {
2305
+ lines.push(` ${chalk.green("+")} ${slug}`);
2306
+ }
2307
+ lines.push("");
2308
+ if (plan.packagesToInstall.prod.length > 0) {
2309
+ lines.push(
2310
+ `${chalk.bold("Install:")} ${plan.packagesToInstall.prod.join(", ")}`
2311
+ );
2312
+ }
2313
+ if (plan.packagesToInstall.dev.length > 0) {
2314
+ lines.push(
2315
+ `${chalk.bold("Install (dev):")} ${plan.packagesToInstall.dev.join(", ")}`
2316
+ );
2317
+ }
2318
+ if (plan.envVars.length > 0) {
2319
+ lines.push("");
2320
+ lines.push(chalk.bold("Env vars:"));
2321
+ for (const ev of plan.envVars) {
2322
+ const marker = ev.sensitive ? chalk.yellow("[sensitive]") : "";
2323
+ lines.push(` ${ev.name} ${marker}`);
2324
+ }
2325
+ }
2326
+ if (plan.filesToWrite.length > 0) {
2327
+ lines.push("");
2328
+ lines.push(chalk.bold("Files:"));
2329
+ for (const f of plan.filesToWrite) {
2330
+ lines.push(` ${chalk.dim(f.action)} ${f.path}`);
2331
+ }
2332
+ }
2333
+ return lines.join("\n");
1723
2334
  }
1724
- async function promptLogging() {
1725
- return await select({
1726
- message: "Which logging framework do you use?",
2335
+ async function confirmOrEditPlan(plan) {
2336
+ console.log(renderPlanPreview(plan));
2337
+ console.log("");
2338
+ const choice = await select({
2339
+ message: "Proceed?",
1727
2340
  choices: [
1728
- { value: null, name: "None / Not sure" },
1729
- { value: "pino", name: "Pino" },
1730
- { value: "winston", name: "Winston" }
2341
+ { value: "apply", name: "Apply the plan above" },
2342
+ { value: "edit", name: "Edit \u2014 deselect items I do not want" },
2343
+ { value: "abort", name: "Abort, write nothing" }
1731
2344
  ],
1732
- default: null
2345
+ default: "apply"
1733
2346
  });
2347
+ if (choice === "abort") return null;
2348
+ if (choice === "apply") return plan;
2349
+ const kept = await checkbox({
2350
+ message: "Keep which presets?",
2351
+ choices: plan.presets.map((slug) => ({
2352
+ value: slug,
2353
+ name: slug,
2354
+ checked: true
2355
+ }))
2356
+ });
2357
+ return { ...plan, presets: kept };
1734
2358
  }
1735
- async function promptDatabases(plugins2) {
1736
- const choices = [...plugins2.entries()].filter(([, preset]) => preset.type === "plugin").map(([slug, preset]) => ({
1737
- value: slug,
1738
- name: `${preset.name} - ${preset.description}`
1739
- }));
1740
- if (choices.length === 0) {
1741
- return [];
2359
+
2360
+ // src/lib/json-output.ts
2361
+ import * as fs3 from "fs";
2362
+ import * as path8 from "path";
2363
+ var outputFilePath = null;
2364
+ var outputFileWritten = false;
2365
+ var outputRoot = null;
2366
+ var redactSecrets = false;
2367
+ function configureJsonOutput(opts) {
2368
+ outputFilePath = opts.outputFile ?? null;
2369
+ outputFileWritten = false;
2370
+ outputRoot = opts.outputRoot ?? null;
2371
+ redactSecrets = opts.noSecrets === true || process.env["AUTOTEL_NO_SECRETS"] === "1" || process.env["AGENT_SANDBOX"] === "1";
2372
+ }
2373
+ var SECRET_KEY_PATTERN = /SECRET|TOKEN|PASSWORD|API[_-]?KEY|DSN/i;
2374
+ var SECRET_VALUE_PATTERN = /^[A-Za-z0-9_\-+/=]{40,}$/;
2375
+ var REDACTED = "[REDACTED]";
2376
+ function redact(value) {
2377
+ if (value === null || value === void 0) return value;
2378
+ if (Array.isArray(value)) {
2379
+ return value.map((v) => redact(v));
2380
+ }
2381
+ if (typeof value === "object") {
2382
+ const out = {};
2383
+ for (const [k, v] of Object.entries(value)) {
2384
+ if (typeof v === "string" && SECRET_KEY_PATTERN.test(k)) {
2385
+ out[k] = REDACTED;
2386
+ } else {
2387
+ out[k] = redact(v);
2388
+ }
2389
+ }
2390
+ return out;
1742
2391
  }
1743
- return await checkbox({
1744
- message: "Which databases/ORMs do you use? (space to select, enter to continue)",
1745
- choices
1746
- });
2392
+ if (typeof value === "string" && SECRET_VALUE_PATTERN.test(value)) {
2393
+ return REDACTED;
2394
+ }
2395
+ return value;
1747
2396
  }
1748
- async function promptSubscribers(subscribers2) {
1749
- const choices = [...subscribers2.entries()].map(([slug, preset]) => ({
1750
- value: slug,
1751
- name: `${preset.name} - ${preset.description}`
1752
- }));
1753
- if (choices.length === 0) {
1754
- return [];
2397
+ function printJson(data) {
2398
+ const payload = redactSecrets ? redact(data) : data;
2399
+ const serialised = JSON.stringify(payload, null, 2);
2400
+ process.stdout.write(serialised + "\n");
2401
+ if (outputFilePath !== null && !outputFileWritten) {
2402
+ writeArtifactFile(outputFilePath, serialised);
2403
+ outputFileWritten = true;
1755
2404
  }
1756
- return await checkbox({
1757
- message: "Which event destinations? (space to select, enter to continue)",
1758
- choices
1759
- });
1760
2405
  }
1761
- async function promptAutoInstrumentation() {
1762
- return await select({
1763
- message: "Auto-instrument common libraries?",
1764
- choices: [
1765
- { value: "all", name: "All (recommended) - http, express, pg, redis, etc." },
1766
- { value: "specific", name: "Let me choose specific ones" },
1767
- { value: "none", name: "None - I'll handle it manually" }
1768
- ],
1769
- default: "all"
1770
- });
1771
- }
1772
- async function promptStartupStyle(hasTypeScript) {
1773
- const choices = hasTypeScript ? [
1774
- { value: "node-esm", name: "Node ESM (node --import) - Recommended" },
1775
- { value: "tsx", name: "tsx (tsx --import) - For development" },
1776
- { value: "ts-node", name: "ts-node" },
1777
- { value: "nextjs", name: "Next.js" },
1778
- { value: "other", name: "Other / Manual" }
1779
- ] : [
1780
- { value: "node-esm", name: "Node ESM (node --import) - Recommended" },
1781
- { value: "nextjs", name: "Next.js" },
1782
- { value: "other", name: "Other / Manual" }
1783
- ];
1784
- return await select({
1785
- message: "How do you start your app?",
1786
- choices,
1787
- default: "node-esm"
1788
- });
2406
+ function writeArtifactFile(filePath, content) {
2407
+ const resolved = path8.resolve(filePath);
2408
+ if (outputRoot !== null) {
2409
+ atomicWrite(resolved, content, { root: outputRoot });
2410
+ } else {
2411
+ fs3.mkdirSync(path8.dirname(resolved), { recursive: true });
2412
+ fs3.writeFileSync(resolved, content, "utf8");
2413
+ }
1789
2414
  }
1790
- async function promptExistingConfigAction() {
1791
- return await select({
1792
- message: "Existing instrumentation detected. What would you like to do?",
1793
- choices: [
1794
- { value: "update", name: "Update existing file (recommended)" },
1795
- { value: "new", name: "Create new file (src/autotel-config.mts)" },
1796
- { value: "abort", name: "Abort" }
1797
- ],
1798
- default: "update"
2415
+
2416
+ // src/ui/prompts.ts
2417
+ import { select as select2, checkbox as checkbox2, confirm, input } from "@inquirer/prompts";
2418
+ async function promptConfirm(message, defaultValue = true) {
2419
+ return await confirm({
2420
+ message,
2421
+ default: defaultValue
1799
2422
  });
1800
2423
  }
1801
2424
 
1802
2425
  // src/ui/output.ts
1803
- import chalk from "chalk";
2426
+ import chalk2 from "chalk";
1804
2427
  var STATUS = {
1805
- ok: chalk.green("[OK]"),
1806
- warn: chalk.yellow("[WARN]"),
1807
- error: chalk.red("[ERROR]"),
1808
- info: chalk.blue("[INFO]"),
1809
- skip: chalk.gray("[SKIP]")
2428
+ ok: chalk2.green("[OK]"),
2429
+ warn: chalk2.yellow("[WARN]"),
2430
+ error: chalk2.red("[ERROR]"),
2431
+ info: chalk2.blue("[INFO]"),
2432
+ skip: chalk2.gray("[SKIP]")
1810
2433
  };
1811
2434
  function formatCheck(check) {
1812
2435
  const lines = [];
@@ -1814,68 +2437,66 @@ function formatCheck(check) {
1814
2437
  lines.push(` ${statusToken} ${check.message}`);
1815
2438
  if (check.details && check.details.length > 0) {
1816
2439
  for (const detail of check.details) {
1817
- lines.push(` ${chalk.dim(detail)}`);
2440
+ lines.push(` ${chalk2.dim(detail)}`);
1818
2441
  }
1819
2442
  }
1820
2443
  if (check.fix) {
1821
- lines.push(` ${chalk.cyan("Fix:")} ${check.fix.cmd}`);
2444
+ lines.push(` ${chalk2.cyan("Fix:")} ${check.fix.cmd}`);
1822
2445
  }
1823
2446
  return lines;
1824
2447
  }
1825
2448
  function formatSummary(summary) {
1826
2449
  const parts = [];
1827
2450
  if (summary.ok > 0) {
1828
- parts.push(chalk.green(`${summary.ok} passed`));
2451
+ parts.push(chalk2.green(`${summary.ok} passed`));
1829
2452
  }
1830
2453
  if (summary.warnings > 0) {
1831
- parts.push(chalk.yellow(`${summary.warnings} warning${summary.warnings > 1 ? "s" : ""}`));
2454
+ parts.push(chalk2.yellow(`${summary.warnings} warning${summary.warnings > 1 ? "s" : ""}`));
1832
2455
  }
1833
2456
  if (summary.errors > 0) {
1834
- parts.push(chalk.red(`${summary.errors} error${summary.errors > 1 ? "s" : ""}`));
2457
+ parts.push(chalk2.red(`${summary.errors} error${summary.errors > 1 ? "s" : ""}`));
1835
2458
  }
1836
2459
  if (summary.skipped > 0) {
1837
- parts.push(chalk.gray(`${summary.skipped} skipped`));
2460
+ parts.push(chalk2.gray(`${summary.skipped} skipped`));
1838
2461
  }
1839
2462
  return `Summary: ${parts.join(", ")}`;
1840
2463
  }
1841
2464
  function formatFooter(options) {
1842
2465
  const lines = [""];
1843
2466
  if (options.detected) {
1844
- lines.push(chalk.dim(`Detected: ${options.detected}`));
2467
+ lines.push(chalk2.dim(`Detected: ${options.detected}`));
1845
2468
  }
1846
2469
  if (options.wrote && options.wrote.length > 0) {
1847
- lines.push(chalk.dim(`Wrote: ${options.wrote.join(", ")}`));
2470
+ lines.push(chalk2.dim(`Wrote: ${options.wrote.join(", ")}`));
1848
2471
  }
1849
2472
  if (options.next) {
1850
- lines.push(chalk.cyan(`Next: ${options.next}`));
2473
+ lines.push(chalk2.cyan(`Next: ${options.next}`));
1851
2474
  }
1852
2475
  return lines.join("\n");
1853
2476
  }
2477
+ function formatPackageManagerInfo(pm, lockfilePath) {
2478
+ if (lockfilePath) {
2479
+ return `${pm} (via ${chalk2.dim(lockfilePath)})`;
2480
+ }
2481
+ return `${pm} (default, no lockfile found)`;
2482
+ }
1854
2483
  function heading(text) {
1855
- console.log(chalk.bold(text));
2484
+ console.log(chalk2.bold(text));
1856
2485
  }
1857
2486
  function info(text) {
1858
- console.log(chalk.blue(text));
2487
+ console.log(chalk2.blue(text));
1859
2488
  }
1860
2489
  function success(text) {
1861
- console.log(chalk.green(text));
2490
+ console.log(chalk2.green(text));
1862
2491
  }
1863
2492
  function warn(text) {
1864
- console.log(chalk.yellow(text));
2493
+ console.log(chalk2.yellow(text));
1865
2494
  }
1866
2495
  function error(text) {
1867
- console.log(chalk.red(text));
2496
+ console.log(chalk2.red(text));
1868
2497
  }
1869
2498
  function dim(text) {
1870
- console.log(chalk.dim(text));
1871
- }
1872
- function isVerbose() {
1873
- return process.env["AUTOTEL_VERBOSE"] === "true";
1874
- }
1875
- function verbose(text) {
1876
- if (isVerbose()) {
1877
- console.log(chalk.gray(`[verbose] ${text}`));
1878
- }
2499
+ console.log(chalk2.dim(text));
1879
2500
  }
1880
2501
 
1881
2502
  // src/ui/spinner.ts
@@ -1906,104 +2527,200 @@ function isCI() {
1906
2527
 
1907
2528
  // src/commands/init.ts
1908
2529
  async function runInit(options) {
1909
- const spinner = createSpinner();
1910
- if (options.verbose) {
1911
- process.env["AUTOTEL_VERBOSE"] = "true";
2530
+ if (options.json) {
2531
+ configureJsonOutput({
2532
+ outputFile: options.outputFile,
2533
+ noSecrets: options.noSecrets
2534
+ });
1912
2535
  }
1913
- if (options.quiet) {
1914
- process.env["AUTOTEL_QUIET"] = "true";
1915
- }
1916
- spinner.start("Discovering project...");
2536
+ if (options.verbose) process.env["AUTOTEL_VERBOSE"] = "true";
2537
+ if (options.quiet) process.env["AUTOTEL_QUIET"] = "true";
1917
2538
  const project = discoverProject(options.cwd);
1918
- if (!project) {
1919
- spinner.fail("No package.json found");
1920
- error("Run this command in a directory with a package.json, or use --cwd");
1921
- process.exit(1);
2539
+ if (project === null) {
2540
+ throw new AutotelError({
2541
+ type: "environment",
2542
+ code: AutotelErrorCodes.E_NO_PACKAGE_JSON,
2543
+ message: `No package.json found at or above ${options.cwd}`,
2544
+ fix: "cd into a directory with a package.json, or pass --cwd <path>",
2545
+ expected: { file: "package.json" }
2546
+ });
1922
2547
  }
1923
- spinner.succeed(`Found ${project.packageJson.name ?? "project"}`);
1924
- verbose(`Package root: ${project.packageRoot}`);
1925
- verbose(`Package manager: ${project.packageManager}`);
1926
- const existingConfig = detectConfig(project.packageRoot);
1927
- if (existingConfig.found && !options.force) {
1928
- info(`Existing instrumentation detected at ${existingConfig.path}`);
1929
- if (options.yes || isCI()) {
1930
- warn("Use --force to overwrite existing config");
1931
- process.exit(0);
1932
- }
1933
- const action = await promptExistingConfigAction();
1934
- if (action === "abort") {
2548
+ if (options.json && options.outputFile !== void 0) {
2549
+ configureJsonOutput({
2550
+ outputFile: options.outputFile,
2551
+ outputRoot: project.packageRoot,
2552
+ noSecrets: options.noSecrets
2553
+ });
2554
+ }
2555
+ let plan = null;
2556
+ if (options.plan !== void 0) {
2557
+ plan = readPlanFromFile(options.plan);
2558
+ } else if (options.input !== void 0) {
2559
+ plan = await readPlanFromInput(options.input);
2560
+ } else if (options.preset !== void 0) {
2561
+ plan = planFromQuickPreset(options.preset, project);
2562
+ } else if (!options.noDetect) {
2563
+ plan = await planFromDetection(project, options);
2564
+ }
2565
+ if (plan === null) {
2566
+ throw new AutotelError({
2567
+ type: "validation",
2568
+ code: AutotelErrorCodes.E_INVALID_FLAG,
2569
+ message: "No plan source available (--no-detect disables detection)",
2570
+ fix: "Drop --no-detect or pass --plan / --input / --preset"
2571
+ });
2572
+ }
2573
+ const interactive = !options.yes && !options.noInteractive && !options.json && !isCI();
2574
+ if (interactive && options.plan === void 0 && options.input === void 0 && options.preset === void 0) {
2575
+ const confirmed = await confirmOrEditPlan(plan);
2576
+ if (confirmed === null) {
1935
2577
  info("Aborted");
1936
- process.exit(0);
1937
- }
1938
- }
1939
- const selectedPresets = [];
1940
- let autoInstrumentations = "all";
1941
- let startupStyle = "node-esm";
1942
- if (options.preset) {
1943
- const quickPreset = getQuickPreset(options.preset);
1944
- if (quickPreset) {
1945
- info(`Using quick preset: ${quickPreset.name}`);
1946
- const backendPreset = getPreset("backend", quickPreset.backend);
1947
- if (backendPreset) {
1948
- selectedPresets.push(backendPreset);
1949
- }
1950
- if (quickPreset.subscribers) {
1951
- for (const sub of quickPreset.subscribers) {
1952
- const subPreset = getPreset("subscriber", sub);
1953
- if (subPreset) selectedPresets.push(subPreset);
1954
- }
1955
- }
1956
- if (quickPreset.plugins) {
1957
- for (const plug of quickPreset.plugins) {
1958
- const plugPreset = getPreset("plugin", plug);
1959
- if (plugPreset) selectedPresets.push(plugPreset);
1960
- }
1961
- }
1962
- autoInstrumentations = quickPreset.autoInstrumentations;
2578
+ return;
2579
+ }
2580
+ plan = confirmed;
2581
+ }
2582
+ if (options.detectOnly) {
2583
+ if (options.json) {
2584
+ printJson({ ok: true, command: "autotel init", plan });
1963
2585
  } else {
1964
- error(`Unknown preset: ${options.preset}`);
1965
- info("Available presets: node-datadog-pino, node-datadog-agent, node-honeycomb, node-otlp");
1966
- process.exit(1);
2586
+ info("Detection-only mode \u2014 no files written");
2587
+ console.log(JSON.stringify(plan, null, 2));
1967
2588
  }
1968
- } else if (options.yes || isCI()) {
1969
- info("Using defaults (local backend, all auto-instrumentations)");
1970
- const localPreset = getPreset("backend", "local");
1971
- if (localPreset) {
1972
- selectedPresets.push(localPreset);
2589
+ return;
2590
+ }
2591
+ if (options.json && options.dryRun) {
2592
+ printJson({ ok: true, command: "autotel init", plan, dryRun: true });
2593
+ return;
2594
+ }
2595
+ if (options.json && !options.dryRun) {
2596
+ const applied2 = applyPlan({ plan, project, options });
2597
+ printJson({ ok: true, command: "autotel init", plan, applied: applied2 });
2598
+ return;
2599
+ }
2600
+ if (options.dryRun) {
2601
+ heading("\nDry run \u2014 no files will be written\n");
2602
+ console.log(JSON.stringify(plan, null, 2));
2603
+ return;
2604
+ }
2605
+ const applied = applyPlan({ plan, project, options });
2606
+ printApplySummary({ plan, applied, project, options });
2607
+ }
2608
+ function readPlanFromFile(filePath) {
2609
+ const content = readFileSafe(filePath);
2610
+ if (content === null) {
2611
+ throw new AutotelError({
2612
+ type: "io",
2613
+ code: AutotelErrorCodes.E_READ_FAILED,
2614
+ message: `Could not read plan file: ${filePath}`
2615
+ });
2616
+ }
2617
+ try {
2618
+ return parsePlan(JSON.parse(content));
2619
+ } catch (error2) {
2620
+ if (error2 instanceof AutotelError) throw error2;
2621
+ throw new AutotelError({
2622
+ type: "validation",
2623
+ code: AutotelErrorCodes.E_INVALID_PLAN,
2624
+ message: `Plan file is not valid JSON: ${error2.message}`
2625
+ });
2626
+ }
2627
+ }
2628
+ async function readPlanFromInput(input2) {
2629
+ if (input2 === "-") {
2630
+ const chunks = [];
2631
+ for await (const chunk of process.stdin) {
2632
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1973
2633
  }
1974
- } else {
1975
- const runtime = await promptRuntime();
1976
- verbose(`Runtime: ${runtime}`);
1977
- const backendSlug = await promptBackend(backends);
1978
- const backendPreset = getPreset("backend", backendSlug);
1979
- if (backendPreset) {
1980
- selectedPresets.push(backendPreset);
1981
- }
1982
- await promptLogging();
1983
- const pluginSlugs = await promptDatabases(plugins);
1984
- for (const slug of pluginSlugs) {
1985
- const preset = getPreset("plugin", slug);
1986
- if (preset) selectedPresets.push(preset);
1987
- }
1988
- const subscriberSlugs = await promptSubscribers(subscribers);
1989
- for (const slug of subscriberSlugs) {
1990
- const preset = getPreset("subscriber", slug);
1991
- if (preset) selectedPresets.push(preset);
1992
- }
1993
- const autoChoice = await promptAutoInstrumentation();
1994
- if (autoChoice === "none") {
1995
- autoInstrumentations = "none";
1996
- } else if (autoChoice === "specific") {
1997
- autoInstrumentations = "all";
1998
- }
1999
- if (runtime === "node") {
2000
- startupStyle = await promptStartupStyle(project.hasTypeScript);
2634
+ const content = Buffer.concat(chunks).toString("utf8");
2635
+ try {
2636
+ return parsePlan(JSON.parse(content));
2637
+ } catch (error2) {
2638
+ if (error2 instanceof AutotelError) throw error2;
2639
+ throw new AutotelError({
2640
+ type: "validation",
2641
+ code: AutotelErrorCodes.E_INVALID_INPUT,
2642
+ message: `stdin did not contain valid JSON: ${error2.message}`
2643
+ });
2644
+ }
2645
+ }
2646
+ return readPlanFromFile(input2);
2647
+ }
2648
+ function planFromQuickPreset(slug, project) {
2649
+ if (project === null) {
2650
+ throw new AutotelError({
2651
+ type: "environment",
2652
+ code: AutotelErrorCodes.E_NO_PACKAGE_JSON,
2653
+ message: "project required"
2654
+ });
2655
+ }
2656
+ const quick = getQuickPreset(slug);
2657
+ if (quick === void 0) {
2658
+ throw new AutotelError({
2659
+ type: "validation",
2660
+ code: AutotelErrorCodes.E_UNKNOWN_PRESET,
2661
+ message: `Unknown preset: ${slug}`,
2662
+ fix: "Run `autotel commands --json` to see available presets"
2663
+ });
2664
+ }
2665
+ const presets = [quick.backend];
2666
+ if (quick.subscribers) presets.push(...quick.subscribers);
2667
+ if (quick.plugins) presets.push(...quick.plugins);
2668
+ const { plan } = buildPlanFromDetection({
2669
+ project,
2670
+ detection: {
2671
+ packages: [],
2672
+ presets,
2673
+ primaryLogger: quick.logging === "pino" ? "pino" : null,
2674
+ autoInstrumentLoggers: [],
2675
+ autoInstrumentedDeps: [],
2676
+ backend: { slug: quick.backend, source: "default" },
2677
+ platform: null
2001
2678
  }
2679
+ });
2680
+ return plan;
2681
+ }
2682
+ async function planFromDetection(project, options) {
2683
+ if (project === null) {
2684
+ throw new AutotelError({
2685
+ type: "environment",
2686
+ code: AutotelErrorCodes.E_NO_PACKAGE_JSON,
2687
+ message: "project required"
2688
+ });
2689
+ }
2690
+ let envConsent = options.scanEnv;
2691
+ if (!envConsent && envFilesRequireConsent(project.packageRoot) && !options.yes && !options.noInteractive && !options.json && !isCI()) {
2692
+ envConsent = await promptConfirm(
2693
+ `Found a .env file. Read its keys to help detect the backend? (values are never read)`,
2694
+ false
2695
+ );
2696
+ }
2697
+ const detection = detectInProject({ project, envConsent });
2698
+ const { plan } = buildPlanFromDetection({ project, detection });
2699
+ return plan;
2700
+ }
2701
+ function applyPlan(args) {
2702
+ const { plan, project, options } = args;
2703
+ const result = {
2704
+ wroteFiles: [],
2705
+ ranInstalls: [],
2706
+ printedInstalls: [],
2707
+ installErrors: []
2708
+ };
2709
+ const presets = [];
2710
+ for (const slug of plan.presets) {
2711
+ const p = resolvePreset(slug);
2712
+ if (p !== null) presets.push(p);
2002
2713
  }
2003
2714
  const codeFile = createCodeFile();
2004
2715
  addImport(codeFile, { source: "autotel/register", sideEffect: true });
2005
2716
  addImport(codeFile, { source: "autotel", specifiers: ["init"] });
2006
- for (const preset of selectedPresets) {
2717
+ if (plan.detected?.primaryLogger === "pino") {
2718
+ setPinoLogger(codeFile);
2719
+ }
2720
+ for (const l of plan.detected?.autoInstrumentLoggers ?? []) {
2721
+ addAutoInstrumentationLogger(codeFile, l);
2722
+ }
2723
+ for (const preset of presets) {
2007
2724
  for (const imp of preset.imports) {
2008
2725
  const section = preset.type === "backend" || preset.type === "platform" ? "backend" : preset.type === "plugin" ? "plugin" : preset.type === "subscriber" ? "subscriber" : void 0;
2009
2726
  addImport(codeFile, imp, section);
@@ -2016,130 +2733,143 @@ async function runInit(options) {
2016
2733
  addPluginInit(codeFile, preset.configBlock.code);
2017
2734
  }
2018
2735
  }
2019
- if (!codeFile.backendConfig) {
2736
+ if (codeFile.backendConfig === null) {
2020
2737
  setBackendConfig(codeFile, "// Local/console mode - no backend configured");
2021
2738
  }
2022
- const instrumentationContent = renderCodeFile(codeFile);
2023
- const depPlan = buildDependencyPlan({
2024
- presets: selectedPresets,
2025
- autoInstrumentations
2026
- });
2739
+ const newContent = renderCodeFile(codeFile);
2740
+ const instrumentationPath = resolveInstrumentationPath(project);
2741
+ const envExamplePath = path9.join(project.packageRoot, ".env.example");
2742
+ const existing = readFileSafe(instrumentationPath);
2743
+ if (existing !== null) {
2744
+ const parsed = parseInstrumentation(existing);
2745
+ if (!parsed.cliOwned && !options.force) {
2746
+ throw new AutotelError({
2747
+ type: "conflict",
2748
+ code: AutotelErrorCodes.E_EXISTING_CONFIG,
2749
+ message: `Hand-edited instrumentation file at ${instrumentationPath} (no CLI markers found)`,
2750
+ fix: "Pass --force to overwrite (creates a .bak backup) or remove the file",
2751
+ expected: { path: instrumentationPath }
2752
+ });
2753
+ }
2754
+ const addedImports = diffImportSources(
2755
+ parsed,
2756
+ [
2757
+ ...codeFile.imports,
2758
+ ...codeFile.backendImports,
2759
+ ...codeFile.pluginImports,
2760
+ ...codeFile.subscriberImports,
2761
+ ...codeFile.loggerImports
2762
+ ].map((i) => i.source)
2763
+ );
2764
+ const addedAuto = diffAutoInstrumentations(
2765
+ parsed,
2766
+ codeFile.autoInstrumentations
2767
+ );
2768
+ const contentChanged = existing !== newContent;
2769
+ if (addedImports.length === 0 && addedAuto.length === 0 && parsed.cliOwned && !contentChanged) {
2770
+ result.wroteFiles.push(
2771
+ `${path9.relative(project.cwd, instrumentationPath)} (no changes)`
2772
+ );
2773
+ } else {
2774
+ atomicWrite(instrumentationPath, newContent, {
2775
+ root: project.packageRoot,
2776
+ backup: true
2777
+ });
2778
+ result.wroteFiles.push(path9.relative(project.cwd, instrumentationPath));
2779
+ }
2780
+ } else {
2781
+ atomicWrite(instrumentationPath, newContent, {
2782
+ root: project.packageRoot
2783
+ });
2784
+ result.wroteFiles.push(path9.relative(project.cwd, instrumentationPath));
2785
+ }
2027
2786
  const envVars = [];
2028
- for (const preset of selectedPresets) {
2029
- envVars.push(...preset.env.required, ...preset.env.optional);
2787
+ for (const p of presets) {
2788
+ envVars.push(...p.env.required, ...p.env.optional);
2030
2789
  }
2031
2790
  const envExampleContent = generateEnvExample(envVars);
2032
- const instrumentationPath = getInstrumentationPath(project.packageRoot, project.hasTypeScript);
2033
- const envExamplePath = path6.join(project.packageRoot, ".env.example");
2034
- if (options.dryRun) {
2035
- heading("\nDry run - no files will be written\n");
2036
- info(`Would write: ${path6.relative(project.cwd, instrumentationPath)}`);
2037
- console.log("---");
2038
- console.log(instrumentationContent);
2039
- console.log("---\n");
2040
- if (envExampleContent) {
2041
- info(`Would write: ${path6.relative(project.cwd, envExamplePath)}`);
2042
- console.log("---");
2043
- console.log(envExampleContent);
2044
- console.log("---\n");
2045
- }
2046
- const prodPkgs2 = getProdPackages(depPlan);
2047
- const devPkgs2 = getDevPackages(depPlan);
2048
- if (prodPkgs2.length > 0) {
2049
- const cmd = getInstallCommand(project.packageManager, prodPkgs2);
2050
- info(`Would run: ${cmd}`);
2051
- }
2052
- if (devPkgs2.length > 0) {
2053
- const cmd = getInstallCommand(project.packageManager, devPkgs2, { dev: true });
2054
- info(`Would run: ${cmd}`);
2055
- }
2056
- process.exit(0);
2057
- }
2058
- spinner.start("Writing instrumentation file...");
2059
- const { backupPath: instrBackup } = atomicWrite(instrumentationPath, instrumentationContent, {
2060
- root: project.packageRoot,
2061
- backup: options.force
2062
- });
2063
- if (instrBackup) {
2064
- verbose(`Backup created: ${instrBackup}`);
2065
- }
2066
- spinner.succeed(`Wrote ${path6.relative(project.cwd, instrumentationPath)}`);
2067
- if (envExampleContent && !fileExists(envExamplePath)) {
2068
- spinner.start("Writing .env.example...");
2069
- atomicWrite(envExamplePath, envExampleContent, { root: project.packageRoot });
2070
- spinner.succeed(`Wrote ${path6.relative(project.cwd, envExamplePath)}`);
2071
- }
2072
- const prodPkgs = getProdPackages(depPlan);
2073
- const devPkgs = getDevPackages(depPlan);
2074
- if (!options.noInstall && (prodPkgs.length > 0 || devPkgs.length > 0)) {
2075
- if (prodPkgs.length > 0) {
2076
- const cmd = getInstallCommand(project.packageManager, prodPkgs);
2077
- if (options.printInstallCmd) {
2078
- info(`Install command: ${cmd}`);
2079
- } else {
2080
- spinner.start("Installing dependencies...");
2791
+ if (envExampleContent.length > 0 && !fileExists(envExamplePath)) {
2792
+ atomicWrite(envExamplePath, envExampleContent, {
2793
+ root: project.packageRoot
2794
+ });
2795
+ result.wroteFiles.push(path9.relative(project.cwd, envExamplePath));
2796
+ }
2797
+ const prod = plan.packagesToInstall.prod;
2798
+ const dev = plan.packagesToInstall.dev;
2799
+ if (prod.length > 0 || dev.length > 0) {
2800
+ if (options.noInstall || options.printInstallCmd) {
2801
+ if (prod.length > 0) {
2802
+ result.printedInstalls.push(
2803
+ getInstallCommand(project.packageManager, prod)
2804
+ );
2805
+ }
2806
+ if (dev.length > 0) {
2807
+ result.printedInstalls.push(
2808
+ getInstallCommand(project.packageManager, dev, { dev: true })
2809
+ );
2810
+ }
2811
+ } else {
2812
+ if (prod.length > 0) {
2813
+ const cmd = getInstallCommand(project.packageManager, prod);
2081
2814
  try {
2082
2815
  execSync(cmd, { cwd: project.packageRoot, stdio: "pipe" });
2083
- spinner.succeed("Dependencies installed");
2816
+ result.ranInstalls.push(cmd);
2084
2817
  } catch {
2085
- spinner.fail("Failed to install dependencies");
2086
- error(`Run manually: ${cmd}`);
2818
+ result.installErrors.push(cmd);
2087
2819
  }
2088
2820
  }
2089
- }
2090
- if (devPkgs.length > 0) {
2091
- const cmd = getInstallCommand(project.packageManager, devPkgs, { dev: true });
2092
- if (options.printInstallCmd) {
2093
- info(`Install command (dev): ${cmd}`);
2094
- } else {
2095
- spinner.start("Installing dev dependencies...");
2821
+ if (dev.length > 0) {
2822
+ const cmd = getInstallCommand(project.packageManager, dev, {
2823
+ dev: true
2824
+ });
2096
2825
  try {
2097
2826
  execSync(cmd, { cwd: project.packageRoot, stdio: "pipe" });
2098
- spinner.succeed("Dev dependencies installed");
2827
+ result.ranInstalls.push(cmd);
2099
2828
  } catch {
2100
- spinner.fail("Failed to install dev dependencies");
2101
- error(`Run manually: ${cmd}`);
2829
+ result.installErrors.push(cmd);
2102
2830
  }
2103
2831
  }
2104
2832
  }
2105
- } else if (options.noInstall && (prodPkgs.length > 0 || devPkgs.length > 0)) {
2106
- info("Skipping installation (--no-install)");
2107
- if (prodPkgs.length > 0) {
2108
- const cmd = getInstallCommand(project.packageManager, prodPkgs);
2109
- dim(`Run: ${cmd}`);
2110
- }
2111
- if (devPkgs.length > 0) {
2112
- const cmd = getInstallCommand(project.packageManager, devPkgs, { dev: true });
2113
- dim(`Run: ${cmd}`);
2114
- }
2115
2833
  }
2116
- const relInstrPath = path6.relative(project.packageRoot, instrumentationPath);
2117
- let nextStepCmd;
2118
- switch (startupStyle) {
2119
- case "tsx":
2120
- nextStepCmd = `tsx --import ./${relInstrPath} src/index.ts`;
2121
- break;
2122
- case "node-esm":
2123
- default:
2124
- nextStepCmd = `node --import ./${relInstrPath} dist/index.js`;
2834
+ return result;
2835
+ }
2836
+ function resolveInstrumentationPath(project) {
2837
+ const srcDir = path9.join(project.packageRoot, "src");
2838
+ const hasSrcDir = fs4.existsSync(srcDir) || fileExists(path9.join(project.packageRoot, "src", "index.ts")) || fileExists(path9.join(project.packageRoot, "src", "index.js"));
2839
+ const dir = hasSrcDir ? srcDir : project.packageRoot;
2840
+ const ext = project.hasTypeScript ? "mts" : "mjs";
2841
+ return path9.join(dir, `instrumentation.${ext}`);
2842
+ }
2843
+ function resolvePreset(slug) {
2844
+ for (const type of ["backend", "subscriber", "plugin", "platform"]) {
2845
+ const p = getPreset(type, slug);
2846
+ if (p !== void 0) return p;
2125
2847
  }
2126
- const pmInfo = project.workspace.isMonorepo ? `${project.packageManager} workspace, package root ${project.packageRoot}` : `${project.packageManager}`;
2127
- const writtenFiles = [path6.relative(project.cwd, instrumentationPath)];
2128
- if (envExampleContent && !fileExists(envExamplePath)) {
2129
- writtenFiles.push(".env.example");
2848
+ return null;
2849
+ }
2850
+ function printApplySummary(args) {
2851
+ const { applied, plan, project } = args;
2852
+ if (applied.wroteFiles.length > 0) {
2853
+ success(`Wrote: ${applied.wroteFiles.join(", ")}`);
2130
2854
  }
2131
- console.log(formatFooter({
2132
- detected: pmInfo,
2133
- wrote: writtenFiles,
2134
- next: nextStepCmd
2135
- }));
2136
- const allNextSteps = selectedPresets.flatMap((p) => p.nextSteps);
2137
- if (allNextSteps.length > 0) {
2855
+ for (const cmd of applied.ranInstalls) {
2856
+ info(`Installed: ${cmd}`);
2857
+ }
2858
+ for (const cmd of applied.printedInstalls) {
2859
+ dim(`Run: ${cmd}`);
2860
+ }
2861
+ for (const cmd of applied.installErrors) {
2862
+ error(`Install failed \u2014 run manually: ${cmd}`);
2863
+ }
2864
+ if (plan.nextSteps.length > 0) {
2138
2865
  console.log("\nNext steps:");
2139
- for (const step of allNextSteps) {
2866
+ for (const step of plan.nextSteps) {
2140
2867
  console.log(` - ${step}`);
2141
2868
  }
2142
2869
  }
2870
+ const pmInfo = project.workspace.isMonorepo ? `${project.packageManager} workspace, package root ${project.packageRoot}` : project.packageManager;
2871
+ console.log(`
2872
+ ${formatPackageManagerInfo ? formatPackageManagerInfo(project.packageManager, project.lockfilePath) : pmInfo}`);
2143
2873
  }
2144
2874
 
2145
2875
  // src/lib/dependency-auditor.ts
@@ -2181,7 +2911,7 @@ function getAutotelInfo(packageJson) {
2181
2911
  }
2182
2912
 
2183
2913
  // src/lib/esm-checker.ts
2184
- import * as path7 from "path";
2914
+ import * as path10 from "path";
2185
2915
  function checkRegisterImportOrder(content) {
2186
2916
  const lines = content.split("\n");
2187
2917
  let registerLine = null;
@@ -2238,7 +2968,7 @@ function checkEsmHook(project) {
2238
2968
  if (result.found && !result.isFirst) {
2239
2969
  return {
2240
2970
  status: "warn",
2241
- message: `autotel/register import found but not first in ${path7.basename(entrypoint)}:${result.lineNumber}`,
2971
+ message: `autotel/register import found but not first in ${path10.basename(entrypoint)}:${result.lineNumber}`,
2242
2972
  details: [
2243
2973
  "autotel/register must be the first import for instrumentation to work",
2244
2974
  "Move it to the top of the file, before any other imports"
@@ -2248,7 +2978,7 @@ function checkEsmHook(project) {
2248
2978
  if (result.found && result.isFirst) {
2249
2979
  return {
2250
2980
  status: "ok",
2251
- message: `autotel/register correctly imported first in ${path7.basename(entrypoint)}`
2981
+ message: `autotel/register correctly imported first in ${path10.basename(entrypoint)}`
2252
2982
  };
2253
2983
  }
2254
2984
  }
@@ -2263,8 +2993,8 @@ function checkEsmHook(project) {
2263
2993
  }
2264
2994
 
2265
2995
  // src/lib/logger-checker.ts
2266
- import * as path8 from "path";
2267
- import * as fs2 from "fs";
2996
+ import * as path11 from "path";
2997
+ import * as fs5 from "fs";
2268
2998
  var LOGGER_INSTRUMENTATION = {
2269
2999
  winston: "@opentelemetry/instrumentation-winston",
2270
3000
  bunyan: "@opentelemetry/instrumentation-bunyan",
@@ -2294,17 +3024,17 @@ function extractAutoInstrumentations(content) {
2294
3024
  }
2295
3025
  function findSourceFiles(packageRoot) {
2296
3026
  const sourceFiles = [];
2297
- const srcDir = path8.join(packageRoot, "src");
3027
+ const srcDir = path11.join(packageRoot, "src");
2298
3028
  const dirsToCheck = [packageRoot, srcDir].filter(
2299
- (dir) => fs2.existsSync(dir) && fs2.statSync(dir).isDirectory()
3029
+ (dir) => fs5.existsSync(dir) && fs5.statSync(dir).isDirectory()
2300
3030
  );
2301
3031
  for (const dir of dirsToCheck) {
2302
- const files = fs2.readdirSync(dir, { recursive: true });
3032
+ const files = fs5.readdirSync(dir, { recursive: true });
2303
3033
  for (const file of files) {
2304
3034
  if (typeof file !== "string") continue;
2305
- const filePath = path8.join(dir, file);
3035
+ const filePath = path11.join(dir, file);
2306
3036
  try {
2307
- if (fs2.statSync(filePath).isFile() && /\.(ts|js|mts|mjs|tsx|jsx)$/.test(file)) {
3037
+ if (fs5.statSync(filePath).isFile() && /\.(ts|js|mts|mjs|tsx|jsx)$/.test(file)) {
2308
3038
  sourceFiles.push(filePath);
2309
3039
  }
2310
3040
  } catch {
@@ -3028,11 +3758,11 @@ ${presetType} presets:
3028
3758
  }
3029
3759
 
3030
3760
  // src/commands/codemod-trace.ts
3031
- import * as path10 from "path";
3761
+ import * as path13 from "path";
3032
3762
  import { glob as glob2 } from "glob";
3033
3763
 
3034
3764
  // src/lib/codemod-trace.ts
3035
- import * as path9 from "path";
3765
+ import * as path12 from "path";
3036
3766
  import { Project, SyntaxKind } from "ts-morph";
3037
3767
  var TRACE_IMPORT_MODULE = "autotel";
3038
3768
  function hasTraceImport(sourceFile) {
@@ -3055,8 +3785,8 @@ function addTraceImport(sourceFile) {
3055
3785
  });
3056
3786
  }
3057
3787
  function expandNamePattern(pattern, name, filePath, cwd) {
3058
- const file = path9.basename(filePath, path9.extname(filePath));
3059
- const relPath = path9.relative(cwd, filePath).replaceAll("\\", "/");
3788
+ const file = path12.basename(filePath, path12.extname(filePath));
3789
+ const relPath = path12.relative(cwd, filePath).replaceAll("\\", "/");
3060
3790
  return pattern.replaceAll("{name}", name).replaceAll("{file}", file).replaceAll("{path}", relPath);
3061
3791
  }
3062
3792
  function getSpanName(name, filePath, options) {
@@ -3292,28 +4022,28 @@ var GLOB_META = /[*?[\]]/;
3292
4022
  async function resolveCodemodFiles(pathArg, cwd) {
3293
4023
  const isGlob = GLOB_META.test(pathArg);
3294
4024
  if (!isGlob) {
3295
- const absolute = path10.isAbsolute(pathArg) ? pathArg : path10.resolve(cwd, pathArg);
4025
+ const absolute = path13.isAbsolute(pathArg) ? pathArg : path13.resolve(cwd, pathArg);
3296
4026
  if (fileExists(absolute)) {
3297
- const ext = path10.extname(absolute);
4027
+ const ext = path13.extname(absolute);
3298
4028
  if (CODEMOD_EXTENSIONS.has(ext) && !absolute.endsWith(".d.ts")) {
3299
4029
  return [absolute];
3300
4030
  }
3301
4031
  return [];
3302
4032
  }
3303
4033
  }
3304
- const pattern = path10.isAbsolute(pathArg) ? pathArg : path10.join(cwd, pathArg);
4034
+ const pattern = path13.isAbsolute(pathArg) ? pathArg : path13.join(cwd, pathArg);
3305
4035
  const matches = await glob2(pattern, {
3306
4036
  cwd,
3307
4037
  absolute: true,
3308
4038
  ignore: ["**/node_modules/**", "**/*.d.ts"]
3309
4039
  });
3310
4040
  return matches.filter((f) => {
3311
- const ext = path10.extname(f);
4041
+ const ext = path13.extname(f);
3312
4042
  return CODEMOD_EXTENSIONS.has(ext) && !f.endsWith(".d.ts");
3313
4043
  });
3314
4044
  }
3315
4045
  async function runCodemodTrace(options) {
3316
- const { path: pathArg, cwd, dryRun, namePattern, skip, printFiles, verbose: verbose2, quiet } = options;
4046
+ const { path: pathArg, cwd, dryRun, namePattern, skip, printFiles, verbose, quiet } = options;
3317
4047
  const files = await resolveCodemodFiles(pathArg, cwd);
3318
4048
  if (files.length === 0) {
3319
4049
  if (!quiet) {
@@ -3329,7 +4059,7 @@ async function runCodemodTrace(options) {
3329
4059
  for (const filePath of files) {
3330
4060
  const content = readFileSafe(filePath);
3331
4061
  if (content === null) {
3332
- if (verbose2) dim(`Skip ${filePath} (unreadable)`);
4062
+ if (verbose) dim(`Skip ${filePath} (unreadable)`);
3333
4063
  continue;
3334
4064
  }
3335
4065
  let result;
@@ -3339,17 +4069,17 @@ async function runCodemodTrace(options) {
3339
4069
  if (!quiet) {
3340
4070
  error(`Failed to transform ${filePath}: ${error2 instanceof Error ? error2.message : String(error2)}`);
3341
4071
  }
3342
- if (verbose2 && error2 instanceof Error && error2.stack) dim(error2.stack);
4072
+ if (verbose && error2 instanceof Error && error2.stack) dim(error2.stack);
3343
4073
  process.exitCode = 1;
3344
4074
  continue;
3345
4075
  }
3346
- const relativePath = path10.relative(cwd, filePath);
4076
+ const relativePath = path13.relative(cwd, filePath);
3347
4077
  if (result.changed) {
3348
4078
  totalWrapped += result.wrappedCount;
3349
4079
  totalChanged += 1;
3350
4080
  if (!dryRun) {
3351
- const fs3 = await import("fs");
3352
- fs3.writeFileSync(filePath, result.modified, "utf8");
4081
+ const fs9 = await import("fs");
4082
+ fs9.writeFileSync(filePath, result.modified, "utf8");
3353
4083
  }
3354
4084
  }
3355
4085
  const showSummary = printFiles || dryRun || result.changed;
@@ -3368,25 +4098,1718 @@ async function runCodemodTrace(options) {
3368
4098
  }
3369
4099
  }
3370
4100
 
4101
+ // src/commands/schema.ts
4102
+ import * as fs6 from "fs";
4103
+ import * as path14 from "path";
4104
+ import { fileURLToPath } from "url";
4105
+
4106
+ // src/lib/manifest.ts
4107
+ var GLOBAL_FLAGS = [
4108
+ {
4109
+ name: "--cwd",
4110
+ takesValue: true,
4111
+ description: "Target directory (default: current working directory)"
4112
+ },
4113
+ { name: "--verbose", description: "Show detailed output" },
4114
+ { name: "--quiet", description: "Only show warnings and errors" }
4115
+ ];
4116
+ var AGENT_FLAGS = [
4117
+ {
4118
+ name: "--json",
4119
+ description: "Emit machine-readable JSON instead of human output"
4120
+ },
4121
+ {
4122
+ name: "--output-file",
4123
+ takesValue: true,
4124
+ description: "Persist the first JSON payload to this path"
4125
+ },
4126
+ {
4127
+ name: "--no-secrets-in-output",
4128
+ description: "Redact secret-shaped values (also via AUTOTEL_NO_SECRETS=1 / AGENT_SANDBOX=1)"
4129
+ },
4130
+ {
4131
+ name: "--no-interactive",
4132
+ description: "Never prompt; fail fast if input is required"
4133
+ }
4134
+ ];
4135
+ var COMMANDS = [
4136
+ {
4137
+ name: "init",
4138
+ description: "Initialise autotel in your project",
4139
+ flags: [
4140
+ ...GLOBAL_FLAGS,
4141
+ ...AGENT_FLAGS,
4142
+ { name: "--dry-run", description: "Print what would be done; write nothing" },
4143
+ { name: "--no-install", description: "Generate files only, skip package installation" },
4144
+ { name: "--print-install-cmd", description: "Output install command without running it" },
4145
+ { name: "--yes", alias: "-y", description: "Auto-apply detected items; no prompts" },
4146
+ { name: "--preset", takesValue: true, description: "Use a named quick preset" },
4147
+ { name: "--force", description: "Overwrite existing config (creates backup)" },
4148
+ { name: "--workspace-root", description: "Install at workspace root, not package root" },
4149
+ { name: "--no-detect", description: "Skip auto-detection of installed deps" },
4150
+ { name: "--detect-only", description: "Run detection and print the proposal; write nothing" },
4151
+ { name: "--plan", takesValue: true, description: "Read a pre-built InitPlan JSON from this path and apply it" },
4152
+ { name: "--input", takesValue: true, description: "Read InitPlan JSON from stdin (-) or a file" },
4153
+ { name: "--scan-env", description: "Consent to reading uncommitted .env files for backend detection" }
4154
+ ],
4155
+ mutating: true,
4156
+ network: true,
4157
+ writesFiles: true,
4158
+ supportsDryRun: true,
4159
+ requiresPackageJson: true,
4160
+ mayReadEnv: true,
4161
+ supportsJson: true,
4162
+ examples: [
4163
+ { description: "Interactive setup with detection", command: "autotel init" },
4164
+ { description: "Non-interactive, apply all detected items", command: "autotel init --yes" },
4165
+ { description: "Preview as JSON without writing", command: "autotel init --json --dry-run" },
4166
+ { description: "Detection only", command: "autotel init --detect-only --json" }
4167
+ ]
4168
+ },
4169
+ {
4170
+ name: "doctor",
4171
+ description: "Run diagnostics on your autotel setup",
4172
+ flags: [
4173
+ ...GLOBAL_FLAGS,
4174
+ ...AGENT_FLAGS,
4175
+ { name: "--fix", description: "Auto-fix resolvable issues" },
4176
+ { name: "--list-checks", description: "List all available checks" },
4177
+ { name: "--env-file", takesValue: true, description: "Path to env file to check" }
4178
+ ],
4179
+ mutating: false,
4180
+ network: false,
4181
+ writesFiles: false,
4182
+ supportsDryRun: false,
4183
+ requiresPackageJson: true,
4184
+ mayReadEnv: true,
4185
+ supportsJson: true
4186
+ },
4187
+ {
4188
+ name: "add",
4189
+ description: "Add a backend, subscriber, plugin, or platform incrementally",
4190
+ args: [
4191
+ { name: "type", required: false, description: "Preset type" },
4192
+ { name: "name", required: false, description: "Preset name" }
4193
+ ],
4194
+ flags: [
4195
+ ...GLOBAL_FLAGS,
4196
+ ...AGENT_FLAGS,
4197
+ { name: "--list", description: "List available presets for the given type" },
4198
+ { name: "--dry-run", description: "Print what would be done" },
4199
+ { name: "--no-install", description: "Generate files only" },
4200
+ { name: "--print-install-cmd", description: "Output install command without running" },
4201
+ { name: "--yes", alias: "-y", description: "Accept defaults" },
4202
+ { name: "--force", description: "Overwrite non-CLI-owned config" },
4203
+ { name: "--workspace-root", description: "Install at workspace root" }
4204
+ ],
4205
+ mutating: true,
4206
+ network: true,
4207
+ writesFiles: true,
4208
+ supportsDryRun: true,
4209
+ requiresPackageJson: true,
4210
+ mayReadEnv: false,
4211
+ supportsJson: true
4212
+ },
4213
+ {
4214
+ name: "codemod trace",
4215
+ description: "Wrap functions in trace() with span name from function/variable/method name",
4216
+ args: [{ name: "path", required: true, description: "File path or glob" }],
4217
+ flags: [
4218
+ ...GLOBAL_FLAGS,
4219
+ { name: "--dry-run", description: "Print changes without writing files" },
4220
+ { name: "--name-pattern", takesValue: true, description: "Span name template" },
4221
+ { name: "--skip", takesValue: true, description: "Skip functions whose name matches (repeatable)" },
4222
+ { name: "--print-files", description: "Print per-file summary" }
4223
+ ],
4224
+ mutating: true,
4225
+ network: false,
4226
+ writesFiles: true,
4227
+ supportsDryRun: true,
4228
+ requiresPackageJson: false,
4229
+ mayReadEnv: false,
4230
+ supportsJson: false
4231
+ },
4232
+ {
4233
+ name: "schema",
4234
+ description: "Print the CLI manifest as JSON (agent discovery)",
4235
+ flags: [
4236
+ { name: "--json", description: "Always JSON (this command is JSON-only)" }
4237
+ ],
4238
+ mutating: false,
4239
+ network: false,
4240
+ writesFiles: false,
4241
+ supportsDryRun: false,
4242
+ requiresPackageJson: false,
4243
+ mayReadEnv: false,
4244
+ supportsJson: true
4245
+ },
4246
+ {
4247
+ name: "schema errors",
4248
+ description: "Print error envelope shape + AUTOTEL_E_* enum",
4249
+ flags: [{ name: "--json", description: "Always JSON" }],
4250
+ mutating: false,
4251
+ network: false,
4252
+ writesFiles: false,
4253
+ supportsDryRun: false,
4254
+ requiresPackageJson: false,
4255
+ mayReadEnv: false,
4256
+ supportsJson: true
4257
+ },
4258
+ {
4259
+ name: "schema outputs",
4260
+ description: "Print JSON output shapes per command",
4261
+ flags: [{ name: "--json", description: "Always JSON" }],
4262
+ mutating: false,
4263
+ network: false,
4264
+ writesFiles: false,
4265
+ supportsDryRun: false,
4266
+ requiresPackageJson: false,
4267
+ mayReadEnv: false,
4268
+ supportsJson: true
4269
+ },
4270
+ {
4271
+ name: "commands",
4272
+ description: "Print compact tool-style command listing",
4273
+ flags: [{ name: "--json", description: "Always JSON" }],
4274
+ mutating: false,
4275
+ network: false,
4276
+ writesFiles: false,
4277
+ supportsDryRun: false,
4278
+ requiresPackageJson: false,
4279
+ mayReadEnv: false,
4280
+ supportsJson: true
4281
+ },
4282
+ {
4283
+ name: "examples",
4284
+ description: "Print copy-pasteable examples for a command",
4285
+ args: [
4286
+ { name: "command", required: false, description: "Command name (omit for all)" }
4287
+ ],
4288
+ flags: [{ name: "--json", description: "Emit JSON" }],
4289
+ mutating: false,
4290
+ network: false,
4291
+ writesFiles: false,
4292
+ supportsDryRun: false,
4293
+ requiresPackageJson: false,
4294
+ mayReadEnv: false,
4295
+ supportsJson: true
4296
+ },
4297
+ {
4298
+ name: "version",
4299
+ description: "Print version info",
4300
+ flags: [{ name: "--json", description: "Emit JSON" }],
4301
+ mutating: false,
4302
+ network: false,
4303
+ writesFiles: false,
4304
+ supportsDryRun: false,
4305
+ requiresPackageJson: false,
4306
+ mayReadEnv: false,
4307
+ supportsJson: true
4308
+ }
4309
+ ];
4310
+ var INVESTIGATE_FLAGS = [
4311
+ { name: "--backend", takesValue: true, description: "Backend kind (env: AUTOTEL_BACKEND)" },
4312
+ { name: "--jaeger-base-url", takesValue: true, description: "Jaeger base URL" },
4313
+ { name: "--tempo-base-url", takesValue: true, description: "Tempo base URL" },
4314
+ { name: "--prometheus-base-url", takesValue: true, description: "Prometheus base URL" },
4315
+ { name: "--loki-base-url", takesValue: true, description: "Loki base URL" },
4316
+ { name: "--collector-port", takesValue: true, description: "OTLP receiver port" },
4317
+ { name: "--fixture-path", takesValue: true, description: "Fixture JSON path" },
4318
+ { name: "--output-file", takesValue: true, description: "Persist JSON output to this path" },
4319
+ { name: "--no-secrets-in-output", description: "Redact secret-shaped values" }
4320
+ ];
4321
+ var STATIC_FLAGS = [
4322
+ { name: "--output-file", takesValue: true, description: "Persist JSON output to this path" },
4323
+ { name: "--no-secrets-in-output", description: "Redact secret-shaped values" }
4324
+ ];
4325
+ function investigateCmd(name, description, extras = {}) {
4326
+ return {
4327
+ name,
4328
+ description,
4329
+ ...extras.args ? { args: extras.args } : {},
4330
+ flags: [...extras.static ? STATIC_FLAGS : INVESTIGATE_FLAGS, ...extras.flags ?? []],
4331
+ mutating: false,
4332
+ network: extras.network ?? !extras.static,
4333
+ writesFiles: false,
4334
+ supportsDryRun: false,
4335
+ requiresPackageJson: false,
4336
+ mayReadEnv: false,
4337
+ supportsJson: true
4338
+ };
4339
+ }
4340
+ var traceIdArg = [
4341
+ { name: "traceId", required: true, description: "Trace ID" }
4342
+ ];
4343
+ var serviceNameArg = [
4344
+ { name: "serviceName", required: true, description: "Service name" }
4345
+ ];
4346
+ var INVESTIGATE_COMMANDS = [
4347
+ investigateCmd("health", "Backend health + signal coverage"),
4348
+ investigateCmd("capabilities", "Which signals the active backend serves"),
4349
+ investigateCmd("discover", "Discover services and field shapes (parent)"),
4350
+ investigateCmd("discover services", "Services with cross-signal metadata"),
4351
+ investigateCmd("discover trace-fields", "Trace/span field names from sampled traces", {
4352
+ flags: [{ name: "--search", takesValue: true, description: "Substring filter" }]
4353
+ }),
4354
+ investigateCmd("discover log-fields", "Log field names from sampled logs", {
4355
+ flags: [{ name: "--search", takesValue: true, description: "Substring filter" }]
4356
+ }),
4357
+ investigateCmd("query", "Query traces/spans/metrics/logs (parent)"),
4358
+ investigateCmd("query traces", "Search traces by service/op/status/tags/time/error"),
4359
+ investigateCmd("query spans", "Search individual spans (extra duration filters)"),
4360
+ investigateCmd("query metrics", "List metric series"),
4361
+ investigateCmd("query logs", "Search logs"),
4362
+ investigateCmd("trace", "Trace lookup commands (parent)"),
4363
+ investigateCmd("trace get", "Get a trace by ID", { args: traceIdArg }),
4364
+ investigateCmd("trace summary", "Compact incident-friendly trace summary", { args: traceIdArg }),
4365
+ investigateCmd("topology", "Service topology commands (parent)"),
4366
+ investigateCmd("topology services", "List known services"),
4367
+ investigateCmd("topology operations", "List operations for a service", { args: serviceNameArg }),
4368
+ investigateCmd("topology map", "Service dependency map with node/edge health"),
4369
+ investigateCmd("diagnose", "Anomaly / root-cause / errors / SLO diagnosis (parent)"),
4370
+ investigateCmd("diagnose anomalies", "Latency / error-rate outliers"),
4371
+ investigateCmd("diagnose root-cause", "Bottleneck span in a trace", { args: traceIdArg }),
4372
+ investigateCmd("diagnose errors", "Error spans grouped by service/operation"),
4373
+ investigateCmd("diagnose slos", "SLO violations for a service"),
4374
+ investigateCmd("correlate", "Cross-signal correlation (parent)"),
4375
+ investigateCmd("correlate trace", "Trace + metrics + logs for a trace ID", { args: traceIdArg }),
4376
+ investigateCmd("correlate explain-slowdown", "Anomalies + root cause + correlated signals"),
4377
+ investigateCmd("llm", "LLM analytics (parent)"),
4378
+ investigateCmd("llm usage", "Token usage + USD by model and service"),
4379
+ investigateCmd("llm models", "Discover LLM models in use"),
4380
+ investigateCmd("llm model-stats", "Per-model latency/token/error stats"),
4381
+ investigateCmd("llm expensive", "Top token-spend traces"),
4382
+ investigateCmd("llm slow", "Slowest LLM traces"),
4383
+ investigateCmd("llm tools", "Tool/function spans grouped by tool name"),
4384
+ investigateCmd("semconv", "Semantic conventions lookup (parent)", { static: true }),
4385
+ investigateCmd("semconv list", "List semconv namespaces", { static: true, network: true }),
4386
+ investigateCmd("semconv get", "Groups for one namespace", {
4387
+ static: true,
4388
+ network: true,
4389
+ args: [{ name: "namespace", required: true, description: "Namespace (e.g. http)" }]
4390
+ }),
4391
+ investigateCmd("semconv refresh", "Clear semconv cache", { static: true }),
4392
+ investigateCmd("score", "Score a span for instrumentation quality (JSON on stdin)", {
4393
+ static: true,
4394
+ flags: [{ name: "--span-file", takesValue: true, description: "Read span JSON from file" }]
4395
+ }),
4396
+ investigateCmd("score explain", "Explain the instrumentation scoring rubric", { static: true }),
4397
+ investigateCmd("collector", "OpenTelemetry Collector config + schema commands (parent)", {
4398
+ static: true
4399
+ }),
4400
+ investigateCmd("collector validate", "Validate OTLP receiver config", {
4401
+ static: true,
4402
+ flags: [{ name: "--config-file", takesValue: true, description: "Read JSON config from file" }]
4403
+ }),
4404
+ investigateCmd("collector suggest", "Minimal OTLP receiver config", { static: true }),
4405
+ investigateCmd("collector explain", "Receiver config shape + defaults", { static: true }),
4406
+ investigateCmd("collector versions", "Supported collector schema versions", { static: true, network: true }),
4407
+ investigateCmd("collector components", "Components for a version", {
4408
+ static: true,
4409
+ network: true,
4410
+ flags: [
4411
+ { name: "--version", takesValue: true, description: "Collector version" },
4412
+ { name: "--kind", takesValue: true, description: "Component kind filter" }
4413
+ ]
4414
+ }),
4415
+ investigateCmd("collector schema", "JSON schema for a component", {
4416
+ static: true,
4417
+ network: true,
4418
+ flags: [
4419
+ { name: "--kind", takesValue: true, description: "Component kind" },
4420
+ { name: "--name", takesValue: true, description: "Component name" },
4421
+ { name: "--version", takesValue: true, description: "Collector version" }
4422
+ ]
4423
+ }),
4424
+ investigateCmd("collector readme", "README for a component", {
4425
+ static: true,
4426
+ network: true,
4427
+ flags: [
4428
+ { name: "--kind", takesValue: true, description: "Component kind" },
4429
+ { name: "--name", takesValue: true, description: "Component name" },
4430
+ { name: "--version", takesValue: true, description: "Collector version" }
4431
+ ]
4432
+ }),
4433
+ investigateCmd("collector validate-component", "Validate component config against upstream schema", {
4434
+ static: true,
4435
+ network: true,
4436
+ flags: [
4437
+ { name: "--kind", takesValue: true, description: "Component kind" },
4438
+ { name: "--name", takesValue: true, description: "Component name" },
4439
+ { name: "--version", takesValue: true, description: "Collector version" },
4440
+ { name: "--config-file", takesValue: true, description: "Read JSON config from file" }
4441
+ ]
4442
+ }),
4443
+ investigateCmd("collector refresh", "Refresh in-memory collector metadata cache", { static: true, network: true })
4444
+ ];
4445
+ COMMANDS.push(...INVESTIGATE_COMMANDS);
4446
+ function getCommand(name) {
4447
+ return COMMANDS.find((c) => c.name === name);
4448
+ }
4449
+ var ERROR_CATALOGUE = [
4450
+ { code: AutotelErrorCodes.E_NO_PACKAGE_JSON, type: "environment", description: "No package.json found in cwd or ancestors" },
4451
+ { code: AutotelErrorCodes.E_UNKNOWN_PRESET, type: "validation", description: "Preset slug not in registry" },
4452
+ { code: AutotelErrorCodes.E_INVALID_PLAN, type: "validation", description: "Plan file failed schema validation" },
4453
+ { code: AutotelErrorCodes.E_INVALID_INPUT, type: "validation", description: "Stdin or --input payload invalid" },
4454
+ { code: AutotelErrorCodes.E_INVALID_FLAG, type: "validation", description: "Conflicting or invalid flag combination" },
4455
+ { code: AutotelErrorCodes.E_NO_WORKSPACE_PACKAGES, type: "environment", description: "Workspace root has no detectable packages" },
4456
+ { code: AutotelErrorCodes.E_ENV_CONSENT_REQUIRED, type: "environment", description: ".env file present; consent required (pass --scan-env or run interactively)" },
4457
+ { code: AutotelErrorCodes.E_EXISTING_CONFIG, type: "conflict", description: "Existing instrumentation config; use --force or run with merge" },
4458
+ { code: AutotelErrorCodes.E_AMBIGUOUS_LOGGER, type: "conflict", description: "Multiple loggers detected and selection could not be inferred" },
4459
+ { code: AutotelErrorCodes.E_INSTALL_FAILED, type: "install", description: "Package manager install command failed" },
4460
+ { code: AutotelErrorCodes.E_WRITE_FAILED, type: "io", description: "Failed to write a file" },
4461
+ { code: AutotelErrorCodes.E_READ_FAILED, type: "io", description: "Failed to read a file" },
4462
+ { code: AutotelErrorCodes.E_UNKNOWN, type: "runtime", description: "Unexpected error" }
4463
+ ];
4464
+
4465
+ // src/commands/schema.ts
4466
+ function configure(opts) {
4467
+ configureJsonOutput({
4468
+ outputFile: opts.outputFile,
4469
+ noSecrets: opts.noSecrets
4470
+ });
4471
+ }
4472
+ function readSelfVersion() {
4473
+ try {
4474
+ const here = path14.dirname(fileURLToPath(import.meta.url));
4475
+ let dir = here;
4476
+ for (let i = 0; i < 5; i++) {
4477
+ const candidate = path14.join(dir, "package.json");
4478
+ if (fs6.existsSync(candidate)) {
4479
+ const pkg = JSON.parse(fs6.readFileSync(candidate, "utf8"));
4480
+ if (pkg.name === "autotel-cli") return String(pkg.version ?? "0.0.0");
4481
+ }
4482
+ dir = path14.dirname(dir);
4483
+ }
4484
+ } catch {
4485
+ }
4486
+ return "0.0.0";
4487
+ }
4488
+ function runSchema(opts) {
4489
+ configure(opts);
4490
+ printJson({
4491
+ ok: true,
4492
+ command: "autotel schema",
4493
+ version: readSelfVersion(),
4494
+ commands: COMMANDS
4495
+ });
4496
+ }
4497
+ function runSchemaErrors(opts) {
4498
+ configure(opts);
4499
+ printJson({
4500
+ ok: true,
4501
+ command: "autotel schema errors",
4502
+ envelope: {
4503
+ ok: false,
4504
+ command: "<command>",
4505
+ error: {
4506
+ type: "<one of: validation, environment, auth, conflict, install, io, runtime>",
4507
+ code: "<AUTOTEL_E_*>",
4508
+ message: "human-readable message",
4509
+ retryable: false,
4510
+ fix: "optional remediation hint",
4511
+ expected: { "<key>": "<value>" },
4512
+ suggestions: ["optional follow-up commands"]
4513
+ }
4514
+ },
4515
+ codes: ERROR_CATALOGUE,
4516
+ exitCodes: {
4517
+ "0": "success",
4518
+ "1": "runtime / unexpected failure",
4519
+ "2": "validation / conflict / refusal"
4520
+ }
4521
+ });
4522
+ }
4523
+ function runSchemaOutputs(opts) {
4524
+ configure(opts);
4525
+ printJson({
4526
+ ok: true,
4527
+ command: "autotel schema outputs",
4528
+ outputs: {
4529
+ "autotel init --json": {
4530
+ ok: "boolean",
4531
+ command: "string",
4532
+ detected: {
4533
+ packages: 'array of { name, version, resolution: "target" | "workspace-root" }',
4534
+ logger: '"pino" | "winston" | "bunyan" | null',
4535
+ backend: '{ source: "env" | "wrangler" | "deps" | "prompt" | "default", value: string }',
4536
+ platform: '"cloudflare" | "aws-lambda" | "edge" | null'
4537
+ },
4538
+ plan: {
4539
+ presets: "string[] (slugs)",
4540
+ packagesToInstall: { prod: "string[]", dev: "string[]" },
4541
+ filesToWrite: 'array of { path, action: "create" | "merge" | "skip" }',
4542
+ envVars: "array of { name, sensitive, action }"
4543
+ },
4544
+ nextSteps: "string[]"
4545
+ }
4546
+ }
4547
+ });
4548
+ }
4549
+ function runCommandsListing(opts) {
4550
+ configure(opts);
4551
+ const compact = COMMANDS.map((c) => ({
4552
+ name: c.name,
4553
+ description: c.description,
4554
+ mutating: c.mutating,
4555
+ network: c.network,
4556
+ writesFiles: c.writesFiles,
4557
+ supportsDryRun: c.supportsDryRun,
4558
+ supportsJson: c.supportsJson
4559
+ }));
4560
+ printJson({ ok: true, command: "autotel commands", commands: compact });
4561
+ }
4562
+ function runExamples(name, opts) {
4563
+ configure(opts);
4564
+ if (name === void 0) {
4565
+ const all = COMMANDS.filter((c) => c.examples && c.examples.length > 0).map(
4566
+ (c) => ({ command: c.name, examples: c.examples })
4567
+ );
4568
+ printJson({ ok: true, command: "autotel examples", examples: all });
4569
+ return;
4570
+ }
4571
+ const cmd = getCommand(name);
4572
+ if (!cmd) {
4573
+ throw new AutotelError({
4574
+ type: "validation",
4575
+ code: AutotelErrorCodes.E_INVALID_INPUT,
4576
+ message: `Unknown command: ${name}`,
4577
+ expected: { command: COMMANDS.map((c) => c.name) },
4578
+ fix: "Run `autotel commands --json` to see available commands"
4579
+ });
4580
+ }
4581
+ printJson({
4582
+ ok: true,
4583
+ command: "autotel examples",
4584
+ target: cmd.name,
4585
+ examples: cmd.examples ?? []
4586
+ });
4587
+ }
4588
+ function runVersion(opts) {
4589
+ configure(opts);
4590
+ printJson({
4591
+ ok: true,
4592
+ command: "autotel version",
4593
+ version: readSelfVersion(),
4594
+ node: process.version,
4595
+ platform: process.platform,
4596
+ arch: process.arch
4597
+ });
4598
+ }
4599
+
4600
+ // src/commands/investigate/health.ts
4601
+ import { Command } from "commander";
4602
+
4603
+ // src/commands/investigate/runtime.ts
4604
+ function applyFlagsToEnv(flags) {
4605
+ if (flags.backend !== void 0) process.env.AUTOTEL_BACKEND = flags.backend;
4606
+ if (flags.jaegerBaseUrl !== void 0)
4607
+ process.env.JAEGER_BASE_URL = flags.jaegerBaseUrl;
4608
+ if (flags.tempoBaseUrl !== void 0)
4609
+ process.env.TEMPO_BASE_URL = flags.tempoBaseUrl;
4610
+ if (flags.prometheusBaseUrl !== void 0)
4611
+ process.env.PROMETHEUS_BASE_URL = flags.prometheusBaseUrl;
4612
+ if (flags.lokiBaseUrl !== void 0)
4613
+ process.env.LOKI_BASE_URL = flags.lokiBaseUrl;
4614
+ if (flags.collectorPort !== void 0)
4615
+ process.env.AUTOTEL_COLLECTOR_PORT = String(flags.collectorPort);
4616
+ if (flags.fixturePath !== void 0)
4617
+ process.env.AUTOTEL_FIXTURE_PATH = flags.fixturePath;
4618
+ }
4619
+ async function openBackend(flags) {
4620
+ applyFlagsToEnv(flags);
4621
+ const { loadConfig, createBackend } = await import("autotel-mcp");
4622
+ const config = loadConfig();
4623
+ return createBackend(config);
4624
+ }
4625
+ async function runStatic(command, flags, fn) {
4626
+ configureJsonOutput({
4627
+ outputFile: flags.outputFile,
4628
+ noSecrets: flags.noSecrets
4629
+ });
4630
+ try {
4631
+ const data = await fn();
4632
+ printJson({ ok: true, command, data });
4633
+ } catch (error2) {
4634
+ throw toInvestigateError(command, error2);
4635
+ }
4636
+ }
4637
+ async function runInvestigate(command, flags, fn) {
4638
+ configureJsonOutput({
4639
+ outputFile: flags.outputFile,
4640
+ noSecrets: flags.noSecrets
4641
+ });
4642
+ let handle = null;
4643
+ try {
4644
+ handle = await openBackend(flags);
4645
+ await handle.start();
4646
+ const data = await fn(handle.backend);
4647
+ printJson({ ok: true, command, data });
4648
+ } catch (error2) {
4649
+ throw toInvestigateError(command, error2);
4650
+ } finally {
4651
+ if (handle) {
4652
+ try {
4653
+ await handle.stop();
4654
+ } catch {
4655
+ }
4656
+ }
4657
+ }
4658
+ }
4659
+ function toInvestigateError(command, error2) {
4660
+ if (error2 instanceof AutotelError) return error2;
4661
+ if (error2 !== null && typeof error2 === "object" && "issues" in error2 && Array.isArray(error2.issues)) {
4662
+ const issues = error2.issues;
4663
+ const first = issues[0];
4664
+ const path15 = first?.path?.join(".") ?? "";
4665
+ return new AutotelError({
4666
+ type: "validation",
4667
+ code: "AUTOTEL_E_INVALID_INPUT",
4668
+ message: `autotel ${command}: invalid input${path15 ? ` for "${path15}"` : ""} \u2014 ${first?.message ?? "validation failed"}`,
4669
+ retryable: false,
4670
+ expected: { issues }
4671
+ });
4672
+ }
4673
+ const message = error2 instanceof Error ? error2.message : String(error2);
4674
+ return new AutotelError({
4675
+ type: "runtime",
4676
+ code: "AUTOTEL_E_UNKNOWN",
4677
+ message: `autotel ${command} failed: ${message}`,
4678
+ retryable: false
4679
+ });
4680
+ }
4681
+
4682
+ // src/commands/investigate/cli-helpers.ts
4683
+ var intArg = (v) => Number.parseInt(v, 10);
4684
+ var floatArg = (v) => Number.parseFloat(v);
4685
+ function addBackendFlags(cmd) {
4686
+ return cmd.option(
4687
+ "--backend <kind>",
4688
+ "Backend: collector|jaeger|tempo|prometheus|loki|stack|auto|fixture (env: AUTOTEL_BACKEND)"
4689
+ ).option("--jaeger-base-url <url>", "Jaeger base URL (env: JAEGER_BASE_URL)").option("--tempo-base-url <url>", "Tempo base URL (env: TEMPO_BASE_URL)").option(
4690
+ "--prometheus-base-url <url>",
4691
+ "Prometheus base URL (env: PROMETHEUS_BASE_URL)"
4692
+ ).option("--loki-base-url <url>", "Loki base URL (env: LOKI_BASE_URL)").option(
4693
+ "--collector-port <n>",
4694
+ "OTLP receiver port for the collector backend",
4695
+ intArg
4696
+ ).option("--fixture-path <path>", "Fixture JSON for the fixture backend").option("--output-file <path>", "Persist JSON output to this file").option("--no-secrets-in-output", "Redact secret-shaped values");
4697
+ }
4698
+ function addStaticFlags(cmd) {
4699
+ return cmd.option("--output-file <path>", "Persist JSON output to this file").option("--no-secrets-in-output", "Redact secret-shaped values");
4700
+ }
4701
+ function backendFlagsFromOpts(opts) {
4702
+ return {
4703
+ backend: opts.backend,
4704
+ jaegerBaseUrl: opts.jaegerBaseUrl,
4705
+ tempoBaseUrl: opts.tempoBaseUrl,
4706
+ prometheusBaseUrl: opts.prometheusBaseUrl,
4707
+ lokiBaseUrl: opts.lokiBaseUrl,
4708
+ collectorPort: opts.collectorPort,
4709
+ fixturePath: opts.fixturePath,
4710
+ outputFile: opts.outputFile,
4711
+ noSecrets: opts.secretsInOutput === false
4712
+ };
4713
+ }
4714
+ function staticFlagsFromOpts(opts) {
4715
+ return {
4716
+ outputFile: opts.outputFile,
4717
+ noSecrets: opts.secretsInOutput === false
4718
+ };
4719
+ }
4720
+ function addTimeWindowFlags(cmd) {
4721
+ return cmd.option("--service-name <name>", "Filter by service name").option("--operation-name <name>", "Filter by operation name").option("--lookback-minutes <n>", "Lookback window in minutes", intArg).option("--from <iso>", "Start time (ISO 8601)").option("--to <iso>", "End time (ISO 8601)").option("--limit <n>", "Max results", intArg);
4722
+ }
4723
+
4724
+ // src/commands/investigate/health.ts
4725
+ async function runHealth(flags) {
4726
+ await runInvestigate("health", flags, async (backend) => {
4727
+ const [health, capabilities] = await Promise.all([
4728
+ backend.healthCheck(),
4729
+ Promise.resolve(backend.capabilities())
4730
+ ]);
4731
+ return { ...health, signals: capabilities };
4732
+ });
4733
+ }
4734
+ async function runCapabilities(flags) {
4735
+ await runInvestigate(
4736
+ "capabilities",
4737
+ flags,
4738
+ async (backend) => backend.capabilities()
4739
+ );
4740
+ }
4741
+ function registerHealthCommands(program) {
4742
+ const healthCmd = new Command("health").description("Backend health check + signal coverage (JSON)").action(async function() {
4743
+ await runHealth(backendFlagsFromOpts(this.opts()));
4744
+ });
4745
+ addBackendFlags(healthCmd);
4746
+ program.addCommand(healthCmd);
4747
+ const capabilitiesCmd = new Command("capabilities").description("Which telemetry signals the active backend can serve (JSON)").action(async function() {
4748
+ await runCapabilities(backendFlagsFromOpts(this.opts()));
4749
+ });
4750
+ addBackendFlags(capabilitiesCmd);
4751
+ program.addCommand(capabilitiesCmd);
4752
+ }
4753
+
4754
+ // src/commands/investigate/discovery.ts
4755
+ import { Command as Command2 } from "commander";
4756
+ import {
4757
+ discoverServices,
4758
+ discoverTraceFields,
4759
+ discoverLogFields
4760
+ } from "autotel-mcp";
4761
+ async function runDiscoverServices(flags) {
4762
+ await runInvestigate("discover services", flags, async (backend) => {
4763
+ const limitServices = flags.limitServices ?? 100;
4764
+ const traceSample = flags.traceSample ?? 200;
4765
+ const logSample = flags.logSample ?? 200;
4766
+ const metricSample = flags.metricSample ?? 200;
4767
+ const caps = backend.capabilities();
4768
+ const servicesResult = await backend.listServices({ limit: limitServices });
4769
+ const services = servicesResult.services.slice(0, limitServices);
4770
+ const [traces, logs, metrics] = await Promise.all([
4771
+ caps.traces === "available" ? backend.searchTraces({ limit: traceSample }).then((r) => r.items) : Promise.resolve([]),
4772
+ caps.logs === "available" ? backend.searchLogs({ limit: logSample }).then((r) => r.items) : Promise.resolve([]),
4773
+ caps.metrics === "available" ? backend.listMetrics({ limit: metricSample }).then((r) => r.items) : Promise.resolve([])
4774
+ ]);
4775
+ const discovered = discoverServices({ services, traces, logs, metrics });
4776
+ return { count: discovered.length, services: discovered };
4777
+ });
4778
+ }
4779
+ async function runDiscoverTraceFields(flags) {
4780
+ await runInvestigate("discover trace-fields", flags, async (backend) => {
4781
+ return discoverFields(backend, flags, "traces");
4782
+ });
4783
+ }
4784
+ async function runDiscoverLogFields(flags) {
4785
+ await runInvestigate("discover log-fields", flags, async (backend) => {
4786
+ return discoverFields(backend, flags, "logs");
4787
+ });
4788
+ }
4789
+ async function discoverFields(backend, flags, signal) {
4790
+ const sampleSize = flags.sampleSize ?? 200;
4791
+ if (signal === "traces") {
4792
+ const traces = await backend.searchTraces({ limit: sampleSize }).then((r) => r.items);
4793
+ return {
4794
+ search: flags.search ?? null,
4795
+ sampleSize: traces.length,
4796
+ ...discoverTraceFields(traces, flags.search)
4797
+ };
4798
+ }
4799
+ const logs = await backend.searchLogs({ limit: sampleSize }).then((r) => r.items);
4800
+ return {
4801
+ search: flags.search ?? null,
4802
+ sampleSize: logs.length,
4803
+ ...discoverLogFields(logs, flags.search)
4804
+ };
4805
+ }
4806
+ function registerDiscoveryCommands(program) {
4807
+ const discoverCmd = new Command2("discover").description(
4808
+ "Discover services and field shapes from the active backend (JSON)"
4809
+ );
4810
+ const servicesCmd = new Command2("services").description("Discover services with cross-signal metadata").option("--limit-services <n>", "Max services", intArg).option("--trace-sample <n>", "Trace sample size", intArg).option("--log-sample <n>", "Log sample size", intArg).option("--metric-sample <n>", "Metric sample size", intArg).action(async function() {
4811
+ const o = this.optsWithGlobals();
4812
+ await runDiscoverServices({
4813
+ ...backendFlagsFromOpts(o),
4814
+ limitServices: o.limitServices,
4815
+ traceSample: o.traceSample,
4816
+ logSample: o.logSample,
4817
+ metricSample: o.metricSample
4818
+ });
4819
+ });
4820
+ const traceFieldsCmd = new Command2("trace-fields").description("Discover trace/span field names from sampled traces").option("--search <text>", "Filter field names by substring").option("--sample-size <n>", "Trace sample size", intArg).action(async function() {
4821
+ const o = this.optsWithGlobals();
4822
+ await runDiscoverTraceFields({
4823
+ ...backendFlagsFromOpts(o),
4824
+ search: o.search,
4825
+ sampleSize: o.sampleSize
4826
+ });
4827
+ });
4828
+ const logFieldsCmd = new Command2("log-fields").description("Discover log field names from sampled logs").option("--search <text>", "Filter field names by substring").option("--sample-size <n>", "Log sample size", intArg).action(async function() {
4829
+ const o = this.optsWithGlobals();
4830
+ await runDiscoverLogFields({
4831
+ ...backendFlagsFromOpts(o),
4832
+ search: o.search,
4833
+ sampleSize: o.sampleSize
4834
+ });
4835
+ });
4836
+ addBackendFlags(discoverCmd);
4837
+ discoverCmd.addCommand(servicesCmd);
4838
+ discoverCmd.addCommand(traceFieldsCmd);
4839
+ discoverCmd.addCommand(logFieldsCmd);
4840
+ program.addCommand(discoverCmd);
4841
+ }
4842
+
4843
+ // src/commands/investigate/investigation.ts
4844
+ import { Command as Command3 } from "commander";
4845
+ import {
4846
+ toTraceSearchQuery,
4847
+ toSpanSearchQuery
4848
+ } from "autotel-mcp";
4849
+
4850
+ // src/commands/investigate/signals.ts
4851
+ import {
4852
+ toMetricSearchQuery,
4853
+ toLogSearchQuery
4854
+ } from "autotel-mcp";
4855
+ async function runQueryMetrics(flags) {
4856
+ await runInvestigate(
4857
+ "query metrics",
4858
+ flags,
4859
+ async (backend) => backend.listMetrics(toMetricSearchQuery(flags))
4860
+ );
4861
+ }
4862
+ async function runQueryLogs(flags) {
4863
+ await runInvestigate(
4864
+ "query logs",
4865
+ flags,
4866
+ async (backend) => backend.searchLogs(toLogSearchQuery(flags))
4867
+ );
4868
+ }
4869
+
4870
+ // src/commands/investigate/investigation.ts
4871
+ async function runQueryTraces(flags) {
4872
+ await runInvestigate(
4873
+ "query traces",
4874
+ flags,
4875
+ async (backend) => backend.searchTraces(toTraceSearchQuery(flags))
4876
+ );
4877
+ }
4878
+ async function runQuerySpans(flags) {
4879
+ await runInvestigate(
4880
+ "query spans",
4881
+ flags,
4882
+ async (backend) => backend.searchSpans(toSpanSearchQuery(flags))
4883
+ );
4884
+ }
4885
+ async function runTraceGet(flags) {
4886
+ await runInvestigate(
4887
+ "trace get",
4888
+ flags,
4889
+ async (backend) => backend.getTrace(flags.traceId)
4890
+ );
4891
+ }
4892
+ async function runTraceSummary(flags) {
4893
+ await runInvestigate(
4894
+ "trace summary",
4895
+ flags,
4896
+ async (backend) => backend.summarizeTrace(flags.traceId)
4897
+ );
4898
+ }
4899
+ function registerQueryCommands(program) {
4900
+ const queryCmd = new Command3("query").description(
4901
+ "Query traces, spans, metrics, or logs (JSON)"
4902
+ );
4903
+ const tracesCmd = addTimeWindowFlags(new Command3("traces")).description("Search traces by service, op, status, tags, time, error").option("--error-only", "Only traces with errors").option("--status-code <code>", "OK | ERROR | UNSET").option("--min-duration-ms <n>", "Minimum duration", intArg).option("--max-duration-ms <n>", "Maximum duration", intArg).option("--gen-ai-system <name>", "gen_ai.system").option("--gen-ai-request-model <name>", "gen_ai.request.model").option("--gen-ai-response-model <name>", "gen_ai.response.model").action(async function() {
4904
+ const o = this.optsWithGlobals();
4905
+ await runQueryTraces({
4906
+ ...backendFlagsFromOpts(o),
4907
+ serviceName: o.serviceName,
4908
+ operationName: o.operationName,
4909
+ lookbackMinutes: o.lookbackMinutes,
4910
+ from: o.from,
4911
+ to: o.to,
4912
+ limit: o.limit,
4913
+ errorOnly: o.errorOnly,
4914
+ statusCode: o.statusCode,
4915
+ minDurationMs: o.minDurationMs,
4916
+ maxDurationMs: o.maxDurationMs,
4917
+ genAiSystem: o.genAiSystem,
4918
+ genAiRequestModel: o.genAiRequestModel,
4919
+ genAiResponseModel: o.genAiResponseModel
4920
+ });
4921
+ });
4922
+ const spansCmd = addTimeWindowFlags(new Command3("spans")).description("Search individual spans by service/op/status/tags/duration").option("--error-only", "Only spans with errors").option("--status-code <code>", "OK | ERROR | UNSET").option("--min-duration-ms <n>", "Minimum span duration", intArg).option("--max-duration-ms <n>", "Maximum span duration", intArg).action(async function() {
4923
+ const o = this.optsWithGlobals();
4924
+ await runQuerySpans({
4925
+ ...backendFlagsFromOpts(o),
4926
+ serviceName: o.serviceName,
4927
+ operationName: o.operationName,
4928
+ lookbackMinutes: o.lookbackMinutes,
4929
+ from: o.from,
4930
+ to: o.to,
4931
+ limit: o.limit,
4932
+ errorOnly: o.errorOnly,
4933
+ statusCode: o.statusCode,
4934
+ minDurationMs: o.minDurationMs,
4935
+ maxDurationMs: o.maxDurationMs
4936
+ });
4937
+ });
4938
+ const metricsCmd = addTimeWindowFlags(new Command3("metrics")).description("List metric series").option("--metric-name <name>", "Filter by metric name").action(async function() {
4939
+ const o = this.optsWithGlobals();
4940
+ await runQueryMetrics({
4941
+ ...backendFlagsFromOpts(o),
4942
+ metricName: o.metricName,
4943
+ serviceName: o.serviceName,
4944
+ lookbackMinutes: o.lookbackMinutes,
4945
+ from: o.from,
4946
+ to: o.to,
4947
+ limit: o.limit
4948
+ });
4949
+ });
4950
+ const logsCmd = addTimeWindowFlags(new Command3("logs")).description("Search logs").option("--trace-id <id>", "Filter by trace id").option("--span-id <id>", "Filter by span id").option("--severity-text <text>", "Severity text").option("--text <text>", "Free-text search").action(async function() {
4951
+ const o = this.optsWithGlobals();
4952
+ await runQueryLogs({
4953
+ ...backendFlagsFromOpts(o),
4954
+ serviceName: o.serviceName,
4955
+ traceId: o.traceId,
4956
+ spanId: o.spanId,
4957
+ severityText: o.severityText,
4958
+ text: o.text,
4959
+ lookbackMinutes: o.lookbackMinutes,
4960
+ from: o.from,
4961
+ to: o.to,
4962
+ limit: o.limit
4963
+ });
4964
+ });
4965
+ addBackendFlags(queryCmd);
4966
+ queryCmd.addCommand(tracesCmd);
4967
+ queryCmd.addCommand(spansCmd);
4968
+ queryCmd.addCommand(metricsCmd);
4969
+ queryCmd.addCommand(logsCmd);
4970
+ program.addCommand(queryCmd);
4971
+ }
4972
+ function registerTraceCommands(program) {
4973
+ const traceLookupCmd = new Command3("trace").description(
4974
+ "Trace lookup commands (JSON)"
4975
+ );
4976
+ const getCmd = new Command3("get").description("Get a trace by ID").argument("<traceId>", "Trace ID").action(async function(traceId) {
4977
+ await runTraceGet({
4978
+ ...backendFlagsFromOpts(this.optsWithGlobals()),
4979
+ traceId
4980
+ });
4981
+ });
4982
+ const summaryCmd = new Command3("summary").description("Compact incident-friendly trace summary").argument("<traceId>", "Trace ID").action(async function(traceId) {
4983
+ await runTraceSummary({
4984
+ ...backendFlagsFromOpts(this.optsWithGlobals()),
4985
+ traceId
4986
+ });
4987
+ });
4988
+ addBackendFlags(traceLookupCmd);
4989
+ traceLookupCmd.addCommand(getCmd);
4990
+ traceLookupCmd.addCommand(summaryCmd);
4991
+ program.addCommand(traceLookupCmd);
4992
+ }
4993
+
4994
+ // src/commands/investigate/topology.ts
4995
+ import { Command as Command4 } from "commander";
4996
+ async function runListServices(flags) {
4997
+ await runInvestigate(
4998
+ "topology services",
4999
+ flags,
5000
+ async (backend) => backend.listServices()
5001
+ );
5002
+ }
5003
+ async function runListOperations(flags) {
5004
+ await runInvestigate(
5005
+ "topology operations",
5006
+ flags,
5007
+ async (backend) => backend.listOperations(flags.serviceName)
5008
+ );
5009
+ }
5010
+ async function runServiceMap(flags) {
5011
+ await runInvestigate(
5012
+ "topology map",
5013
+ flags,
5014
+ async (backend) => backend.serviceMap(flags.lookbackMinutes ?? 60, flags.limit ?? 20)
5015
+ );
5016
+ }
5017
+ function registerTopologyCommands(program) {
5018
+ const topologyCmd = new Command4("topology").description(
5019
+ "Service topology commands (JSON)"
5020
+ );
5021
+ const servicesCmd = new Command4("services").description("List known services").action(async function() {
5022
+ await runListServices(backendFlagsFromOpts(this.optsWithGlobals()));
5023
+ });
5024
+ const operationsCmd = new Command4("operations").description("List operations for a service").argument("<serviceName>", "Service name").action(async function(serviceName) {
5025
+ await runListOperations({
5026
+ ...backendFlagsFromOpts(this.optsWithGlobals()),
5027
+ serviceName
5028
+ });
5029
+ });
5030
+ const mapCmd = new Command4("map").description("Build a service dependency map").option("--lookback-minutes <n>", "Lookback in minutes", intArg).option("--limit <n>", "Max services", intArg).action(async function() {
5031
+ const o = this.optsWithGlobals();
5032
+ await runServiceMap({
5033
+ ...backendFlagsFromOpts(o),
5034
+ lookbackMinutes: o.lookbackMinutes,
5035
+ limit: o.limit
5036
+ });
5037
+ });
5038
+ addBackendFlags(topologyCmd);
5039
+ topologyCmd.addCommand(servicesCmd);
5040
+ topologyCmd.addCommand(operationsCmd);
5041
+ topologyCmd.addCommand(mapCmd);
5042
+ program.addCommand(topologyCmd);
5043
+ }
5044
+
5045
+ // src/commands/investigate/diagnosis.ts
5046
+ import { Command as Command5 } from "commander";
5047
+ import {
5048
+ detectAnomalies,
5049
+ findRootCause,
5050
+ pickErrorMessage
5051
+ } from "autotel-mcp";
5052
+ async function runDiagnoseAnomalies(flags) {
5053
+ await runInvestigate("diagnose anomalies", flags, async (backend) => {
5054
+ const lookback = flags.lookbackMinutes ?? 60;
5055
+ const nowMs = Date.now();
5056
+ const result = await backend.searchTraces({
5057
+ service: flags.service,
5058
+ startTimeUnixMs: nowMs - lookback * 60 * 1e3,
5059
+ endTimeUnixMs: nowMs,
5060
+ limit: 100
5061
+ });
5062
+ return detectAnomalies(result.items, {
5063
+ service: flags.service,
5064
+ operation: flags.operation
5065
+ });
5066
+ });
5067
+ }
5068
+ async function runDiagnoseRootCause(flags) {
5069
+ await runInvestigate("diagnose root-cause", flags, async (backend) => {
5070
+ const trace = await backend.getTrace(flags.traceId);
5071
+ if (!trace) return { error: `Trace not found: ${flags.traceId}` };
5072
+ return findRootCause(trace);
5073
+ });
5074
+ }
5075
+ async function runDiagnoseErrors(flags) {
5076
+ await runInvestigate("diagnose errors", flags, async (backend) => {
5077
+ const lookback = flags.lookbackMinutes ?? 60;
5078
+ const limit = flags.limit ?? 20;
5079
+ const nowMs = Date.now();
5080
+ const result = await backend.searchTraces({
5081
+ service: flags.service,
5082
+ hasError: true,
5083
+ startTimeUnixMs: nowMs - lookback * 60 * 1e3,
5084
+ endTimeUnixMs: nowMs,
5085
+ limit
5086
+ });
5087
+ const groups = /* @__PURE__ */ new Map();
5088
+ for (const trace of result.items) {
5089
+ for (const span of trace.spans) {
5090
+ if (!span.hasError) continue;
5091
+ if (flags.service && span.serviceName !== flags.service) continue;
5092
+ const key = `${span.serviceName}::${span.operationName}`;
5093
+ if (!groups.has(key)) {
5094
+ groups.set(key, {
5095
+ service: span.serviceName,
5096
+ operation: span.operationName,
5097
+ count: 0,
5098
+ errorMessages: [],
5099
+ traceIds: []
5100
+ });
5101
+ }
5102
+ const group = groups.get(key);
5103
+ group.count++;
5104
+ const msg = pickErrorMessage(span.tags);
5105
+ if (msg && !group.errorMessages.includes(msg)) {
5106
+ group.errorMessages.push(msg);
5107
+ }
5108
+ if (!group.traceIds.includes(trace.traceId)) {
5109
+ group.traceIds.push(trace.traceId);
5110
+ }
5111
+ }
5112
+ }
5113
+ return {
5114
+ totalTraces: result.totalCount,
5115
+ groups: [...groups.values()].toSorted((a, b) => b.count - a.count)
5116
+ };
5117
+ });
5118
+ }
5119
+ async function runDiagnoseSlos(flags) {
5120
+ await runInvestigate("diagnose slos", flags, async (backend) => {
5121
+ const lookback = flags.lookbackMinutes ?? 60;
5122
+ const nowMs = Date.now();
5123
+ const result = await backend.searchTraces({
5124
+ service: flags.service,
5125
+ startTimeUnixMs: nowMs - lookback * 60 * 1e3,
5126
+ endTimeUnixMs: nowMs,
5127
+ limit: 100
5128
+ });
5129
+ const spans = result.items.flatMap(
5130
+ (t) => t.spans.filter((s) => s.serviceName === flags.service)
5131
+ );
5132
+ const violations = [];
5133
+ if (spans.length === 0) {
5134
+ return {
5135
+ service: flags.service,
5136
+ totalSpans: 0,
5137
+ violations,
5138
+ message: "No spans found for the given service and time window."
5139
+ };
5140
+ }
5141
+ const durations = spans.map((s) => s.durationMs).toSorted((a, b) => a - b);
5142
+ const p99Index = Math.floor(durations.length * 0.99);
5143
+ const actualP99 = durations[Math.min(p99Index, durations.length - 1)];
5144
+ if (flags.p99LatencyMs !== void 0 && actualP99 > flags.p99LatencyMs) {
5145
+ violations.push({
5146
+ type: "p99_latency",
5147
+ target: flags.p99LatencyMs,
5148
+ actual: actualP99,
5149
+ description: `p99 latency ${actualP99.toFixed(1)}ms exceeds target ${flags.p99LatencyMs}ms`
5150
+ });
5151
+ }
5152
+ const errorCount = spans.filter((s) => s.hasError).length;
5153
+ const actualErrorRate = errorCount / spans.length;
5154
+ if (flags.maxErrorRate !== void 0 && actualErrorRate > flags.maxErrorRate) {
5155
+ violations.push({
5156
+ type: "error_rate",
5157
+ target: flags.maxErrorRate,
5158
+ actual: actualErrorRate,
5159
+ description: `Error rate ${(actualErrorRate * 100).toFixed(2)}% exceeds target ${(flags.maxErrorRate * 100).toFixed(2)}%`
5160
+ });
5161
+ }
5162
+ return {
5163
+ service: flags.service,
5164
+ totalSpans: spans.length,
5165
+ p99LatencyMs: actualP99,
5166
+ errorRate: actualErrorRate,
5167
+ violations
5168
+ };
5169
+ });
5170
+ }
5171
+ function registerDiagnoseCommands(program) {
5172
+ const diagnoseCmd = new Command5("diagnose").description(
5173
+ "Anomaly / root-cause / errors / SLO diagnosis (JSON)"
5174
+ );
5175
+ const anomaliesCmd = new Command5("anomalies").description("Detect latency/error-rate outliers").option("--service <name>", "Service filter").option("--operation <name>", "Operation filter").option("--lookback-minutes <n>", "Lookback in minutes", intArg).action(async function() {
5176
+ const o = this.optsWithGlobals();
5177
+ await runDiagnoseAnomalies({
5178
+ ...backendFlagsFromOpts(o),
5179
+ service: o.service,
5180
+ operation: o.operation,
5181
+ lookbackMinutes: o.lookbackMinutes
5182
+ });
5183
+ });
5184
+ const rootCauseCmd = new Command5("root-cause").description("Walk a trace tree to identify the bottleneck span").argument("<traceId>", "Trace ID").action(async function(traceId) {
5185
+ await runDiagnoseRootCause({
5186
+ ...backendFlagsFromOpts(this.optsWithGlobals()),
5187
+ traceId
5188
+ });
5189
+ });
5190
+ const errorsCmd = new Command5("errors").description("Aggregate error spans by service/operation").option("--service <name>", "Service filter").option("--lookback-minutes <n>", "Lookback in minutes", intArg).option("--limit <n>", "Max traces to scan", intArg).action(async function() {
5191
+ const o = this.optsWithGlobals();
5192
+ await runDiagnoseErrors({
5193
+ ...backendFlagsFromOpts(o),
5194
+ service: o.service,
5195
+ lookbackMinutes: o.lookbackMinutes,
5196
+ limit: o.limit
5197
+ });
5198
+ });
5199
+ const slosCmd = new Command5("slos").description("Report SLO violations for a service").requiredOption("--service <name>", "Service to check").option("--p99-latency-ms <n>", "p99 latency target", floatArg).option("--max-error-rate <n>", "Error-rate target (0..1)", floatArg).option("--lookback-minutes <n>", "Lookback in minutes", intArg).action(async function() {
5200
+ const o = this.optsWithGlobals();
5201
+ await runDiagnoseSlos({
5202
+ ...backendFlagsFromOpts(o),
5203
+ service: o.service,
5204
+ p99LatencyMs: o.p99LatencyMs,
5205
+ maxErrorRate: o.maxErrorRate,
5206
+ lookbackMinutes: o.lookbackMinutes
5207
+ });
5208
+ });
5209
+ addBackendFlags(diagnoseCmd);
5210
+ diagnoseCmd.addCommand(anomaliesCmd);
5211
+ diagnoseCmd.addCommand(rootCauseCmd);
5212
+ diagnoseCmd.addCommand(errorsCmd);
5213
+ diagnoseCmd.addCommand(slosCmd);
5214
+ program.addCommand(diagnoseCmd);
5215
+ }
5216
+
5217
+ // src/commands/investigate/correlation.ts
5218
+ import { Command as Command6 } from "commander";
5219
+ import { detectAnomalies as detectAnomalies2, findRootCause as findRootCause2 } from "autotel-mcp";
5220
+ async function runCorrelate(flags) {
5221
+ await runInvestigate(
5222
+ "correlate trace",
5223
+ flags,
5224
+ async (backend) => backend.getCorrelatedSignals(flags.traceId)
5225
+ );
5226
+ }
5227
+ async function runExplainSlowdown(flags) {
5228
+ await runInvestigate("correlate explain-slowdown", flags, async (backend) => {
5229
+ const lookback = flags.lookbackMinutes ?? 60;
5230
+ const nowMs = Date.now();
5231
+ const result = await backend.searchTraces({
5232
+ service: flags.service,
5233
+ startTimeUnixMs: nowMs - lookback * 60 * 1e3,
5234
+ endTimeUnixMs: nowMs,
5235
+ limit: 100
5236
+ });
5237
+ const anomalies = detectAnomalies2(result.items, { service: flags.service });
5238
+ const findings = await Promise.all(
5239
+ anomalies.map(async (anomaly) => {
5240
+ const sampleTraceId = anomaly.affectedTraceIds[0];
5241
+ if (!sampleTraceId) {
5242
+ return { anomaly, rootCause: null, correlatedSignals: null };
5243
+ }
5244
+ const trace = await backend.getTrace(sampleTraceId);
5245
+ const rootCause = trace ? findRootCause2(trace) : null;
5246
+ const correlatedSignals = await backend.getCorrelatedSignals(sampleTraceId);
5247
+ return { anomaly, rootCause, correlatedSignals };
5248
+ })
5249
+ );
5250
+ return {
5251
+ service: flags.service,
5252
+ lookbackMinutes: lookback,
5253
+ anomalyCount: anomalies.length,
5254
+ findings
5255
+ };
5256
+ });
5257
+ }
5258
+ function registerCorrelateCommands(program) {
5259
+ const correlateCmd = new Command6("correlate").description(
5260
+ "Cross-signal correlation (JSON)"
5261
+ );
5262
+ const traceCmd = new Command6("trace").description("Trace + metrics + correlated logs for a trace ID").argument("<traceId>", "Trace ID").action(async function(traceId) {
5263
+ await runCorrelate({
5264
+ ...backendFlagsFromOpts(this.optsWithGlobals()),
5265
+ traceId
5266
+ });
5267
+ });
5268
+ const slowdownCmd = new Command6("explain-slowdown").description("Identify when/why a service degraded").requiredOption("--service <name>", "Service name").option("--lookback-minutes <n>", "Lookback in minutes", intArg).action(async function() {
5269
+ const o = this.optsWithGlobals();
5270
+ await runExplainSlowdown({
5271
+ ...backendFlagsFromOpts(o),
5272
+ service: o.service,
5273
+ lookbackMinutes: o.lookbackMinutes
5274
+ });
5275
+ });
5276
+ addBackendFlags(correlateCmd);
5277
+ correlateCmd.addCommand(traceCmd);
5278
+ correlateCmd.addCommand(slowdownCmd);
5279
+ program.addCommand(correlateCmd);
5280
+ }
5281
+
5282
+ // src/commands/investigate/llm.ts
5283
+ import {
5284
+ collectUsage,
5285
+ listModels,
5286
+ getModelStats,
5287
+ rankExpensiveTraces,
5288
+ rankSlowTraces,
5289
+ listToolUsage,
5290
+ toTraceSearchQuery as toTraceSearchQuery2
5291
+ } from "autotel-mcp";
5292
+ import { Command as Command7 } from "commander";
5293
+ function parseDateToUnixMs(value) {
5294
+ if (!value) return void 0;
5295
+ const parsed = Date.parse(value);
5296
+ return Number.isFinite(parsed) ? parsed : void 0;
5297
+ }
5298
+ async function collectTracesForAnalytics(backend, input2) {
5299
+ const result = await backend.searchTraces(
5300
+ toTraceSearchQuery2({
5301
+ serviceName: input2.serviceName,
5302
+ genAiSystem: input2.genAiSystem,
5303
+ genAiRequestModel: input2.genAiRequestModel,
5304
+ genAiResponseModel: input2.genAiResponseModel,
5305
+ limit: input2.limit ?? 1e3
5306
+ })
5307
+ );
5308
+ let filtered = result.items;
5309
+ const startUnixMs = parseDateToUnixMs(input2.startTime);
5310
+ const endUnixMs = parseDateToUnixMs(input2.endTime);
5311
+ if (startUnixMs !== void 0) {
5312
+ filtered = filtered.filter(
5313
+ (trace) => trace.spans.some((span) => span.startTimeUnixMs >= startUnixMs)
5314
+ );
5315
+ }
5316
+ if (endUnixMs !== void 0) {
5317
+ filtered = filtered.filter(
5318
+ (trace) => trace.spans.some((span) => span.startTimeUnixMs <= endUnixMs)
5319
+ );
5320
+ }
5321
+ return filtered;
5322
+ }
5323
+ async function runLlmUsage(flags) {
5324
+ await runInvestigate("llm usage", flags, async (backend) => {
5325
+ const traces = await collectTracesForAnalytics(backend, flags);
5326
+ const report = collectUsage(traces);
5327
+ return {
5328
+ period: {
5329
+ startTime: flags.startTime ?? null,
5330
+ endTime: flags.endTime ?? null
5331
+ },
5332
+ filters: {
5333
+ serviceName: flags.serviceName ?? null,
5334
+ genAiSystem: flags.genAiSystem ?? null,
5335
+ genAiRequestModel: flags.genAiRequestModel ?? null,
5336
+ genAiResponseModel: flags.genAiResponseModel ?? null
5337
+ },
5338
+ summary: {
5339
+ totalRequests: report.totalRequests,
5340
+ totalPromptTokens: report.totalPromptTokens,
5341
+ totalCompletionTokens: report.totalCompletionTokens,
5342
+ totalTokens: report.totalTokens,
5343
+ totalCostUsd: report.totalCostUsd,
5344
+ unpricedRequests: report.unpricedRequests
5345
+ },
5346
+ byModel: report.byModel,
5347
+ byService: report.byService
5348
+ };
5349
+ });
5350
+ }
5351
+ async function runLlmModels(flags) {
5352
+ await runInvestigate("llm models", flags, async (backend) => {
5353
+ const traces = await collectTracesForAnalytics(backend, flags);
5354
+ const models = listModels(traces).slice(0, flags.limit ?? 1e3);
5355
+ return { count: models.length, models };
5356
+ });
5357
+ }
5358
+ async function runLlmModelStats(flags) {
5359
+ await runInvestigate("llm model-stats", flags, async (backend) => {
5360
+ const traces = await collectTracesForAnalytics(backend, flags);
5361
+ const stats = getModelStats(traces, flags.modelName);
5362
+ if (!stats) {
5363
+ return {
5364
+ error: `No traces found for model '${flags.modelName}' in the specified time range`
5365
+ };
5366
+ }
5367
+ return stats;
5368
+ });
5369
+ }
5370
+ async function runLlmExpensive(flags) {
5371
+ await runInvestigate("llm expensive", flags, async (backend) => {
5372
+ const traces = await collectTracesForAnalytics(backend, flags);
5373
+ let ranked = rankExpensiveTraces(traces);
5374
+ if (flags.minTokens !== void 0) {
5375
+ ranked = ranked.filter((t) => t.tokens.total >= flags.minTokens);
5376
+ }
5377
+ ranked = ranked.slice(0, flags.limit ?? 10);
5378
+ return { count: ranked.length, traces: ranked };
5379
+ });
5380
+ }
5381
+ async function runLlmSlow(flags) {
5382
+ await runInvestigate("llm slow", flags, async (backend) => {
5383
+ const traces = await collectTracesForAnalytics(backend, flags);
5384
+ let ranked = rankSlowTraces(traces);
5385
+ if (flags.minDurationMs !== void 0) {
5386
+ ranked = ranked.filter((t) => t.durationMs >= flags.minDurationMs);
5387
+ }
5388
+ ranked = ranked.slice(0, flags.limit ?? 10);
5389
+ return { count: ranked.length, traces: ranked };
5390
+ });
5391
+ }
5392
+ async function runLlmTools(flags) {
5393
+ await runInvestigate("llm tools", flags, async (backend) => {
5394
+ const traces = await collectTracesForAnalytics(backend, flags);
5395
+ const tools = listToolUsage(traces).slice(0, flags.limit ?? 1e3);
5396
+ return {
5397
+ count: tools.length,
5398
+ totalCalls: tools.reduce((sum, t) => sum + t.usageCount, 0),
5399
+ tools
5400
+ };
5401
+ });
5402
+ }
5403
+ function registerLlmCommands(program) {
5404
+ const llmCmd = new Command7("llm").description(
5405
+ "LLM analytics (cost, models, expensive/slow traces, tools)"
5406
+ );
5407
+ const commonOpts = (cmd) => cmd.option("--start-time <iso>", "Start of window (ISO 8601)").option("--end-time <iso>", "End of window (ISO 8601)").option("--service-name <name>", "Service filter").option("--gen-ai-system <name>", "gen_ai.system filter").option("--gen-ai-request-model <name>", "gen_ai.request.model filter").option("--gen-ai-response-model <name>", "gen_ai.response.model filter").option("--limit <n>", "Max results", intArg);
5408
+ const usageCmd = commonOpts(new Command7("usage")).description("Aggregate token usage by model and service").action(async function() {
5409
+ const o = this.optsWithGlobals();
5410
+ await runLlmUsage({
5411
+ ...backendFlagsFromOpts(o),
5412
+ startTime: o.startTime,
5413
+ endTime: o.endTime,
5414
+ serviceName: o.serviceName,
5415
+ genAiSystem: o.genAiSystem,
5416
+ genAiRequestModel: o.genAiRequestModel,
5417
+ genAiResponseModel: o.genAiResponseModel,
5418
+ limit: o.limit
5419
+ });
5420
+ });
5421
+ const modelsCmd = commonOpts(new Command7("models")).description("Discover LLM models in use").action(async function() {
5422
+ const o = this.optsWithGlobals();
5423
+ await runLlmModels({
5424
+ ...backendFlagsFromOpts(o),
5425
+ startTime: o.startTime,
5426
+ endTime: o.endTime,
5427
+ serviceName: o.serviceName,
5428
+ genAiSystem: o.genAiSystem,
5429
+ limit: o.limit
5430
+ });
5431
+ });
5432
+ const modelStatsCmd = commonOpts(new Command7("model-stats")).description("Latency/token/error stats for one LLM model").requiredOption("--model-name <name>", "Model to inspect").action(async function() {
5433
+ const o = this.optsWithGlobals();
5434
+ await runLlmModelStats({
5435
+ ...backendFlagsFromOpts(o),
5436
+ modelName: o.modelName,
5437
+ startTime: o.startTime,
5438
+ endTime: o.endTime,
5439
+ serviceName: o.serviceName
5440
+ });
5441
+ });
5442
+ const expensiveCmd = commonOpts(new Command7("expensive")).description("Traces with highest total LLM token usage").option("--min-tokens <n>", "Minimum token threshold", intArg).action(async function() {
5443
+ const o = this.optsWithGlobals();
5444
+ await runLlmExpensive({
5445
+ ...backendFlagsFromOpts(o),
5446
+ startTime: o.startTime,
5447
+ endTime: o.endTime,
5448
+ serviceName: o.serviceName,
5449
+ genAiRequestModel: o.genAiRequestModel,
5450
+ genAiResponseModel: o.genAiResponseModel,
5451
+ minTokens: o.minTokens,
5452
+ limit: o.limit
5453
+ });
5454
+ });
5455
+ const slowCmd = commonOpts(new Command7("slow")).description("Slowest traces that include LLM spans").option("--min-duration-ms <n>", "Minimum duration", intArg).action(async function() {
5456
+ const o = this.optsWithGlobals();
5457
+ await runLlmSlow({
5458
+ ...backendFlagsFromOpts(o),
5459
+ startTime: o.startTime,
5460
+ endTime: o.endTime,
5461
+ serviceName: o.serviceName,
5462
+ genAiRequestModel: o.genAiRequestModel,
5463
+ genAiResponseModel: o.genAiResponseModel,
5464
+ minDurationMs: o.minDurationMs,
5465
+ limit: o.limit
5466
+ });
5467
+ });
5468
+ const toolsCmd = commonOpts(new Command7("tools")).description("Discover tool/function spans grouped by tool name").action(async function() {
5469
+ const o = this.optsWithGlobals();
5470
+ await runLlmTools({
5471
+ ...backendFlagsFromOpts(o),
5472
+ startTime: o.startTime,
5473
+ endTime: o.endTime,
5474
+ serviceName: o.serviceName,
5475
+ genAiSystem: o.genAiSystem,
5476
+ limit: o.limit
5477
+ });
5478
+ });
5479
+ addBackendFlags(llmCmd);
5480
+ llmCmd.addCommand(usageCmd);
5481
+ llmCmd.addCommand(modelsCmd);
5482
+ llmCmd.addCommand(modelStatsCmd);
5483
+ llmCmd.addCommand(expensiveCmd);
5484
+ llmCmd.addCommand(slowCmd);
5485
+ llmCmd.addCommand(toolsCmd);
5486
+ program.addCommand(llmCmd);
5487
+ }
5488
+
5489
+ // src/commands/investigate/semconv.ts
5490
+ import {
5491
+ clearSemanticConventionCache,
5492
+ getSemanticConventionNamespace,
5493
+ listSemanticConventionNamespaces
5494
+ } from "autotel-mcp";
5495
+ import { Command as Command8 } from "commander";
5496
+ async function runSemconvList(flags) {
5497
+ await runStatic("semconv list", flags, async () => ({
5498
+ namespaces: await listSemanticConventionNamespaces()
5499
+ }));
5500
+ }
5501
+ async function runSemconvGet(flags) {
5502
+ await runStatic(
5503
+ "semconv get",
5504
+ flags,
5505
+ async () => getSemanticConventionNamespace(flags.namespace)
5506
+ );
5507
+ }
5508
+ async function runSemconvRefresh(flags) {
5509
+ await runStatic("semconv refresh", flags, async () => {
5510
+ clearSemanticConventionCache();
5511
+ return { cleared: true };
5512
+ });
5513
+ }
5514
+ function registerSemconvCommands(program) {
5515
+ const semconvCmd = new Command8("semconv").description(
5516
+ "OpenTelemetry semantic conventions lookup (JSON)"
5517
+ );
5518
+ const listCmd = addStaticFlags(new Command8("list")).description("List semconv namespaces").action(async function() {
5519
+ await runSemconvList(staticFlagsFromOpts(this.optsWithGlobals()));
5520
+ });
5521
+ const getCmd = addStaticFlags(new Command8("get")).description("Get groups for one namespace").argument("<namespace>", "Namespace (e.g. http, rpc, database)").action(async function(namespace) {
5522
+ await runSemconvGet({
5523
+ ...staticFlagsFromOpts(this.optsWithGlobals()),
5524
+ namespace
5525
+ });
5526
+ });
5527
+ const refreshCmd = addStaticFlags(new Command8("refresh")).description("Clear semconv cache").action(async function() {
5528
+ await runSemconvRefresh(staticFlagsFromOpts(this.optsWithGlobals()));
5529
+ });
5530
+ semconvCmd.addCommand(listCmd);
5531
+ semconvCmd.addCommand(getCmd);
5532
+ semconvCmd.addCommand(refreshCmd);
5533
+ program.addCommand(semconvCmd);
5534
+ }
5535
+
5536
+ // src/commands/investigate/instrumentation.ts
5537
+ import {
5538
+ scoreSpan,
5539
+ suggestInstrumentationFixes,
5540
+ buildInstrumentationGuide
5541
+ } from "autotel-mcp";
5542
+ import { Command as Command9 } from "commander";
5543
+ import * as fs7 from "fs";
5544
+ function readSpanFromStdinOrFile(spanFile) {
5545
+ let raw;
5546
+ if (spanFile) {
5547
+ raw = fs7.readFileSync(spanFile, "utf8");
5548
+ } else {
5549
+ raw = fs7.readFileSync(0, "utf8");
5550
+ }
5551
+ const parsed = JSON.parse(raw);
5552
+ if (typeof parsed.operationName !== "string" || typeof parsed.serviceName !== "string" || typeof parsed.tags !== "object" || typeof parsed.hasError !== "boolean") {
5553
+ throw new AutotelError({
5554
+ type: "validation",
5555
+ code: "AUTOTEL_E_INVALID_INPUT",
5556
+ message: "score expects JSON with operationName, serviceName, tags, hasError",
5557
+ retryable: false,
5558
+ expected: {
5559
+ shape: {
5560
+ operationName: "string",
5561
+ serviceName: "string",
5562
+ tags: "Record<string, string | number | boolean>",
5563
+ hasError: "boolean"
5564
+ }
5565
+ }
5566
+ });
5567
+ }
5568
+ return parsed;
5569
+ }
5570
+ async function runScoreSpan(flags) {
5571
+ await runStatic("score", flags, async () => {
5572
+ const span = readSpanFromStdinOrFile(flags.spanFile);
5573
+ const result = scoreSpan(span);
5574
+ const suggestions = suggestInstrumentationFixes(span);
5575
+ return { ...result, suggestions };
5576
+ });
5577
+ }
5578
+ async function runScoreExplain(flags) {
5579
+ await runStatic("score explain", flags, async () => ({
5580
+ guide: buildInstrumentationGuide()
5581
+ }));
5582
+ }
5583
+ function registerScoreCommands(program) {
5584
+ const scoreCmd = addStaticFlags(new Command9("score")).description("Score a span for instrumentation quality (JSON)").option("--span-file <path>", "Read span JSON from file (default: stdin)").action(async function() {
5585
+ const o = this.optsWithGlobals();
5586
+ await runScoreSpan({
5587
+ ...staticFlagsFromOpts(o),
5588
+ spanFile: o.spanFile
5589
+ });
5590
+ });
5591
+ const explainCmd = addStaticFlags(new Command9("explain")).description("Explain the instrumentation scoring rubric").action(async function() {
5592
+ await runScoreExplain(staticFlagsFromOpts(this.optsWithGlobals()));
5593
+ });
5594
+ scoreCmd.addCommand(explainCmd);
5595
+ program.addCommand(scoreCmd);
5596
+ }
5597
+
5598
+ // src/commands/investigate/collector.ts
5599
+ import {
5600
+ validateOtlpReceiverConfig,
5601
+ suggestCollectorConfig,
5602
+ buildCollectorGuide,
5603
+ listCollectorVersions,
5604
+ listCollectorComponents,
5605
+ getCollectorComponentSchema,
5606
+ getCollectorComponentReadme,
5607
+ validateCollectorComponentConfig,
5608
+ refreshCollectorCatalog,
5609
+ resolveCollectorVersion
5610
+ } from "autotel-mcp";
5611
+ import { Command as Command10 } from "commander";
5612
+ import * as fs8 from "fs";
5613
+ function readJsonFromStdinOrFile(file) {
5614
+ const raw = file ? fs8.readFileSync(file, "utf8") : fs8.readFileSync(0, "utf8");
5615
+ return JSON.parse(raw);
5616
+ }
5617
+ function assertVersion(version) {
5618
+ if (version === void 0) return;
5619
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
5620
+ throw new AutotelError({
5621
+ type: "validation",
5622
+ code: "AUTOTEL_E_INVALID_INPUT",
5623
+ message: `--version must be semver (got "${version}")`,
5624
+ retryable: false
5625
+ });
5626
+ }
5627
+ }
5628
+ async function runCollectorValidate(flags) {
5629
+ await runStatic("collector validate", flags, async () => {
5630
+ const config = readJsonFromStdinOrFile(flags.configFile);
5631
+ return validateOtlpReceiverConfig(config);
5632
+ });
5633
+ }
5634
+ async function runCollectorSuggest(flags) {
5635
+ await runStatic("collector suggest", flags, async () => ({
5636
+ suggestion: suggestCollectorConfig()
5637
+ }));
5638
+ }
5639
+ async function runCollectorExplain(flags) {
5640
+ await runStatic("collector explain", flags, async () => ({
5641
+ guide: buildCollectorGuide()
5642
+ }));
5643
+ }
5644
+ async function runCollectorVersions(flags) {
5645
+ await runStatic("collector versions", flags, async () => ({
5646
+ versions: await listCollectorVersions()
5647
+ }));
5648
+ }
5649
+ async function runCollectorComponents(flags) {
5650
+ await runStatic("collector components", flags, async () => {
5651
+ assertVersion(flags.version);
5652
+ const resolvedVersion = await resolveCollectorVersion(flags.version);
5653
+ const components = await listCollectorComponents(resolvedVersion);
5654
+ if (flags.kind) {
5655
+ return {
5656
+ version: resolvedVersion,
5657
+ kind: flags.kind,
5658
+ components: components[flags.kind]
5659
+ };
5660
+ }
5661
+ return { version: resolvedVersion, components };
5662
+ });
5663
+ }
5664
+ async function runCollectorSchema(flags) {
5665
+ await runStatic("collector schema", flags, async () => {
5666
+ assertVersion(flags.version);
5667
+ const resolvedVersion = await resolveCollectorVersion(flags.version);
5668
+ const schema = await getCollectorComponentSchema(
5669
+ flags.kind,
5670
+ flags.name,
5671
+ resolvedVersion
5672
+ );
5673
+ return { version: resolvedVersion, kind: flags.kind, name: flags.name, schema };
5674
+ });
5675
+ }
5676
+ async function runCollectorReadme(flags) {
5677
+ await runStatic("collector readme", flags, async () => {
5678
+ assertVersion(flags.version);
5679
+ const resolvedVersion = await resolveCollectorVersion(flags.version);
5680
+ const readme = await getCollectorComponentReadme(
5681
+ flags.kind,
5682
+ flags.name,
5683
+ resolvedVersion
5684
+ );
5685
+ return { version: resolvedVersion, kind: flags.kind, name: flags.name, readme };
5686
+ });
5687
+ }
5688
+ async function runCollectorValidateComponent(flags) {
5689
+ await runStatic("collector validate-component", flags, async () => {
5690
+ assertVersion(flags.version);
5691
+ const resolvedVersion = await resolveCollectorVersion(flags.version);
5692
+ const config = readJsonFromStdinOrFile(flags.configFile);
5693
+ const result = await validateCollectorComponentConfig({
5694
+ kind: flags.kind,
5695
+ name: flags.name,
5696
+ version: resolvedVersion,
5697
+ config
5698
+ });
5699
+ return { version: resolvedVersion, kind: flags.kind, name: flags.name, ...result };
5700
+ });
5701
+ }
5702
+ async function runCollectorRefresh(flags) {
5703
+ await runStatic(
5704
+ "collector refresh",
5705
+ flags,
5706
+ async () => refreshCollectorCatalog()
5707
+ );
5708
+ }
5709
+ function registerCollectorCommands(program) {
5710
+ const collectorCmd = new Command10("collector").description(
5711
+ "OpenTelemetry Collector config + schema commands (JSON)"
5712
+ );
5713
+ const validateCmd = addStaticFlags(new Command10("validate")).description("Validate an OTLP receiver config fragment").option("--config-file <path>", "Read JSON config (default: stdin)").action(async function() {
5714
+ const o = this.optsWithGlobals();
5715
+ await runCollectorValidate({
5716
+ ...staticFlagsFromOpts(o),
5717
+ configFile: o.configFile
5718
+ });
5719
+ });
5720
+ const suggestCmd = addStaticFlags(new Command10("suggest")).description("Print a minimal OTLP receiver config").action(async function() {
5721
+ await runCollectorSuggest(staticFlagsFromOpts(this.optsWithGlobals()));
5722
+ });
5723
+ const explainCmd = addStaticFlags(new Command10("explain")).description("Explain OTLP receiver config shape + defaults").action(async function() {
5724
+ await runCollectorExplain(staticFlagsFromOpts(this.optsWithGlobals()));
5725
+ });
5726
+ const versionsCmd = addStaticFlags(new Command10("versions")).description("List supported collector schema versions").action(async function() {
5727
+ await runCollectorVersions(staticFlagsFromOpts(this.optsWithGlobals()));
5728
+ });
5729
+ const componentsCmd = addStaticFlags(new Command10("components")).description("List components for a version (optionally filter by kind)").option("--version <semver>", "Collector version (e.g. 0.110.0)").option(
5730
+ "--kind <kind>",
5731
+ "receiver | processor | exporter | connector | extension"
5732
+ ).action(async function() {
5733
+ const o = this.optsWithGlobals();
5734
+ await runCollectorComponents({
5735
+ ...staticFlagsFromOpts(o),
5736
+ version: o.version,
5737
+ kind: o.kind
5738
+ });
5739
+ });
5740
+ const schemaCmd = addStaticFlags(new Command10("schema")).description("Get JSON Schema for a collector component").requiredOption("--kind <kind>", "Component kind").requiredOption("--name <name>", "Component name").option("--version <semver>", "Collector version").action(async function() {
5741
+ const o = this.optsWithGlobals();
5742
+ await runCollectorSchema({
5743
+ ...staticFlagsFromOpts(o),
5744
+ kind: o.kind,
5745
+ name: o.name,
5746
+ version: o.version
5747
+ });
5748
+ });
5749
+ const readmeCmd = addStaticFlags(new Command10("readme")).description("Get README for a collector component").requiredOption("--kind <kind>", "Component kind").requiredOption("--name <name>", "Component name").option("--version <semver>", "Collector version").action(async function() {
5750
+ const o = this.optsWithGlobals();
5751
+ await runCollectorReadme({
5752
+ ...staticFlagsFromOpts(o),
5753
+ kind: o.kind,
5754
+ name: o.name,
5755
+ version: o.version
5756
+ });
5757
+ });
5758
+ const validateComponentCmd = addStaticFlags(
5759
+ new Command10("validate-component")
5760
+ ).description("Validate component config against upstream schema").requiredOption("--kind <kind>", "Component kind").requiredOption("--name <name>", "Component name").option("--version <semver>", "Collector version").option("--config-file <path>", "Read JSON config (default: stdin)").action(async function() {
5761
+ const o = this.optsWithGlobals();
5762
+ await runCollectorValidateComponent({
5763
+ ...staticFlagsFromOpts(o),
5764
+ kind: o.kind,
5765
+ name: o.name,
5766
+ version: o.version,
5767
+ configFile: o.configFile
5768
+ });
5769
+ });
5770
+ const refreshCmd = addStaticFlags(new Command10("refresh")).description("Refresh in-memory collector metadata cache").action(async function() {
5771
+ await runCollectorRefresh(staticFlagsFromOpts(this.optsWithGlobals()));
5772
+ });
5773
+ collectorCmd.addCommand(validateCmd);
5774
+ collectorCmd.addCommand(suggestCmd);
5775
+ collectorCmd.addCommand(explainCmd);
5776
+ collectorCmd.addCommand(versionsCmd);
5777
+ collectorCmd.addCommand(componentsCmd);
5778
+ collectorCmd.addCommand(schemaCmd);
5779
+ collectorCmd.addCommand(readmeCmd);
5780
+ collectorCmd.addCommand(validateComponentCmd);
5781
+ collectorCmd.addCommand(refreshCmd);
5782
+ program.addCommand(collectorCmd);
5783
+ }
5784
+
3371
5785
  // src/cli.ts
3372
5786
  function createProgram() {
3373
- const program = new Command();
5787
+ const program = new Command11();
3374
5788
  program.name("autotel").description("CLI for autotel - setup wizard, diagnostics, and incremental features").version("0.1.0");
3375
5789
  const addGlobalOptions = (cmd) => {
3376
5790
  return cmd.option("--cwd <path>", "Target directory", process.cwd()).option("--verbose", "Show detailed output").option("--quiet", "Only show warnings and errors");
3377
5791
  };
3378
- const initCmd = new Command("init").description("Initialize autotel in your project").option("--dry-run", "Skip installation and print what would be done").option("--no-install", "Generate files only, skip package installation").option("--print-install-cmd", "Output the install command without running it").option("-y, --yes", "Accept defaults, non-interactive").option("--preset <name>", "Use a quick preset (e.g., node-datadog-pino)").option("--force", "Overwrite existing config (creates backup first)").option("--workspace-root", "Install at workspace root instead of package root").action(async (opts) => {
5792
+ const initCmd = new Command11("init").description("Initialize autotel in your project").option("--dry-run", "Skip installation and print what would be done").option("--no-install", "Generate files only, skip package installation").option("--print-install-cmd", "Output the install command without running it").option("-y, --yes", "Accept defaults, non-interactive").option("--preset <name>", "Use a quick preset (e.g., node-datadog-pino)").option("--force", "Overwrite existing config (creates backup first)").option("--workspace-root", "Install at workspace root instead of package root").option("--no-detect", "Skip auto-detection of installed deps").option("--detect-only", "Run detection, print the plan, write nothing").option("--plan <path>", "Apply a pre-built InitPlan JSON file").option("--input <path>", "Read InitPlan JSON from stdin (-) or a file").option("--scan-env", "Consent to reading .env / .env.local for backend detection").option("--json", "Emit machine-readable JSON").option("--output-file <path>", "Persist JSON output to this file").option("--no-secrets-in-output", "Redact secret-shaped values").option("--no-interactive", "Never prompt; fail if input would be required").action(async (opts) => {
3379
5793
  const options = {
3380
5794
  cwd: opts.cwd ?? process.cwd(),
3381
5795
  dryRun: opts.dryRun ?? false,
3382
- noInstall: opts.noInstall ?? false,
5796
+ noInstall: opts.install === false,
3383
5797
  printInstallCmd: opts.printInstallCmd ?? false,
3384
5798
  verbose: opts.verbose ?? false,
3385
5799
  quiet: opts.quiet ?? false,
3386
5800
  workspaceRoot: opts.workspaceRoot ?? false,
3387
5801
  yes: opts.yes ?? false,
3388
5802
  preset: opts.preset,
3389
- force: opts.force ?? false
5803
+ force: opts.force ?? false,
5804
+ noDetect: opts.detect === false,
5805
+ detectOnly: opts.detectOnly ?? false,
5806
+ plan: opts.plan,
5807
+ input: opts.input,
5808
+ scanEnv: opts.scanEnv ?? false,
5809
+ json: opts.json ?? false,
5810
+ outputFile: opts.outputFile,
5811
+ noSecrets: opts.secretsInOutput === false,
5812
+ noInteractive: opts.interactive === false
3390
5813
  };
3391
5814
  if (options.dryRun) {
3392
5815
  options.noInstall = true;
@@ -3396,7 +5819,7 @@ function createProgram() {
3396
5819
  });
3397
5820
  addGlobalOptions(initCmd);
3398
5821
  program.addCommand(initCmd);
3399
- const doctorCmd = new Command("doctor").description("Run diagnostics on your autotel setup").option("--json", "Output machine-readable JSON").option("--fix", "Auto-fix resolvable issues").option("--list-checks", "List all available checks").option("--env-file <path>", "Specify env file to check").action(async (opts) => {
5822
+ const doctorCmd = new Command11("doctor").description("Run diagnostics on your autotel setup").option("--json", "Output machine-readable JSON").option("--fix", "Auto-fix resolvable issues").option("--list-checks", "List all available checks").option("--env-file <path>", "Specify env file to check").action(async (opts) => {
3400
5823
  const options = {
3401
5824
  cwd: opts.cwd ?? process.cwd(),
3402
5825
  dryRun: false,
@@ -3414,7 +5837,7 @@ function createProgram() {
3414
5837
  });
3415
5838
  addGlobalOptions(doctorCmd);
3416
5839
  program.addCommand(doctorCmd);
3417
- const addCmd = new Command("add").description("Add a backend, subscriber, plugin, or platform").argument("[type]", "Preset type (backend, subscriber, plugin, platform)").argument("[name]", "Preset name (e.g., datadog, posthog, mongoose)").option("--list", "List available presets for the given type").option("--dry-run", "Skip installation and print what would be done").option("--no-install", "Generate files only, skip package installation").option("--print-install-cmd", "Output the install command without running it").option("-y, --yes", "Accept defaults, non-interactive").option("--force", "Overwrite non-CLI-owned config (creates backup first)").option("--json", "Output machine-readable JSON (for --list)").option("--workspace-root", "Install at workspace root instead of package root").action(async (type, name, opts) => {
5840
+ const addCmd = new Command11("add").description("Add a backend, subscriber, plugin, or platform").argument("[type]", "Preset type (backend, subscriber, plugin, platform)").argument("[name]", "Preset name (e.g., datadog, posthog, mongoose)").option("--list", "List available presets for the given type").option("--dry-run", "Skip installation and print what would be done").option("--no-install", "Generate files only, skip package installation").option("--print-install-cmd", "Output the install command without running it").option("-y, --yes", "Accept defaults, non-interactive").option("--force", "Overwrite non-CLI-owned config (creates backup first)").option("--json", "Output machine-readable JSON (for --list)").option("--workspace-root", "Install at workspace root instead of package root").action(async (type, name, opts) => {
3418
5841
  const options = {
3419
5842
  cwd: opts.cwd ?? process.cwd(),
3420
5843
  dryRun: opts.dryRun ?? false,
@@ -3436,8 +5859,8 @@ function createProgram() {
3436
5859
  });
3437
5860
  addGlobalOptions(addCmd);
3438
5861
  program.addCommand(addCmd);
3439
- const codemodCmd = new Command("codemod").description("Codemod commands for adopting autotel");
3440
- const traceCmd = new Command("trace").description("Wrap functions in trace() with span name from function/variable/method name").argument("<path>", "File path or glob (e.g. src/index.ts, src/**/*.ts)").option("--dry-run", "Print changes without writing files").option("--name-pattern <pattern>", "Span name template: {name}, {file}, {path}").option("--skip <regex>...", "Skip functions whose name matches (repeatable)").option("--print-files", "Print per-file summary (wrapped count, skipped)").action(async (pathArg, opts) => {
5862
+ const codemodCmd = new Command11("codemod").description("Codemod commands for adopting autotel");
5863
+ const traceCmd = new Command11("trace").description("Wrap functions in trace() with span name from function/variable/method name").argument("<path>", "File path or glob (e.g. src/index.ts, src/**/*.ts)").option("--dry-run", "Print changes without writing files").option("--name-pattern <pattern>", "Span name template: {name}, {file}, {path}").option("--skip <regex>...", "Skip functions whose name matches (repeatable)").option("--print-files", "Print per-file summary (wrapped count, skipped)").action(async (pathArg, opts) => {
3441
5864
  const options = {
3442
5865
  cwd: opts.cwd ?? process.cwd(),
3443
5866
  dryRun: opts.dryRun ?? false,
@@ -3457,16 +5880,104 @@ function createProgram() {
3457
5880
  codemodCmd.addCommand(traceCmd);
3458
5881
  addGlobalOptions(codemodCmd);
3459
5882
  program.addCommand(codemodCmd);
5883
+ const schemaCmd = new Command11("schema").description("Print the CLI manifest as JSON (agent discovery)").option("--output-file <path>", "Persist JSON to a file").option("--no-secrets-in-output", "Redact secret-shaped values").action((opts) => {
5884
+ runSchema({ outputFile: opts.outputFile, noSecrets: opts.secretsInOutput === false });
5885
+ });
5886
+ const schemaErrorsCmd = new Command11("errors").description("Print error envelope shape + AUTOTEL_E_* codes").option("--output-file <path>", "Persist JSON to a file").action((opts) => {
5887
+ runSchemaErrors({ outputFile: opts.outputFile });
5888
+ });
5889
+ const schemaOutputsCmd = new Command11("outputs").description("Print JSON output shapes per command").option("--output-file <path>", "Persist JSON to a file").action((opts) => {
5890
+ runSchemaOutputs({ outputFile: opts.outputFile });
5891
+ });
5892
+ schemaCmd.addCommand(schemaErrorsCmd);
5893
+ schemaCmd.addCommand(schemaOutputsCmd);
5894
+ program.addCommand(schemaCmd);
5895
+ const commandsCmd = new Command11("commands").description("Print compact tool-style listing of commands").option("--output-file <path>", "Persist JSON to a file").action((opts) => {
5896
+ runCommandsListing({ outputFile: opts.outputFile });
5897
+ });
5898
+ program.addCommand(commandsCmd);
5899
+ const examplesCmd = new Command11("examples").description("Print copy-pasteable examples for a command").argument("[command]", "Command name (omit for all)").option("--output-file <path>", "Persist JSON to a file").action((name, opts) => {
5900
+ runExamples(name, { outputFile: opts.outputFile });
5901
+ });
5902
+ program.addCommand(examplesCmd);
5903
+ const versionCmd = new Command11("version").description("Print version info as JSON").option("--output-file <path>", "Persist JSON to a file").action((opts) => {
5904
+ runVersion({ outputFile: opts.outputFile });
5905
+ });
5906
+ program.addCommand(versionCmd);
5907
+ registerHealthCommands(program);
5908
+ registerDiscoveryCommands(program);
5909
+ registerQueryCommands(program);
5910
+ registerTraceCommands(program);
5911
+ registerTopologyCommands(program);
5912
+ registerDiagnoseCommands(program);
5913
+ registerCorrelateCommands(program);
5914
+ registerLlmCommands(program);
5915
+ registerSemconvCommands(program);
5916
+ registerScoreCommands(program);
5917
+ registerCollectorCommands(program);
3460
5918
  return program;
3461
5919
  }
3462
5920
  async function run() {
3463
5921
  const program = createProgram();
5922
+ program.exitOverride();
5923
+ const argvJoined = process.argv.slice(2).join(" ");
5924
+ const isJsonOnly = process.argv.includes("--json") || /^(schema|commands|examples|version|health|capabilities|discover|query|trace|diagnose|topology|correlate|llm|semconv|score|collector)\b/.test(
5925
+ argvJoined
5926
+ );
5927
+ if (isJsonOnly) {
5928
+ program.configureOutput({ writeErr: () => {
5929
+ } });
5930
+ }
5931
+ const stack = [...program.commands];
5932
+ while (stack.length > 0) {
5933
+ const cmd = stack.pop();
5934
+ cmd.exitOverride();
5935
+ if (isJsonOnly) {
5936
+ cmd.configureOutput({ writeErr: () => {
5937
+ } });
5938
+ }
5939
+ stack.push(...cmd.commands);
5940
+ }
3464
5941
  await program.parseAsync(process.argv);
3465
5942
  }
3466
5943
 
5944
+ // src/lib/commander-error.ts
5945
+ function commanderErrorToAutotel(error2) {
5946
+ if (error2 === null || typeof error2 !== "object" || !("code" in error2) || typeof error2.code !== "string" || !error2.code.startsWith("commander.")) {
5947
+ return null;
5948
+ }
5949
+ const ce = error2;
5950
+ if (ce.code === "commander.help" || ce.code === "commander.helpDisplayed" || ce.code === "commander.version") {
5951
+ process.exit(ce.exitCode ?? 0);
5952
+ }
5953
+ return new AutotelError({
5954
+ type: "validation",
5955
+ code: "AUTOTEL_E_INVALID_FLAG",
5956
+ message: ce.message,
5957
+ retryable: false,
5958
+ expected: { commanderCode: ce.code }
5959
+ });
5960
+ }
5961
+
3467
5962
  // src/index.ts
5963
+ function jsonModeRequested() {
5964
+ return process.argv.includes("--json");
5965
+ }
3468
5966
  run().catch((error2) => {
3469
- console.error("Error:", error2 instanceof Error ? error2.message : error2);
3470
- process.exit(1);
5967
+ const err = commanderErrorToAutotel(error2) ?? toAutotelError(error2);
5968
+ const isJson = jsonModeRequested() || // schema/commands/examples/version + investigate commands are JSON-only
5969
+ /^(schema|commands|examples|version|health|capabilities|discover|query|trace|diagnose|topology|correlate|llm|semconv|score|collector)\b/.test(
5970
+ process.argv.slice(2).join(" ")
5971
+ );
5972
+ if (isJson) {
5973
+ printJson(err.toEnvelope());
5974
+ } else {
5975
+ process.stderr.write(
5976
+ `Error [${err.code}]: ${err.message}
5977
+ ` + (err.fix !== void 0 ? `Fix: ${err.fix}
5978
+ ` : "")
5979
+ );
5980
+ }
5981
+ process.exit(exitCodeForError(err));
3471
5982
  });
3472
5983
  //# sourceMappingURL=index.js.map