@peakinfer/cli 1.0.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.env.example +6 -0
  3. package/.github/workflows/peakinfer.yml +64 -0
  4. package/CHANGELOG.md +31 -0
  5. package/LICENSE +190 -0
  6. package/README.md +335 -0
  7. package/data/inferencemax.json +274 -0
  8. package/dist/agent-analyzer.d.ts +45 -0
  9. package/dist/agent-analyzer.d.ts.map +1 -0
  10. package/dist/agent-analyzer.js +374 -0
  11. package/dist/agent-analyzer.js.map +1 -0
  12. package/dist/agent.d.ts +76 -0
  13. package/dist/agent.d.ts.map +1 -0
  14. package/dist/agent.js +965 -0
  15. package/dist/agent.js.map +1 -0
  16. package/dist/agents/correlation-analyzer.d.ts +34 -0
  17. package/dist/agents/correlation-analyzer.d.ts.map +1 -0
  18. package/dist/agents/correlation-analyzer.js +261 -0
  19. package/dist/agents/correlation-analyzer.js.map +1 -0
  20. package/dist/agents/index.d.ts +91 -0
  21. package/dist/agents/index.d.ts.map +1 -0
  22. package/dist/agents/index.js +111 -0
  23. package/dist/agents/index.js.map +1 -0
  24. package/dist/agents/runtime-analyzer.d.ts +38 -0
  25. package/dist/agents/runtime-analyzer.d.ts.map +1 -0
  26. package/dist/agents/runtime-analyzer.js +244 -0
  27. package/dist/agents/runtime-analyzer.js.map +1 -0
  28. package/dist/analysis-types.d.ts +500 -0
  29. package/dist/analysis-types.d.ts.map +1 -0
  30. package/dist/analysis-types.js +11 -0
  31. package/dist/analysis-types.js.map +1 -0
  32. package/dist/analytics.d.ts +25 -0
  33. package/dist/analytics.d.ts.map +1 -0
  34. package/dist/analytics.js +94 -0
  35. package/dist/analytics.js.map +1 -0
  36. package/dist/analyzer.d.ts +48 -0
  37. package/dist/analyzer.d.ts.map +1 -0
  38. package/dist/analyzer.js +547 -0
  39. package/dist/analyzer.js.map +1 -0
  40. package/dist/artifacts.d.ts +44 -0
  41. package/dist/artifacts.d.ts.map +1 -0
  42. package/dist/artifacts.js +165 -0
  43. package/dist/artifacts.js.map +1 -0
  44. package/dist/benchmarks/index.d.ts +88 -0
  45. package/dist/benchmarks/index.d.ts.map +1 -0
  46. package/dist/benchmarks/index.js +205 -0
  47. package/dist/benchmarks/index.js.map +1 -0
  48. package/dist/cli.d.ts +3 -0
  49. package/dist/cli.d.ts.map +1 -0
  50. package/dist/cli.js +427 -0
  51. package/dist/cli.js.map +1 -0
  52. package/dist/commands/ci.d.ts +19 -0
  53. package/dist/commands/ci.d.ts.map +1 -0
  54. package/dist/commands/ci.js +253 -0
  55. package/dist/commands/ci.js.map +1 -0
  56. package/dist/commands/config.d.ts +16 -0
  57. package/dist/commands/config.d.ts.map +1 -0
  58. package/dist/commands/config.js +249 -0
  59. package/dist/commands/config.js.map +1 -0
  60. package/dist/commands/demo.d.ts +15 -0
  61. package/dist/commands/demo.d.ts.map +1 -0
  62. package/dist/commands/demo.js +106 -0
  63. package/dist/commands/demo.js.map +1 -0
  64. package/dist/commands/export.d.ts +14 -0
  65. package/dist/commands/export.d.ts.map +1 -0
  66. package/dist/commands/export.js +209 -0
  67. package/dist/commands/export.js.map +1 -0
  68. package/dist/commands/history.d.ts +15 -0
  69. package/dist/commands/history.d.ts.map +1 -0
  70. package/dist/commands/history.js +389 -0
  71. package/dist/commands/history.js.map +1 -0
  72. package/dist/commands/template.d.ts +14 -0
  73. package/dist/commands/template.d.ts.map +1 -0
  74. package/dist/commands/template.js +341 -0
  75. package/dist/commands/template.js.map +1 -0
  76. package/dist/commands/validate-map.d.ts +12 -0
  77. package/dist/commands/validate-map.d.ts.map +1 -0
  78. package/dist/commands/validate-map.js +274 -0
  79. package/dist/commands/validate-map.js.map +1 -0
  80. package/dist/commands/whatif.d.ts +17 -0
  81. package/dist/commands/whatif.d.ts.map +1 -0
  82. package/dist/commands/whatif.js +206 -0
  83. package/dist/commands/whatif.js.map +1 -0
  84. package/dist/comparison.d.ts +38 -0
  85. package/dist/comparison.d.ts.map +1 -0
  86. package/dist/comparison.js +223 -0
  87. package/dist/comparison.js.map +1 -0
  88. package/dist/config.d.ts +42 -0
  89. package/dist/config.d.ts.map +1 -0
  90. package/dist/config.js +158 -0
  91. package/dist/config.js.map +1 -0
  92. package/dist/connectors/helicone.d.ts +9 -0
  93. package/dist/connectors/helicone.d.ts.map +1 -0
  94. package/dist/connectors/helicone.js +106 -0
  95. package/dist/connectors/helicone.js.map +1 -0
  96. package/dist/connectors/index.d.ts +37 -0
  97. package/dist/connectors/index.d.ts.map +1 -0
  98. package/dist/connectors/index.js +65 -0
  99. package/dist/connectors/index.js.map +1 -0
  100. package/dist/connectors/langsmith.d.ts +9 -0
  101. package/dist/connectors/langsmith.d.ts.map +1 -0
  102. package/dist/connectors/langsmith.js +122 -0
  103. package/dist/connectors/langsmith.js.map +1 -0
  104. package/dist/connectors/types.d.ts +83 -0
  105. package/dist/connectors/types.d.ts.map +1 -0
  106. package/dist/connectors/types.js +98 -0
  107. package/dist/connectors/types.js.map +1 -0
  108. package/dist/cost-estimator.d.ts +46 -0
  109. package/dist/cost-estimator.d.ts.map +1 -0
  110. package/dist/cost-estimator.js +104 -0
  111. package/dist/cost-estimator.js.map +1 -0
  112. package/dist/costs.d.ts +57 -0
  113. package/dist/costs.d.ts.map +1 -0
  114. package/dist/costs.js +251 -0
  115. package/dist/costs.js.map +1 -0
  116. package/dist/counterfactuals.d.ts +29 -0
  117. package/dist/counterfactuals.d.ts.map +1 -0
  118. package/dist/counterfactuals.js +448 -0
  119. package/dist/counterfactuals.js.map +1 -0
  120. package/dist/enhancement-prompts.d.ts +41 -0
  121. package/dist/enhancement-prompts.d.ts.map +1 -0
  122. package/dist/enhancement-prompts.js +88 -0
  123. package/dist/enhancement-prompts.js.map +1 -0
  124. package/dist/envelopes.d.ts +20 -0
  125. package/dist/envelopes.d.ts.map +1 -0
  126. package/dist/envelopes.js +790 -0
  127. package/dist/envelopes.js.map +1 -0
  128. package/dist/format-normalizer.d.ts +71 -0
  129. package/dist/format-normalizer.d.ts.map +1 -0
  130. package/dist/format-normalizer.js +1331 -0
  131. package/dist/format-normalizer.js.map +1 -0
  132. package/dist/history.d.ts +79 -0
  133. package/dist/history.d.ts.map +1 -0
  134. package/dist/history.js +313 -0
  135. package/dist/history.js.map +1 -0
  136. package/dist/html.d.ts +11 -0
  137. package/dist/html.d.ts.map +1 -0
  138. package/dist/html.js +463 -0
  139. package/dist/html.js.map +1 -0
  140. package/dist/impact.d.ts +42 -0
  141. package/dist/impact.d.ts.map +1 -0
  142. package/dist/impact.js +443 -0
  143. package/dist/impact.js.map +1 -0
  144. package/dist/index.d.ts +26 -0
  145. package/dist/index.d.ts.map +1 -0
  146. package/dist/index.js +34 -0
  147. package/dist/index.js.map +1 -0
  148. package/dist/insights.d.ts +5 -0
  149. package/dist/insights.d.ts.map +1 -0
  150. package/dist/insights.js +271 -0
  151. package/dist/insights.js.map +1 -0
  152. package/dist/joiner.d.ts +9 -0
  153. package/dist/joiner.d.ts.map +1 -0
  154. package/dist/joiner.js +247 -0
  155. package/dist/joiner.js.map +1 -0
  156. package/dist/orchestrator.d.ts +34 -0
  157. package/dist/orchestrator.d.ts.map +1 -0
  158. package/dist/orchestrator.js +827 -0
  159. package/dist/orchestrator.js.map +1 -0
  160. package/dist/pdf.d.ts +26 -0
  161. package/dist/pdf.d.ts.map +1 -0
  162. package/dist/pdf.js +84 -0
  163. package/dist/pdf.js.map +1 -0
  164. package/dist/prediction.d.ts +33 -0
  165. package/dist/prediction.d.ts.map +1 -0
  166. package/dist/prediction.js +316 -0
  167. package/dist/prediction.js.map +1 -0
  168. package/dist/prompts/loader.d.ts +38 -0
  169. package/dist/prompts/loader.d.ts.map +1 -0
  170. package/dist/prompts/loader.js +60 -0
  171. package/dist/prompts/loader.js.map +1 -0
  172. package/dist/renderer.d.ts +64 -0
  173. package/dist/renderer.d.ts.map +1 -0
  174. package/dist/renderer.js +923 -0
  175. package/dist/renderer.js.map +1 -0
  176. package/dist/runid.d.ts +57 -0
  177. package/dist/runid.d.ts.map +1 -0
  178. package/dist/runid.js +199 -0
  179. package/dist/runid.js.map +1 -0
  180. package/dist/runtime.d.ts +29 -0
  181. package/dist/runtime.d.ts.map +1 -0
  182. package/dist/runtime.js +366 -0
  183. package/dist/runtime.js.map +1 -0
  184. package/dist/scanner.d.ts +11 -0
  185. package/dist/scanner.d.ts.map +1 -0
  186. package/dist/scanner.js +426 -0
  187. package/dist/scanner.js.map +1 -0
  188. package/dist/templates.d.ts +120 -0
  189. package/dist/templates.d.ts.map +1 -0
  190. package/dist/templates.js +429 -0
  191. package/dist/templates.js.map +1 -0
  192. package/dist/tools/index.d.ts +153 -0
  193. package/dist/tools/index.d.ts.map +1 -0
  194. package/dist/tools/index.js +177 -0
  195. package/dist/tools/index.js.map +1 -0
  196. package/dist/types.d.ts +3647 -0
  197. package/dist/types.d.ts.map +1 -0
  198. package/dist/types.js +703 -0
  199. package/dist/types.js.map +1 -0
  200. package/dist/version.d.ts +7 -0
  201. package/dist/version.d.ts.map +1 -0
  202. package/dist/version.js +23 -0
  203. package/dist/version.js.map +1 -0
  204. package/docs/demo-guide.md +423 -0
  205. package/docs/events-format.md +295 -0
  206. package/docs/inferencemap-spec.md +344 -0
  207. package/docs/migration-v2.md +293 -0
  208. package/fixtures/demo/precomputed.json +142 -0
  209. package/fixtures/demo-project/README.md +52 -0
  210. package/fixtures/demo-project/ai-service.ts +65 -0
  211. package/fixtures/demo-project/sample-events.jsonl +15 -0
  212. package/fixtures/demo-project/src/ai-service.ts +128 -0
  213. package/fixtures/demo-project/src/llm-client.ts +155 -0
  214. package/package.json +65 -0
  215. package/prompts/agent-analyzer.yaml +47 -0
  216. package/prompts/ci-gate.yaml +98 -0
  217. package/prompts/correlation-analyzer.yaml +178 -0
  218. package/prompts/format-normalizer.yaml +46 -0
  219. package/prompts/peak-performance.yaml +180 -0
  220. package/prompts/pr-comment.yaml +111 -0
  221. package/prompts/runtime-analyzer.yaml +189 -0
  222. package/prompts/unified-analyzer.yaml +241 -0
  223. package/schemas/inference-map.v0.1.json +215 -0
  224. package/scripts/benchmark.ts +394 -0
  225. package/scripts/demo-v1.5.sh +158 -0
  226. package/scripts/sync-from-site.sh +197 -0
  227. package/scripts/validate-sync.sh +178 -0
  228. package/src/agent-analyzer.ts +481 -0
  229. package/src/agent.ts +1232 -0
  230. package/src/agents/correlation-analyzer.ts +353 -0
  231. package/src/agents/index.ts +235 -0
  232. package/src/agents/runtime-analyzer.ts +343 -0
  233. package/src/analysis-types.ts +558 -0
  234. package/src/analytics.ts +100 -0
  235. package/src/analyzer.ts +692 -0
  236. package/src/artifacts.ts +218 -0
  237. package/src/benchmarks/index.ts +309 -0
  238. package/src/cli.ts +503 -0
  239. package/src/commands/ci.ts +336 -0
  240. package/src/commands/config.ts +288 -0
  241. package/src/commands/demo.ts +175 -0
  242. package/src/commands/export.ts +297 -0
  243. package/src/commands/history.ts +425 -0
  244. package/src/commands/template.ts +385 -0
  245. package/src/commands/validate-map.ts +324 -0
  246. package/src/commands/whatif.ts +272 -0
  247. package/src/comparison.ts +283 -0
  248. package/src/config.ts +188 -0
  249. package/src/connectors/helicone.ts +164 -0
  250. package/src/connectors/index.ts +93 -0
  251. package/src/connectors/langsmith.ts +179 -0
  252. package/src/connectors/types.ts +180 -0
  253. package/src/cost-estimator.ts +146 -0
  254. package/src/costs.ts +347 -0
  255. package/src/counterfactuals.ts +516 -0
  256. package/src/enhancement-prompts.ts +118 -0
  257. package/src/envelopes.ts +814 -0
  258. package/src/format-normalizer.ts +1486 -0
  259. package/src/history.ts +400 -0
  260. package/src/html.ts +512 -0
  261. package/src/impact.ts +522 -0
  262. package/src/index.ts +83 -0
  263. package/src/insights.ts +341 -0
  264. package/src/joiner.ts +289 -0
  265. package/src/orchestrator.ts +1015 -0
  266. package/src/pdf.ts +110 -0
  267. package/src/prediction.ts +392 -0
  268. package/src/prompts/loader.ts +88 -0
  269. package/src/renderer.ts +1045 -0
  270. package/src/runid.ts +261 -0
  271. package/src/runtime.ts +450 -0
  272. package/src/scanner.ts +508 -0
  273. package/src/templates.ts +561 -0
  274. package/src/tools/index.ts +214 -0
  275. package/src/types.ts +873 -0
  276. package/src/version.ts +24 -0
  277. package/templates/context-accumulation.yaml +23 -0
  278. package/templates/cost-concentration.yaml +20 -0
  279. package/templates/dead-code.yaml +20 -0
  280. package/templates/latency-explainer.yaml +23 -0
  281. package/templates/optimizations/ab-testing-framework.yaml +74 -0
  282. package/templates/optimizations/api-gateway-optimization.yaml +81 -0
  283. package/templates/optimizations/api-model-routing-strategy.yaml +126 -0
  284. package/templates/optimizations/auto-scaling-optimization.yaml +85 -0
  285. package/templates/optimizations/batch-utilization-diagnostic.yaml +142 -0
  286. package/templates/optimizations/comprehensive-apm.yaml +76 -0
  287. package/templates/optimizations/context-window-optimization.yaml +91 -0
  288. package/templates/optimizations/cost-sensitive-batch-processing.yaml +77 -0
  289. package/templates/optimizations/distributed-training-optimization.yaml +77 -0
  290. package/templates/optimizations/document-analysis-edge.yaml +77 -0
  291. package/templates/optimizations/document-pipeline-optimization.yaml +78 -0
  292. package/templates/optimizations/domain-specific-distillation.yaml +78 -0
  293. package/templates/optimizations/error-handling-optimization.yaml +76 -0
  294. package/templates/optimizations/gptq-4bit-quantization.yaml +96 -0
  295. package/templates/optimizations/long-context-memory-management.yaml +78 -0
  296. package/templates/optimizations/max-tokens-optimization.yaml +76 -0
  297. package/templates/optimizations/memory-bandwidth-optimization.yaml +73 -0
  298. package/templates/optimizations/multi-framework-resilience.yaml +75 -0
  299. package/templates/optimizations/multi-tenant-optimization.yaml +75 -0
  300. package/templates/optimizations/prompt-caching-optimization.yaml +143 -0
  301. package/templates/optimizations/pytorch-to-onnx-migration.yaml +109 -0
  302. package/templates/optimizations/quality-monitoring.yaml +74 -0
  303. package/templates/optimizations/realtime-budget-controls.yaml +74 -0
  304. package/templates/optimizations/realtime-latency-optimization.yaml +74 -0
  305. package/templates/optimizations/sglang-concurrency-optimization.yaml +78 -0
  306. package/templates/optimizations/smart-model-routing.yaml +96 -0
  307. package/templates/optimizations/streaming-batch-selection.yaml +167 -0
  308. package/templates/optimizations/system-prompt-optimization.yaml +75 -0
  309. package/templates/optimizations/tensorrt-llm-performance.yaml +77 -0
  310. package/templates/optimizations/vllm-high-throughput-optimization.yaml +93 -0
  311. package/templates/optimizations/vllm-migration-memory-bound.yaml +78 -0
  312. package/templates/overpowered-extraction.yaml +32 -0
  313. package/templates/overpowered-model.yaml +31 -0
  314. package/templates/prompt-bloat.yaml +24 -0
  315. package/templates/retry-explosion.yaml +28 -0
  316. package/templates/schema/insight.schema.json +113 -0
  317. package/templates/schema/optimization.schema.json +180 -0
  318. package/templates/streaming-drift.yaml +30 -0
  319. package/templates/throughput-gap.yaml +21 -0
  320. package/templates/token-underutilization.yaml +28 -0
  321. package/templates/untested-fallback.yaml +21 -0
  322. package/tests/accuracy/drift-detection.test.ts +184 -0
  323. package/tests/accuracy/false-positives.test.ts +166 -0
  324. package/tests/accuracy/templates.test.ts +205 -0
  325. package/tests/action/commands.test.ts +125 -0
  326. package/tests/action/comments.test.ts +347 -0
  327. package/tests/cli.test.ts +203 -0
  328. package/tests/comparison.test.ts +309 -0
  329. package/tests/correlation-analyzer.test.ts +534 -0
  330. package/tests/counterfactuals.test.ts +347 -0
  331. package/tests/fixtures/events/missing-id.jsonl +1 -0
  332. package/tests/fixtures/events/missing-input.jsonl +1 -0
  333. package/tests/fixtures/events/missing-latency.jsonl +1 -0
  334. package/tests/fixtures/events/missing-model.jsonl +1 -0
  335. package/tests/fixtures/events/missing-output.jsonl +1 -0
  336. package/tests/fixtures/events/missing-provider.jsonl +1 -0
  337. package/tests/fixtures/events/missing-ts.jsonl +1 -0
  338. package/tests/fixtures/events/valid.csv +3 -0
  339. package/tests/fixtures/events/valid.json +1 -0
  340. package/tests/fixtures/events/valid.jsonl +2 -0
  341. package/tests/fixtures/events/with-callsite.jsonl +1 -0
  342. package/tests/fixtures/events/with-intent.jsonl +1 -0
  343. package/tests/fixtures/events/wrong-type.jsonl +1 -0
  344. package/tests/fixtures/repos/empty/.gitkeep +0 -0
  345. package/tests/fixtures/repos/hybrid-router/router.py +35 -0
  346. package/tests/fixtures/repos/saas-anthropic/agent.ts +27 -0
  347. package/tests/fixtures/repos/saas-openai/assistant.js +33 -0
  348. package/tests/fixtures/repos/saas-openai/client.py +26 -0
  349. package/tests/fixtures/repos/self-hosted-vllm/inference.py +22 -0
  350. package/tests/github-action.test.ts +292 -0
  351. package/tests/insights.test.ts +878 -0
  352. package/tests/joiner.test.ts +168 -0
  353. package/tests/performance/action-latency.test.ts +132 -0
  354. package/tests/performance/benchmark.test.ts +189 -0
  355. package/tests/performance/cli-latency.test.ts +102 -0
  356. package/tests/pr-comment.test.ts +313 -0
  357. package/tests/prediction.test.ts +296 -0
  358. package/tests/runtime-analyzer.test.ts +375 -0
  359. package/tests/runtime.test.ts +205 -0
  360. package/tests/scanner.test.ts +122 -0
  361. package/tests/template-conformance.test.ts +526 -0
  362. package/tests/unit/cost-calculator.test.ts +303 -0
  363. package/tests/unit/credits.test.ts +180 -0
  364. package/tests/unit/inference-map.test.ts +276 -0
  365. package/tests/unit/schema.test.ts +300 -0
  366. package/tsconfig.json +20 -0
  367. package/vitest.config.ts +14 -0
