@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,561 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { parse as parseYAML } from 'yaml';
5
+ import { InsightTemplate, OptimizationTemplate } from './types.js';
6
+
7
+ // =============================================================================
8
+ // CONSTANTS
9
+ // =============================================================================
10
+
11
+ const TEMPLATES_REPO = 'https://raw.githubusercontent.com/Kalmantic/peakinfer_templates/main';
12
+ const MANIFEST_URL = `${TEMPLATES_REPO}/insights/manifest.json`;
13
+ const CACHE_DIR = '.peakinfer/cache/templates';
14
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
15
+
16
+ // Get bundled templates directory (relative to this file)
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+ const BUNDLED_DIR = join(__dirname, '..', 'templates');
20
+ const OPTIMIZATIONS_DIR = join(__dirname, '..', 'templates', 'optimizations');
21
+ const PROMPTS_DIR = join(__dirname, '..', 'prompts');
22
+
23
+ // =============================================================================
24
+ // TYPES
25
+ // =============================================================================
26
+
27
+ interface Manifest {
28
+ version: string;
29
+ templates: string[];
30
+ updated: string;
31
+ }
32
+
33
+ interface CacheMeta {
34
+ fetchedAt: number;
35
+ version: string;
36
+ }
37
+
38
+ /**
39
+ * Analysis prompt configuration loaded from YAML
40
+ * Used for LLM-based code analysis with configurable focus areas
41
+ */
42
+ export interface AnalysisPrompt {
43
+ id: string;
44
+ name: string;
45
+ version: string;
46
+ description: string;
47
+ prompt: string;
48
+ categories: string[];
49
+ defaults?: {
50
+ expensive_models?: string[];
51
+ cheap_models?: string[];
52
+ latency_critical_threshold_ms?: number;
53
+ batch_opportunity_threshold?: number;
54
+ };
55
+ }
56
+
57
+ // =============================================================================
58
+ // HELPERS
59
+ // =============================================================================
60
+
61
+ function getCacheDir(): string {
62
+ return join(process.cwd(), CACHE_DIR);
63
+ }
64
+
65
+ function getCacheMetaPath(): string {
66
+ return join(getCacheDir(), 'meta.json');
67
+ }
68
+
69
+ function isCacheValid(): boolean {
70
+ const metaPath = getCacheMetaPath();
71
+ if (!existsSync(metaPath)) return false;
72
+
73
+ try {
74
+ const meta: CacheMeta = JSON.parse(readFileSync(metaPath, 'utf-8'));
75
+ return Date.now() - meta.fetchedAt < CACHE_TTL_MS;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ function findYamlFiles(dir: string): string[] {
82
+ if (!existsSync(dir)) return [];
83
+
84
+ const results: string[] = [];
85
+ const entries = readdirSync(dir, { withFileTypes: true });
86
+
87
+ for (const entry of entries) {
88
+ const fullPath = join(dir, entry.name);
89
+ if (entry.isDirectory()) {
90
+ results.push(...findYamlFiles(fullPath));
91
+ } else if (entry.name.endsWith('.yaml')) {
92
+ results.push(fullPath);
93
+ }
94
+ }
95
+
96
+ return results;
97
+ }
98
+
99
+ function loadCachedTemplates(): InsightTemplate[] {
100
+ const cacheDir = getCacheDir();
101
+ if (!existsSync(cacheDir)) return [];
102
+
103
+ const templates: InsightTemplate[] = [];
104
+ const files = findYamlFiles(cacheDir);
105
+
106
+ for (const file of files) {
107
+ try {
108
+ const content = readFileSync(file, 'utf-8');
109
+ const parsed = parseYAML(content);
110
+ const validated = InsightTemplate.parse(parsed);
111
+ templates.push(validated);
112
+ } catch {
113
+ // Skip invalid templates
114
+ }
115
+ }
116
+
117
+ return templates;
118
+ }
119
+
120
+ function loadBundledTemplates(): InsightTemplate[] {
121
+ if (!existsSync(BUNDLED_DIR)) return [];
122
+
123
+ const templates: InsightTemplate[] = [];
124
+ const files = readdirSync(BUNDLED_DIR).filter(f => f.endsWith('.yaml'));
125
+
126
+ for (const file of files) {
127
+ try {
128
+ const content = readFileSync(join(BUNDLED_DIR, file), 'utf-8');
129
+ const parsed = parseYAML(content);
130
+ const validated = InsightTemplate.parse(parsed);
131
+ templates.push(validated);
132
+ } catch {
133
+ // Skip invalid templates
134
+ }
135
+ }
136
+
137
+ return templates;
138
+ }
139
+
140
+ async function fetchManifest(): Promise<Manifest | null> {
141
+ try {
142
+ const response = await fetch(MANIFEST_URL);
143
+ if (!response.ok) return null;
144
+ return await response.json() as Manifest;
145
+ } catch {
146
+ return null;
147
+ }
148
+ }
149
+
150
+ async function fetchTemplate(templatePath: string): Promise<string | null> {
151
+ try {
152
+ // Templates are now in insights/{category}/{name}.yaml
153
+ const url = `${TEMPLATES_REPO}/insights/${templatePath}`;
154
+ const response = await fetch(url);
155
+ if (!response.ok) return null;
156
+ return await response.text();
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ function saveToCache(templates: Map<string, string>, version: string): void {
163
+ const cacheDir = getCacheDir();
164
+ mkdirSync(cacheDir, { recursive: true });
165
+
166
+ // Save each template (handle nested paths like cost/prompt-bloat.yaml)
167
+ for (const [name, content] of templates) {
168
+ const filePath = join(cacheDir, name);
169
+ mkdirSync(dirname(filePath), { recursive: true });
170
+ writeFileSync(filePath, content);
171
+ }
172
+
173
+ // Save meta
174
+ const meta: CacheMeta = {
175
+ fetchedAt: Date.now(),
176
+ version,
177
+ };
178
+ writeFileSync(getCacheMetaPath(), JSON.stringify(meta, null, 2));
179
+ }
180
+
181
+ // =============================================================================
182
+ // PUBLIC API
183
+ // =============================================================================
184
+
185
+ export interface LoadOptions {
186
+ offline?: boolean;
187
+ }
188
+
189
+ export async function loadTemplates(opts: LoadOptions = {}): Promise<InsightTemplate[]> {
190
+ // If offline mode, skip remote fetch
191
+ if (opts.offline) {
192
+ const cached = loadCachedTemplates();
193
+ if (cached.length > 0) return cached;
194
+ return loadBundledTemplates();
195
+ }
196
+
197
+ // Check cache validity
198
+ if (isCacheValid()) {
199
+ const cached = loadCachedTemplates();
200
+ if (cached.length > 0) return cached;
201
+ }
202
+
203
+ // Try to fetch from remote
204
+ const manifest = await fetchManifest();
205
+ if (manifest) {
206
+ const templateContents = new Map<string, string>();
207
+ const templates: InsightTemplate[] = [];
208
+
209
+ for (const templatePath of manifest.templates) {
210
+ const content = await fetchTemplate(templatePath);
211
+ if (content) {
212
+ templateContents.set(templatePath, content);
213
+ try {
214
+ const parsed = parseYAML(content);
215
+ const validated = InsightTemplate.parse(parsed);
216
+ templates.push(validated);
217
+ } catch {
218
+ // Skip invalid templates
219
+ }
220
+ }
221
+ }
222
+
223
+ if (templates.length > 0) {
224
+ saveToCache(templateContents, manifest.version);
225
+ return templates;
226
+ }
227
+ }
228
+
229
+ // Fall back to stale cache
230
+ const cached = loadCachedTemplates();
231
+ if (cached.length > 0) {
232
+ console.warn('[templates] Using stale cache');
233
+ return cached;
234
+ }
235
+
236
+ // Fall back to bundled templates
237
+ console.warn('[templates] Using bundled templates');
238
+ return loadBundledTemplates();
239
+ }
240
+
241
+ /**
242
+ * Get a single template by ID
243
+ */
244
+ export async function getTemplate(id: string, opts: LoadOptions = {}): Promise<InsightTemplate | null> {
245
+ const templates = await loadTemplates(opts);
246
+ return templates.find(t => t.id === id) || null;
247
+ }
248
+
249
+ /**
250
+ * Clear template cache
251
+ */
252
+ export function clearCache(): void {
253
+ const cacheDir = getCacheDir();
254
+ if (existsSync(cacheDir)) {
255
+ const files = readdirSync(cacheDir);
256
+ for (const file of files) {
257
+ const filePath = join(cacheDir, file);
258
+ try {
259
+ readFileSync(filePath); // Check if readable
260
+ writeFileSync(filePath, ''); // Clear content
261
+ } catch {
262
+ // Skip
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ // =============================================================================
269
+ // OPTIMIZATION TEMPLATES API (v1.8 - Inference Squeeze Guide)
270
+ // =============================================================================
271
+
272
+ /**
273
+ * Load bundled optimization templates from templates/optimizations/
274
+ * These are community optimization runbooks with implementation steps
275
+ */
276
+ export function loadOptimizationTemplates(): OptimizationTemplate[] {
277
+ if (!existsSync(OPTIMIZATIONS_DIR)) return [];
278
+
279
+ const templates: OptimizationTemplate[] = [];
280
+ const files = readdirSync(OPTIMIZATIONS_DIR).filter(f => f.endsWith('.yaml'));
281
+
282
+ for (const file of files) {
283
+ try {
284
+ const content = readFileSync(join(OPTIMIZATIONS_DIR, file), 'utf-8');
285
+ const parsed = parseYAML(content);
286
+ const validated = OptimizationTemplate.parse(parsed);
287
+ templates.push(validated);
288
+ } catch (err) {
289
+ // Skip invalid templates but log for debugging
290
+ console.warn(`[templates] Failed to load optimization template ${file}:`, err);
291
+ }
292
+ }
293
+
294
+ return templates;
295
+ }
296
+
297
+ /**
298
+ * Get a single optimization template by ID
299
+ */
300
+ export function getOptimizationTemplate(id: string): OptimizationTemplate | null {
301
+ const templates = loadOptimizationTemplates();
302
+ return templates.find(t => t.id === id) || null;
303
+ }
304
+
305
+ // =============================================================================
306
+ // ANALYSIS PROMPTS API
307
+ // =============================================================================
308
+
309
+ /**
310
+ * Load an analysis prompt by ID from the prompts directory
311
+ * @param id - Prompt ID (e.g., 'peak-performance')
312
+ * @returns AnalysisPrompt or null if not found
313
+ */
314
+ export function loadPrompt(id: string): AnalysisPrompt | null {
315
+ const promptPath = join(PROMPTS_DIR, `${id}.yaml`);
316
+
317
+ if (!existsSync(promptPath)) {
318
+ return null;
319
+ }
320
+
321
+ try {
322
+ const content = readFileSync(promptPath, 'utf-8');
323
+ const parsed = parseYAML(content) as AnalysisPrompt;
324
+
325
+ // Validate required fields
326
+ if (!parsed.id || !parsed.prompt) {
327
+ console.warn(`[prompts] Invalid prompt file: ${promptPath} (missing id or prompt)`);
328
+ return null;
329
+ }
330
+
331
+ return parsed;
332
+ } catch (err) {
333
+ console.warn(`[prompts] Failed to load prompt ${id}:`, err);
334
+ return null;
335
+ }
336
+ }
337
+
338
+ /**
339
+ * List all available analysis prompts
340
+ * @returns Array of prompt IDs
341
+ */
342
+ export function listPrompts(): string[] {
343
+ if (!existsSync(PROMPTS_DIR)) {
344
+ return [];
345
+ }
346
+
347
+ try {
348
+ const files = readdirSync(PROMPTS_DIR).filter(f => f.endsWith('.yaml'));
349
+ return files.map(f => f.replace('.yaml', ''));
350
+ } catch {
351
+ return [];
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Get the default analysis prompt (peak-performance)
357
+ * @returns AnalysisPrompt
358
+ * @throws Error if default prompt not found
359
+ */
360
+ export function getDefaultPrompt(): AnalysisPrompt {
361
+ const prompt = loadPrompt('peak-performance');
362
+ if (!prompt) {
363
+ throw new Error('[prompts] Default prompt "peak-performance" not found. Ensure prompts/peak-performance.yaml exists.');
364
+ }
365
+ return prompt;
366
+ }
367
+
368
+ // =============================================================================
369
+ // CONFIGURATION API
370
+ // =============================================================================
371
+
372
+ const CONFIG_DIR = join(__dirname, '..', 'config');
373
+
374
+ /**
375
+ * PeakInfer configuration schema
376
+ */
377
+ export interface PeakInferConfig {
378
+ id: string;
379
+ version: string;
380
+ description: string;
381
+ analysis: {
382
+ mode: 'agent' | 'llm' | 'regex';
383
+ cascade: boolean;
384
+ };
385
+ models: {
386
+ agent: {
387
+ primary: string;
388
+ fallback: string;
389
+ };
390
+ llm: {
391
+ primary: string;
392
+ fallback: string;
393
+ };
394
+ };
395
+ agent: {
396
+ max_iterations: number;
397
+ verbose: boolean;
398
+ };
399
+ scanner: {
400
+ extensions: string[];
401
+ max_file_size: number;
402
+ ignore: string[];
403
+ };
404
+ output: {
405
+ format: 'json' | 'yaml' | 'markdown';
406
+ include_confidence: boolean;
407
+ min_confidence: number;
408
+ };
409
+ }
410
+
411
+ // Default configuration (used as fallback)
412
+ const DEFAULT_CONFIG: PeakInferConfig = {
413
+ id: 'peakinfer',
414
+ version: '1.0',
415
+ description: 'Default PeakInfer configuration',
416
+ analysis: {
417
+ mode: 'agent',
418
+ cascade: true,
419
+ },
420
+ models: {
421
+ agent: {
422
+ primary: 'claude-opus-4-5-20251101',
423
+ fallback: 'claude-sonnet-4-20250514',
424
+ },
425
+ llm: {
426
+ primary: 'claude-sonnet-4-20250514',
427
+ fallback: 'claude-sonnet-4-20250514',
428
+ },
429
+ },
430
+ agent: {
431
+ max_iterations: 15,
432
+ verbose: false,
433
+ },
434
+ scanner: {
435
+ extensions: ['.py', '.ts', '.tsx', '.js', '.jsx'],
436
+ max_file_size: 1048576,
437
+ ignore: ['node_modules', '.git', '__pycache__', '.venv', 'venv', 'dist', 'build'],
438
+ },
439
+ output: {
440
+ format: 'json',
441
+ include_confidence: true,
442
+ min_confidence: 0.5,
443
+ },
444
+ };
445
+
446
+ // Cached config
447
+ let cachedConfig: PeakInferConfig | null = null;
448
+
449
+ /**
450
+ * Load PeakInfer configuration from config/peakinfer.yaml
451
+ * Environment variables override file settings:
452
+ * - PEAKINFER_MODE: analysis mode (agent, llm, regex)
453
+ * - PEAKINFER_MODEL: primary model override
454
+ * - PEAKINFER_VERBOSE: enable verbose output
455
+ * @returns PeakInferConfig
456
+ */
457
+ export function loadConfig(): PeakInferConfig {
458
+ // Return cached config if available
459
+ if (cachedConfig) {
460
+ return cachedConfig;
461
+ }
462
+
463
+ let config: PeakInferConfig = { ...DEFAULT_CONFIG };
464
+
465
+ // Try to load from config file
466
+ const configPath = join(CONFIG_DIR, 'peakinfer.yaml');
467
+ if (existsSync(configPath)) {
468
+ try {
469
+ const content = readFileSync(configPath, 'utf-8');
470
+ const parsed = parseYAML(content) as Partial<PeakInferConfig>;
471
+
472
+ // Deep merge with defaults
473
+ config = deepMerge(
474
+ DEFAULT_CONFIG as unknown as Record<string, unknown>,
475
+ parsed as unknown as Record<string, unknown>
476
+ ) as unknown as PeakInferConfig;
477
+ } catch (err) {
478
+ console.warn('[config] Failed to load config file, using defaults:', err);
479
+ }
480
+ }
481
+
482
+ // Apply environment variable overrides
483
+ if (process.env.PEAKINFER_MODE) {
484
+ const mode = process.env.PEAKINFER_MODE.toLowerCase();
485
+ if (['agent', 'llm', 'regex'].includes(mode)) {
486
+ config.analysis.mode = mode as 'agent' | 'llm' | 'regex';
487
+ }
488
+ }
489
+
490
+ if (process.env.PEAKINFER_MODEL) {
491
+ config.models.agent.primary = process.env.PEAKINFER_MODEL;
492
+ config.models.llm.primary = process.env.PEAKINFER_MODEL;
493
+ }
494
+
495
+ if (process.env.PEAKINFER_VERBOSE === '1' || process.env.PEAKINFER_VERBOSE === 'true') {
496
+ config.agent.verbose = true;
497
+ }
498
+
499
+ // Cache the config
500
+ cachedConfig = config;
501
+
502
+ return config;
503
+ }
504
+
505
+ /**
506
+ * Get the configured model for a given analysis type
507
+ * @param type - 'agent' or 'llm'
508
+ * @param fallback - whether to return fallback model
509
+ * @returns model name
510
+ */
511
+ export function getConfiguredModel(type: 'agent' | 'llm', fallback: boolean = false): string {
512
+ const config = loadConfig();
513
+ const models = config.models[type];
514
+ return fallback ? models.fallback : models.primary;
515
+ }
516
+
517
+ /**
518
+ * Get the configured analysis mode
519
+ * @returns analysis mode
520
+ */
521
+ export function getConfiguredMode(): 'agent' | 'llm' | 'regex' {
522
+ const config = loadConfig();
523
+ return config.analysis.mode;
524
+ }
525
+
526
+ /**
527
+ * Check if cascade fallback is enabled
528
+ * @returns true if cascade is enabled
529
+ */
530
+ export function isCascadeEnabled(): boolean {
531
+ const config = loadConfig();
532
+ return config.analysis.cascade;
533
+ }
534
+
535
+ /**
536
+ * Deep merge two objects
537
+ */
538
+ function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
539
+ const result = { ...target };
540
+
541
+ for (const key of Object.keys(source)) {
542
+ if (source[key] !== undefined) {
543
+ if (
544
+ typeof source[key] === 'object' &&
545
+ source[key] !== null &&
546
+ !Array.isArray(source[key]) &&
547
+ typeof target[key] === 'object' &&
548
+ target[key] !== null
549
+ ) {
550
+ result[key] = deepMerge(
551
+ target[key] as Record<string, unknown>,
552
+ source[key] as Record<string, unknown>
553
+ );
554
+ } else {
555
+ result[key] = source[key];
556
+ }
557
+ }
558
+ }
559
+
560
+ return result;
561
+ }