@@ -0,0 +1,425 @@
1
+ /**
2
+ * History Commands (v1.6)
3
+ *
4
+ * CLI commands for viewing and managing analysis history:
5
+ * - list: List past analysis runs
6
+ * - show: Show details of a specific run
7
+ * - export: Export history to JSON
8
+ * - prune: Remove old runs
9
+ */
10
+
11
+ import { Command } from 'commander';
12
+ import { writeFileSync } from 'fs';
13
+ import { listRuns, loadRun, pruneHistory, getHistoryDir, deleteRun, clearAllHistory } from '../history.js';
14
+ import { compareSnapshots, formatComparisonSummary, hasSignificantChanges, type AnalysisSnapshot } from '../comparison.js';
15
+ import type { HistoryManifest } from '../types.js';
16
+
17
+ // =============================================================================
18
+ // HELPERS
19
+ // =============================================================================
20
+
21
+ /**
22
+ * Format timestamp for display
23
+ */
24
+ function formatTimestamp(iso: string): string {
25
+ const date = new Date(iso);
26
+ return date.toLocaleString();
27
+ }
28
+
29
+ /**
30
+ * Format relative time
31
+ */
32
+ function formatRelativeTime(iso: string): string {
33
+ const date = new Date(iso);
34
+ const now = new Date();
35
+ const diffMs = now.getTime() - date.getTime();
36
+
37
+ const seconds = Math.floor(diffMs / 1000);
38
+ const minutes = Math.floor(seconds / 60);
39
+ const hours = Math.floor(minutes / 60);
40
+ const days = Math.floor(hours / 24);
41
+
42
+ if (days > 0) return `${days}d ago`;
43
+ if (hours > 0) return `${hours}h ago`;
44
+ if (minutes > 0) return `${minutes}m ago`;
45
+ return 'just now';
46
+ }
47
+
48
+ /**
49
+ * Format analysis type with indicator
50
+ */
51
+ function formatAnalysisType(type: string): string {
52
+ return type;
53
+ }
54
+
55
+ /**
56
+ * Display history list
57
+ */
58
+ function displayHistoryList(runs: HistoryManifest[]): void {
59
+ if (runs.length === 0) {
60
+ console.log('\nNo analysis history found.');
61
+ console.log('Run "peakinfer analyze ." to create your first analysis.\n');
62
+ return;
63
+ }
64
+
65
+ console.log(`\n${runs.length} analysis run${runs.length !== 1 ? 's' : ''} in history:\n`);
66
+
67
+ // Table header
68
+ console.log(' Run ID Type Points When Duration');
69
+ console.log(' ─'.repeat(35));
70
+
71
+ for (const run of runs) {
72
+ const type = formatAnalysisType(run.analysisType).padEnd(10);
73
+ const points = String(run.inferencePointCount).padStart(6);
74
+ const when = formatRelativeTime(run.timestamp).padEnd(12);
75
+ const duration = run.durationMs ? `${(run.durationMs / 1000).toFixed(1)}s` : '-';
76
+
77
+ console.log(` ${run.runId.slice(0, 18).padEnd(20)} ${type} ${points} ${when} ${duration}`);
78
+ }
79
+
80
+ console.log('');
81
+ console.log('Use "peakinfer history show <runId>" for details.');
82
+ console.log('');
83
+ }
84
+
85
+ /**
86
+ * Display run details
87
+ */
88
+ function displayRunDetails(runId: string, manifest: HistoryManifest): void {
89
+ console.log(`\nAnalysis Run: ${runId}`);
90
+ console.log('═'.repeat(60));
91
+
92
+ console.log(` Timestamp: ${formatTimestamp(manifest.timestamp)}`);
93
+ console.log(` Path: ${manifest.path}`);
94
+ console.log(` Type: ${formatAnalysisType(manifest.analysisType)}`);
95
+ console.log(` Version: ${manifest.version}`);
96
+ console.log('');
97
+
98
+ console.log('Metrics:');
99
+ console.log(` Inference Points: ${manifest.inferencePointCount}`);
100
+ if (manifest.eventCount !== undefined) {
101
+ console.log(` Runtime Events: ${manifest.eventCount}`);
102
+ }
103
+ if (manifest.driftCount !== undefined) {
104
+ console.log(` Drift Signals: ${manifest.driftCount}`);
105
+ }
106
+ if (manifest.insightCount !== undefined) {
107
+ console.log(` Insights: ${manifest.insightCount}`);
108
+ }
109
+ if (manifest.durationMs !== undefined) {
110
+ console.log(` Duration: ${(manifest.durationMs / 1000).toFixed(2)}s`);
111
+ }
112
+
113
+ if (manifest.artifacts) {
114
+ console.log('');
115
+ console.log('Artifacts:');
116
+ if (manifest.artifacts.inferenceMap) {
117
+ console.log(` Inference Map: ${manifest.artifacts.inferenceMap}`);
118
+ }
119
+ if (manifest.artifacts.html) {
120
+ console.log(` HTML Report: ${manifest.artifacts.html}`);
121
+ }
122
+ if (manifest.artifacts.pdf) {
123
+ console.log(` PDF Report: ${manifest.artifacts.pdf}`);
124
+ }
125
+ }
126
+
127
+ console.log('');
128
+ }
129
+
130
+ // =============================================================================
131
+ // COMMANDS
132
+ // =============================================================================
133
+
134
+ /**
135
+ * Register history commands
136
+ */
137
+ export function registerHistoryCommands(program: Command): void {
138
+ const historyCmd = program
139
+ .command('history')
140
+ .description('view and manage analysis history');
141
+
142
+ // List runs (default action)
143
+ historyCmd
144
+ .command('list', { isDefault: true })
145
+ .description('list past analysis runs')
146
+ .option('--path <path>', 'filter by analyzed path')
147
+ .option('--limit <n>', 'limit number of results', parseInt)
148
+ .action((options: { path?: string; limit?: number }) => {
149
+ try {
150
+ let runs = listRuns(options.path);
151
+
152
+ if (options.limit && options.limit > 0) {
153
+ runs = runs.slice(0, options.limit);
154
+ }
155
+
156
+ displayHistoryList(runs);
157
+ } catch (error) {
158
+ console.error('Error:', error instanceof Error ? error.message : 'Failed to load history');
159
+ process.exit(1);
160
+ }
161
+ });
162
+
163
+ // Show run details
164
+ historyCmd
165
+ .command('show')
166
+ .description('show details of a specific run')
167
+ .argument('<runId>', 'run ID to show')
168
+ .action((runId: string) => {
169
+ try {
170
+ const run = loadRun(runId);
171
+
172
+ if (!run) {
173
+ console.error(`Run not found: ${runId}`);
174
+ process.exit(1);
175
+ }
176
+
177
+ displayRunDetails(runId, run.manifest);
178
+
179
+ // Show summary of data if available
180
+ if (run.data.inferenceMap) {
181
+ console.log('Inference Map Summary:');
182
+ console.log(` Total Points: ${run.data.inferenceMap.callsites.length}`);
183
+ console.log(` Providers: ${run.data.inferenceMap.summary.providers.join(', ') || 'none detected'}`);
184
+ console.log(` Models: ${run.data.inferenceMap.summary.models.join(', ') || 'none detected'}`);
185
+ console.log('');
186
+ }
187
+ } catch (error) {
188
+ console.error('Error:', error instanceof Error ? error.message : 'Failed to load run');
189
+ process.exit(1);
190
+ }
191
+ });
192
+
193
+ // Compare two runs
194
+ historyCmd
195
+ .command('compare')
196
+ .description('compare two analysis runs')
197
+ .argument('<runId1>', 'first run ID (baseline)')
198
+ .argument('[runId2]', 'second run ID (defaults to latest)')
199
+ .option('--json', 'output as JSON')
200
+ .action((runId1: string, runId2: string | undefined, options: { json?: boolean }) => {
201
+ try {
202
+ // Load first run (baseline)
203
+ const run1 = loadRun(runId1);
204
+ if (!run1) {
205
+ console.error(`Run not found: ${runId1}`);
206
+ process.exit(1);
207
+ }
208
+
209
+ // Load second run (or latest if not specified)
210
+ let run2;
211
+ if (runId2) {
212
+ run2 = loadRun(runId2);
213
+ if (!run2) {
214
+ console.error(`Run not found: ${runId2}`);
215
+ process.exit(1);
216
+ }
217
+ } else {
218
+ // Get the most recent run (excluding run1)
219
+ const runs = listRuns();
220
+ const latestRun = runs.find(r => r.runId !== runId1);
221
+ if (!latestRun) {
222
+ console.error('No other run found to compare with.');
223
+ process.exit(1);
224
+ }
225
+ run2 = loadRun(latestRun.runId);
226
+ if (!run2) {
227
+ console.error('Failed to load latest run.');
228
+ process.exit(1);
229
+ }
230
+ }
231
+
232
+ // Build snapshots
233
+ const baseline: AnalysisSnapshot = {
234
+ runId: run1.manifest.runId,
235
+ timestamp: run1.manifest.timestamp,
236
+ callsites: run1.data.inferenceMap?.callsites || [],
237
+ insights: run1.data.insights,
238
+ };
239
+
240
+ const current: AnalysisSnapshot = {
241
+ runId: run2.manifest.runId,
242
+ timestamp: run2.manifest.timestamp,
243
+ callsites: run2.data.inferenceMap?.callsites || [],
244
+ insights: run2.data.insights,
245
+ };
246
+
247
+ // Compare
248
+ const comparison = compareSnapshots(baseline, current);
249
+
250
+ if (options.json) {
251
+ console.log(JSON.stringify(comparison, null, 2));
252
+ } else {
253
+ console.log('');
254
+ console.log(`Baseline: ${run1.manifest.runId} (${formatTimestamp(run1.manifest.timestamp)})`);
255
+ console.log(`Current: ${run2.manifest.runId} (${formatTimestamp(run2.manifest.timestamp)})`);
256
+ console.log('');
257
+ console.log(formatComparisonSummary(comparison));
258
+
259
+ // Show details for added/removed/changed
260
+ if (comparison.added.length > 0 && comparison.added.length <= 5) {
261
+ console.log('');
262
+ console.log('Added inference points:');
263
+ for (const cs of comparison.added) {
264
+ console.log(` + ${cs.file}:${cs.line} (${cs.provider || 'unknown'})`);
265
+ }
266
+ }
267
+
268
+ if (comparison.removed.length > 0 && comparison.removed.length <= 5) {
269
+ console.log('');
270
+ console.log('Removed inference points:');
271
+ for (const cs of comparison.removed) {
272
+ console.log(` - ${cs.file}:${cs.line} (${cs.provider || 'unknown'})`);
273
+ }
274
+ }
275
+
276
+ if (comparison.changed.length > 0 && comparison.changed.length <= 5) {
277
+ console.log('');
278
+ console.log('Changed inference points:');
279
+ for (const change of comparison.changed) {
280
+ const fields = change.changes.map(c => c.field).join(', ');
281
+ console.log(` ~ ${change.point.file}:${change.point.line} (${fields})`);
282
+ }
283
+ }
284
+
285
+ console.log('');
286
+ if (hasSignificantChanges(comparison)) {
287
+ console.log('Result: Changes detected - review recommended before deploy.');
288
+ } else {
289
+ console.log('Result: No significant changes.');
290
+ }
291
+ console.log('');
292
+ }
293
+ } catch (error) {
294
+ console.error('Error:', error instanceof Error ? error.message : 'Failed to compare runs');
295
+ process.exit(1);
296
+ }
297
+ });
298
+
299
+ // Export history
300
+ historyCmd
301
+ .command('export')
302
+ .description('export history to JSON file')
303
+ .option('--output <file>', 'output file path', 'peakinfer-history.json')
304
+ .option('--path <path>', 'filter by analyzed path')
305
+ .action((options: { output: string; path?: string }) => {
306
+ try {
307
+ const runs = listRuns(options.path);
308
+
309
+ if (runs.length === 0) {
310
+ console.log('No history to export.');
311
+ process.exit(0);
312
+ }
313
+
314
+ // Load full data for each run
315
+ const exportData = runs.map(manifest => {
316
+ const run = loadRun(manifest.runId);
317
+ return {
318
+ manifest,
319
+ data: run?.data || null,
320
+ };
321
+ });
322
+
323
+ const output = {
324
+ exportedAt: new Date().toISOString(),
325
+ runCount: runs.length,
326
+ runs: exportData,
327
+ };
328
+
329
+ writeFileSync(options.output, JSON.stringify(output, null, 2));
330
+ console.log(`Exported ${runs.length} run${runs.length !== 1 ? 's' : ''} to ${options.output}`);
331
+ } catch (error) {
332
+ console.error('Error:', error instanceof Error ? error.message : 'Failed to export history');
333
+ process.exit(1);
334
+ }
335
+ });
336
+
337
+ // Prune old runs
338
+ historyCmd
339
+ .command('prune')
340
+ .description('remove old runs (keeps most recent per path)')
341
+ .option('--keep <n>', 'number of runs to keep per path', parseInt, 10)
342
+ .option('--dry-run', 'show what would be deleted without deleting')
343
+ .action((options: { keep: number; dryRun?: boolean }) => {
344
+ try {
345
+ if (options.dryRun) {
346
+ // Count what would be deleted
347
+ const runs = listRuns();
348
+ const byPath = new Map<string, number>();
349
+ for (const run of runs) {
350
+ byPath.set(run.pathHash, (byPath.get(run.pathHash) || 0) + 1);
351
+ }
352
+
353
+ let totalToDelete = 0;
354
+ for (const [, count] of byPath) {
355
+ if (count > options.keep) {
356
+ totalToDelete += count - options.keep;
357
+ }
358
+ }
359
+
360
+ console.log(`Would delete ${totalToDelete} run${totalToDelete !== 1 ? 's' : ''} (keeping ${options.keep} per path)`);
361
+ } else {
362
+ const deleted = pruneHistory(options.keep);
363
+ console.log(`Pruned ${deleted} run${deleted !== 1 ? 's' : ''} from history.`);
364
+ }
365
+ } catch (error) {
366
+ console.error('Error:', error instanceof Error ? error.message : 'Failed to prune history');
367
+ process.exit(1);
368
+ }
369
+ });
370
+
371
+ // Delete specific run
372
+ historyCmd
373
+ .command('delete')
374
+ .description('delete a specific run by ID')
375
+ .argument('<runId>', 'run ID to delete')
376
+ .action((runId: string) => {
377
+ try {
378
+ const deleted = deleteRun(runId);
379
+ if (deleted) {
380
+ console.log(`Deleted run: ${runId}`);
381
+ } else {
382
+ console.error(`Run not found: ${runId}`);
383
+ process.exit(1);
384
+ }
385
+ } catch (error) {
386
+ console.error('Error:', error instanceof Error ? error.message : 'Failed to delete run');
387
+ process.exit(1);
388
+ }
389
+ });
390
+
391
+ // Clear all history
392
+ historyCmd
393
+ .command('clear')
394
+ .description('delete all history')
395
+ .option('--confirm', 'confirm deletion without prompt')
396
+ .action((options: { confirm?: boolean }) => {
397
+ try {
398
+ const runs = listRuns();
399
+ if (runs.length === 0) {
400
+ console.log('No history to clear.');
401
+ return;
402
+ }
403
+
404
+ if (!options.confirm) {
405
+ console.log(`This will delete ${runs.length} run${runs.length !== 1 ? 's' : ''}.`);
406
+ console.log('Use --confirm to proceed.');
407
+ return;
408
+ }
409
+
410
+ const deleted = clearAllHistory();
411
+ console.log(`Cleared ${deleted} run${deleted !== 1 ? 's' : ''} from history.`);
412
+ } catch (error) {
413
+ console.error('Error:', error instanceof Error ? error.message : 'Failed to clear history');
414
+ process.exit(1);
415
+ }
416
+ });
417
+
418
+ // Show history directory
419
+ historyCmd
420
+ .command('path')
421
+ .description('show history storage location')
422
+ .action(() => {
423
+ console.log(`History stored at: ${getHistoryDir()}`);
424
+ });
425
+ }