@dewtech/dare-cli 3.11.0 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/README.md +2 -0
  2. package/dist/__tests__/cli-only-invariants.test.d.ts +2 -0
  3. package/dist/__tests__/cli-only-invariants.test.d.ts.map +1 -0
  4. package/dist/__tests__/cli-only-invariants.test.js +100 -0
  5. package/dist/__tests__/cli-only-invariants.test.js.map +1 -0
  6. package/dist/__tests__/cli-only-regression.test.d.ts +2 -0
  7. package/dist/__tests__/cli-only-regression.test.d.ts.map +1 -0
  8. package/dist/__tests__/cli-only-regression.test.js +29 -0
  9. package/dist/__tests__/cli-only-regression.test.js.map +1 -0
  10. package/dist/__tests__/ensure-skills.test.js +5 -0
  11. package/dist/__tests__/ensure-skills.test.js.map +1 -1
  12. package/dist/__tests__/ide-command-parity.test.js +1 -0
  13. package/dist/__tests__/ide-command-parity.test.js.map +1 -1
  14. package/dist/__tests__/project-generator.test.js +17 -0
  15. package/dist/__tests__/project-generator.test.js.map +1 -1
  16. package/dist/__tests__/reverse-facts.test.js +1 -0
  17. package/dist/__tests__/reverse-facts.test.js.map +1 -1
  18. package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
  19. package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
  20. package/dist/__tests__/terminal-parity-regression.test.js +116 -0
  21. package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
  22. package/dist/__tests__/terminal-parity.test.d.ts +2 -0
  23. package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
  24. package/dist/__tests__/terminal-parity.test.js +81 -0
  25. package/dist/__tests__/terminal-parity.test.js.map +1 -0
  26. package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
  27. package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
  28. package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
  29. package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
  30. package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
  31. package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
  32. package/dist/agent/__tests__/codex-driver.test.js +68 -0
  33. package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
  34. package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
  35. package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
  36. package/dist/agent/__tests__/cursor-driver.test.js +52 -0
  37. package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
  38. package/dist/agent/driver.d.ts +1 -1
  39. package/dist/agent/driver.d.ts.map +1 -1
  40. package/dist/agent/drivers/antigravity.d.ts +8 -0
  41. package/dist/agent/drivers/antigravity.d.ts.map +1 -0
  42. package/dist/agent/drivers/antigravity.js +99 -0
  43. package/dist/agent/drivers/antigravity.js.map +1 -0
  44. package/dist/agent/drivers/codex.d.ts +12 -0
  45. package/dist/agent/drivers/codex.d.ts.map +1 -0
  46. package/dist/agent/drivers/codex.js +137 -0
  47. package/dist/agent/drivers/codex.js.map +1 -0
  48. package/dist/agent/drivers/cursor.d.ts +8 -0
  49. package/dist/agent/drivers/cursor.d.ts.map +1 -0
  50. package/dist/agent/drivers/cursor.js +99 -0
  51. package/dist/agent/drivers/cursor.js.map +1 -0
  52. package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
  53. package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
  54. package/dist/ai/__tests__/ai-core.test.js +41 -0
  55. package/dist/ai/__tests__/ai-core.test.js.map +1 -0
  56. package/dist/ai/__tests__/parity.test.d.ts +2 -0
  57. package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
  58. package/dist/ai/__tests__/parity.test.js +36 -0
  59. package/dist/ai/__tests__/parity.test.js.map +1 -0
  60. package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
  61. package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
  62. package/dist/ai/__tests__/pipeline.test.js +147 -0
  63. package/dist/ai/__tests__/pipeline.test.js.map +1 -0
  64. package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
  65. package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
  66. package/dist/ai/__tests__/refine-bridge.test.js +17 -0
  67. package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
  68. package/dist/ai/__tests__/resolve.test.d.ts +2 -0
  69. package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
  70. package/dist/ai/__tests__/resolve.test.js +42 -0
  71. package/dist/ai/__tests__/resolve.test.js.map +1 -0
  72. package/dist/ai/capabilities.d.ts +3 -0
  73. package/dist/ai/capabilities.d.ts.map +1 -0
  74. package/dist/ai/capabilities.js +11 -0
  75. package/dist/ai/capabilities.js.map +1 -0
  76. package/dist/ai/command-options.d.ts +10 -0
  77. package/dist/ai/command-options.d.ts.map +1 -0
  78. package/dist/ai/command-options.js +15 -0
  79. package/dist/ai/command-options.js.map +1 -0
  80. package/dist/ai/config.d.ts +27 -0
  81. package/dist/ai/config.d.ts.map +1 -0
  82. package/dist/ai/config.js +89 -0
  83. package/dist/ai/config.js.map +1 -0
  84. package/dist/ai/parity.d.ts +13 -0
  85. package/dist/ai/parity.d.ts.map +1 -0
  86. package/dist/ai/parity.js +87 -0
  87. package/dist/ai/parity.js.map +1 -0
  88. package/dist/ai/parse-json-output.d.ts +5 -0
  89. package/dist/ai/parse-json-output.d.ts.map +1 -0
  90. package/dist/ai/parse-json-output.js +25 -0
  91. package/dist/ai/parse-json-output.js.map +1 -0
  92. package/dist/ai/pipeline.d.ts +20 -0
  93. package/dist/ai/pipeline.d.ts.map +1 -0
  94. package/dist/ai/pipeline.js +303 -0
  95. package/dist/ai/pipeline.js.map +1 -0
  96. package/dist/ai/prompts.d.ts +6 -0
  97. package/dist/ai/prompts.d.ts.map +1 -0
  98. package/dist/ai/prompts.js +49 -0
  99. package/dist/ai/prompts.js.map +1 -0
  100. package/dist/ai/providers.d.ts +63 -0
  101. package/dist/ai/providers.d.ts.map +1 -0
  102. package/dist/ai/providers.js +297 -0
  103. package/dist/ai/providers.js.map +1 -0
  104. package/dist/ai/refine-bridge.d.ts +5 -0
  105. package/dist/ai/refine-bridge.d.ts.map +1 -0
  106. package/dist/ai/refine-bridge.js +14 -0
  107. package/dist/ai/refine-bridge.js.map +1 -0
  108. package/dist/ai/registry.d.ts +12 -0
  109. package/dist/ai/registry.d.ts.map +1 -0
  110. package/dist/ai/registry.js +43 -0
  111. package/dist/ai/registry.js.map +1 -0
  112. package/dist/ai/resolve.d.ts +28 -0
  113. package/dist/ai/resolve.d.ts.map +1 -0
  114. package/dist/ai/resolve.js +83 -0
  115. package/dist/ai/resolve.js.map +1 -0
  116. package/dist/ai/schemas.d.ts +175 -0
  117. package/dist/ai/schemas.d.ts.map +1 -0
  118. package/dist/ai/schemas.js +199 -0
  119. package/dist/ai/schemas.js.map +1 -0
  120. package/dist/ai/types.d.ts +52 -0
  121. package/dist/ai/types.d.ts.map +1 -0
  122. package/dist/ai/types.js +8 -0
  123. package/dist/ai/types.js.map +1 -0
  124. package/dist/bin/dare.js +2 -0
  125. package/dist/bin/dare.js.map +1 -1
  126. package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
  127. package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
  128. package/dist/commands/__tests__/ai-command.test.js +68 -0
  129. package/dist/commands/__tests__/ai-command.test.js.map +1 -0
  130. package/dist/commands/__tests__/execute-agent.test.js +82 -0
  131. package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
  132. package/dist/commands/ai.d.ts +3 -0
  133. package/dist/commands/ai.d.ts.map +1 -0
  134. package/dist/commands/ai.js +141 -0
  135. package/dist/commands/ai.js.map +1 -0
  136. package/dist/commands/blueprint.d.ts.map +1 -1
  137. package/dist/commands/blueprint.js +17 -3
  138. package/dist/commands/blueprint.js.map +1 -1
  139. package/dist/commands/design.d.ts.map +1 -1
  140. package/dist/commands/design.js +21 -2
  141. package/dist/commands/design.js.map +1 -1
  142. package/dist/commands/discover.d.ts.map +1 -1
  143. package/dist/commands/discover.js +9 -1
  144. package/dist/commands/discover.js.map +1 -1
  145. package/dist/commands/dna.d.ts.map +1 -1
  146. package/dist/commands/dna.js +23 -3
  147. package/dist/commands/dna.js.map +1 -1
  148. package/dist/commands/execute.d.ts +11 -0
  149. package/dist/commands/execute.d.ts.map +1 -1
  150. package/dist/commands/execute.js +111 -4
  151. package/dist/commands/execute.js.map +1 -1
  152. package/dist/commands/init.d.ts.map +1 -1
  153. package/dist/commands/init.js +1 -0
  154. package/dist/commands/init.js.map +1 -1
  155. package/dist/commands/migrate.d.ts.map +1 -1
  156. package/dist/commands/migrate.js +14 -2
  157. package/dist/commands/migrate.js.map +1 -1
  158. package/dist/commands/patterns.d.ts.map +1 -1
  159. package/dist/commands/patterns.js +14 -2
  160. package/dist/commands/patterns.js.map +1 -1
  161. package/dist/commands/refine.d.ts.map +1 -1
  162. package/dist/commands/refine.js +23 -2
  163. package/dist/commands/refine.js.map +1 -1
  164. package/dist/commands/reverse.d.ts.map +1 -1
  165. package/dist/commands/reverse.js +28 -3
  166. package/dist/commands/reverse.js.map +1 -1
  167. package/dist/commands/review.d.ts.map +1 -1
  168. package/dist/commands/review.js +25 -3
  169. package/dist/commands/review.js.map +1 -1
  170. package/dist/core/types/project.d.ts +1 -1
  171. package/dist/core/types/project.d.ts.map +1 -1
  172. package/dist/dag-runner/run_dag.d.ts +1 -1
  173. package/dist/dag-runner/run_dag.d.ts.map +1 -1
  174. package/dist/exec/safe-spawn.d.ts.map +1 -1
  175. package/dist/exec/safe-spawn.js +6 -1
  176. package/dist/exec/safe-spawn.js.map +1 -1
  177. package/dist/skills/bundled.d.ts +5 -0
  178. package/dist/skills/bundled.d.ts.map +1 -0
  179. package/dist/skills/bundled.js +34 -0
  180. package/dist/skills/bundled.js.map +1 -0
  181. package/dist/skills/commands/add.d.ts +1 -3
  182. package/dist/skills/commands/add.d.ts.map +1 -1
  183. package/dist/skills/commands/add.js +20 -3
  184. package/dist/skills/commands/add.js.map +1 -1
  185. package/dist/skills/tests/bundled.spec.d.ts +2 -0
  186. package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
  187. package/dist/skills/tests/bundled.spec.js +24 -0
  188. package/dist/skills/tests/bundled.spec.js.map +1 -0
  189. package/dist/types/UpdateManifest.types.d.ts +1 -1
  190. package/dist/types/UpdateManifest.types.d.ts.map +1 -1
  191. package/dist/utils/dag-converter.js +1 -1
  192. package/dist/utils/dag-converter.js.map +1 -1
  193. package/dist/utils/project-detector.d.ts +1 -0
  194. package/dist/utils/project-detector.d.ts.map +1 -1
  195. package/dist/utils/project-detector.js +8 -0
  196. package/dist/utils/project-detector.js.map +1 -1
  197. package/dist/utils/project-generator.d.ts +1 -1
  198. package/dist/utils/project-generator.d.ts.map +1 -1
  199. package/dist/utils/project-generator.js +23 -2
  200. package/dist/utils/project-generator.js.map +1 -1
  201. package/dist/utils/templates.d.ts +2 -0
  202. package/dist/utils/templates.d.ts.map +1 -1
  203. package/dist/utils/templates.js +74 -0
  204. package/dist/utils/templates.js.map +1 -1
  205. package/dist/verification/__tests__/safe-spawn.test.js +12 -0
  206. package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
  207. package/package.json +2 -1
  208. package/skills/dare-ax/generator.ts +325 -0
  209. package/skills/dare-ax/index.ts +19 -0
  210. package/skills/dare-ax/metrics.ts +352 -0
  211. package/skills/dare-ax/package-lock.json +1855 -0
  212. package/skills/dare-ax/package.json +50 -0
  213. package/skills/dare-ax/secret-detector.ts +123 -0
  214. package/skills/dare-ax/skill.yml +19 -0
  215. package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
  216. package/skills/dare-ax/tests/generator.spec.ts +193 -0
  217. package/skills/dare-ax/tests/metrics.spec.ts +394 -0
  218. package/skills/dare-ax/tests/validator.spec.ts +298 -0
  219. package/skills/dare-ax/tsconfig.json +18 -0
  220. package/skills/dare-ax/types.ts +79 -0
  221. package/skills/dare-ax/validator.ts +238 -0
  222. package/skills/dare-frontend-design/generator.ts +616 -0
  223. package/skills/dare-frontend-design/index.ts +25 -0
  224. package/skills/dare-frontend-design/linter.ts +227 -0
  225. package/skills/dare-frontend-design/metrics.ts +82 -0
  226. package/skills/dare-frontend-design/package-lock.json +1855 -0
  227. package/skills/dare-frontend-design/package.json +43 -0
  228. package/skills/dare-frontend-design/skill.yml +20 -0
  229. package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
  230. package/skills/dare-frontend-design/tsconfig.json +18 -0
  231. package/skills/dare-frontend-design/types.ts +62 -0
  232. package/skills/dare-layered-design/generator.ts +740 -0
  233. package/skills/dare-layered-design/index.ts +17 -0
  234. package/skills/dare-layered-design/linter.ts +462 -0
  235. package/skills/dare-layered-design/metrics.ts +409 -0
  236. package/skills/dare-layered-design/package-lock.json +1855 -0
  237. package/skills/dare-layered-design/package.json +50 -0
  238. package/skills/dare-layered-design/skill.yml +35 -0
  239. package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
  240. package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
  241. package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
  242. package/skills/dare-layered-design/tsconfig.json +18 -0
  243. package/skills/dare-layered-design/types.ts +48 -0
  244. package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
  245. package/skills/dare-llm-integration/index.ts +49 -0
  246. package/skills/dare-llm-integration/metrics.ts +107 -0
  247. package/skills/dare-llm-integration/package-lock.json +1855 -0
  248. package/skills/dare-llm-integration/package.json +49 -0
  249. package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
  250. package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
  251. package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
  252. package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
  253. package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
  254. package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
  255. package/skills/dare-llm-integration/skill.yml +23 -0
  256. package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
  257. package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
  258. package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
  259. package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
  260. package/skills/dare-llm-integration/tsconfig.json +23 -0
  261. package/skills/dare-llm-integration/types.ts +91 -0
  262. package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
  263. package/skills/dare-quality-telemetry/collect.ts +134 -0
  264. package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
  265. package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
  266. package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
  267. package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
  268. package/skills/dare-quality-telemetry/index.ts +18 -0
  269. package/skills/dare-quality-telemetry/metrics.ts +137 -0
  270. package/skills/dare-quality-telemetry/package-lock.json +1855 -0
  271. package/skills/dare-quality-telemetry/package.json +48 -0
  272. package/skills/dare-quality-telemetry/regression.ts +60 -0
  273. package/skills/dare-quality-telemetry/reporter.ts +132 -0
  274. package/skills/dare-quality-telemetry/skill.yml +18 -0
  275. package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
  276. package/skills/dare-quality-telemetry/tsconfig.json +19 -0
  277. package/skills/dare-quality-telemetry/types.ts +41 -0
  278. package/skills/dare-realtime/event_registry.ts +101 -0
  279. package/skills/dare-realtime/index.ts +30 -0
  280. package/skills/dare-realtime/metrics.ts +84 -0
  281. package/skills/dare-realtime/package-lock.json +1855 -0
  282. package/skills/dare-realtime/package.json +43 -0
  283. package/skills/dare-realtime/reconnect_strategy.ts +85 -0
  284. package/skills/dare-realtime/schema_validator.ts +80 -0
  285. package/skills/dare-realtime/skill.yml +21 -0
  286. package/skills/dare-realtime/subscription_manager.ts +106 -0
  287. package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
  288. package/skills/dare-realtime/tsconfig.json +18 -0
  289. package/skills/dare-realtime/types.ts +51 -0
  290. package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
  291. package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
  292. package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
  293. package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
  294. package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
  295. package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
  296. package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
  297. package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
  298. package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
  299. package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
  300. package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
  301. package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
  302. package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
  303. package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
  304. package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
  305. package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
  306. package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
  307. package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
  308. package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
  309. package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
  310. package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
  311. package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
  312. package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
  313. package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
  314. package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
  315. package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
  316. package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@dewtech/dare-llm-integration",
3
+ "version": "1.0.0",
4
+ "description": "dare-llm-integration — LLM integration patterns skill for DARE Method v3.0",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "skill.yml"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "typecheck": "tsc --noEmit"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^20.19.41",
26
+ "typescript": "^5.9.3",
27
+ "vitest": "^1.6.1"
28
+ },
29
+ "keywords": [
30
+ "dare",
31
+ "dare-method",
32
+ "dare-llm-integration",
33
+ "llm",
34
+ "openai",
35
+ "anthropic",
36
+ "caching",
37
+ "rate-limit"
38
+ ],
39
+ "author": "Dewtech Technologies",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/dewtech-technologies/dare-method.git",
44
+ "directory": "packages/skills/dare-llm-integration"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ }
49
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * dare-llm-integration — PromptLoader
3
+ * Loads and renders versioned Jinja2 prompt templates.
4
+ * Uses the same token-based engine as dare-ax/generator.ts.
5
+ * License: MIT
6
+ */
7
+
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+
11
+ export interface PromptLoaderConfig {
12
+ /** Directory where .jinja2 template files live. */
13
+ templatesDir: string;
14
+ }
15
+
16
+ type Token =
17
+ | { type: 'text'; value: string }
18
+ | { type: 'var'; name: string }
19
+ | { type: 'for'; varName: string; listName: string }
20
+ | { type: 'endfor' }
21
+ | { type: 'if'; condName: string }
22
+ | { type: 'else' }
23
+ | { type: 'endif' };
24
+
25
+ export class PromptLoader {
26
+ private readonly templatesDir: string;
27
+ private readonly templateCache: Map<string, string> = new Map();
28
+
29
+ constructor(config: PromptLoaderConfig) {
30
+ this.templatesDir = config.templatesDir;
31
+ }
32
+
33
+ /**
34
+ * Load and render a versioned template.
35
+ * File resolved as: <templatesDir>/<name>_<version>.jinja2
36
+ *
37
+ * @param name - Template name, e.g. "summarize"
38
+ * @param version - Version string, e.g. "v1"
39
+ * @param vars - Variables to interpolate into the template
40
+ * @returns Rendered prompt string
41
+ */
42
+ load(name: string, version: string, vars: Record<string, string>): string {
43
+ const filename = `${name}_${version}.jinja2`;
44
+ const filepath = path.join(this.templatesDir, filename);
45
+
46
+ let content = this.templateCache.get(filepath);
47
+ if (!content) {
48
+ if (!fs.existsSync(filepath)) {
49
+ throw new Error(`PromptLoader: template not found: ${filepath}`);
50
+ }
51
+ content = fs.readFileSync(filepath, 'utf-8');
52
+ this.templateCache.set(filepath, content);
53
+ }
54
+
55
+ return renderTemplate(content, vars);
56
+ }
57
+
58
+ /**
59
+ * Render a template string directly (without reading a file).
60
+ */
61
+ render(templateContent: string, vars: Record<string, string>): string {
62
+ return renderTemplate(templateContent, vars);
63
+ }
64
+
65
+ /**
66
+ * List all available template names and versions in the templates directory.
67
+ */
68
+ listTemplates(): Array<{ name: string; version: string; filename: string }> {
69
+ if (!fs.existsSync(this.templatesDir)) return [];
70
+
71
+ return fs
72
+ .readdirSync(this.templatesDir)
73
+ .filter((f) => f.endsWith('.jinja2'))
74
+ .map((filename) => {
75
+ const base = filename.replace('.jinja2', '');
76
+ const lastUnderscore = base.lastIndexOf('_');
77
+ if (lastUnderscore < 0) return null;
78
+ return {
79
+ name: base.slice(0, lastUnderscore),
80
+ version: base.slice(lastUnderscore + 1),
81
+ filename,
82
+ };
83
+ })
84
+ .filter((x): x is { name: string; version: string; filename: string } => x !== null);
85
+ }
86
+
87
+ /**
88
+ * Clear the template file cache (useful in tests).
89
+ */
90
+ clearCache(): void {
91
+ this.templateCache.clear();
92
+ }
93
+ }
94
+
95
+ // --- Jinja2-style template engine (same logic as dare-ax/generator.ts) ---
96
+
97
+ function renderTemplate(content: string, vars: Record<string, unknown>): string {
98
+ const tokens = tokenize(content);
99
+ return evaluate(tokens, vars, 0).output;
100
+ }
101
+
102
+ function tokenize(template: string): Token[] {
103
+ const tokens: Token[] = [];
104
+ let pos = 0;
105
+
106
+ while (pos < template.length) {
107
+ const varStart = template.indexOf('{{', pos);
108
+ const tagStart = template.indexOf('{%', pos);
109
+
110
+ let next: number;
111
+ let isVar: boolean;
112
+
113
+ if (varStart === -1 && tagStart === -1) {
114
+ tokens.push({ type: 'text', value: template.slice(pos) });
115
+ break;
116
+ } else if (varStart === -1) {
117
+ next = tagStart;
118
+ isVar = false;
119
+ } else if (tagStart === -1) {
120
+ next = varStart;
121
+ isVar = true;
122
+ } else {
123
+ next = Math.min(varStart, tagStart);
124
+ isVar = varStart < tagStart;
125
+ }
126
+
127
+ if (next > pos) {
128
+ tokens.push({ type: 'text', value: template.slice(pos, next) });
129
+ }
130
+
131
+ if (isVar) {
132
+ const end = template.indexOf('}}', next + 2);
133
+ if (end === -1) {
134
+ tokens.push({ type: 'text', value: template.slice(next) });
135
+ break;
136
+ }
137
+ const name = template.slice(next + 2, end).trim();
138
+ tokens.push({ type: 'var', name });
139
+ pos = end + 2;
140
+ } else {
141
+ const end = template.indexOf('%}', next + 2);
142
+ if (end === -1) {
143
+ tokens.push({ type: 'text', value: template.slice(next) });
144
+ break;
145
+ }
146
+ const inner = template.slice(next + 2, end).trim();
147
+ pos = end + 2;
148
+
149
+ if (inner.startsWith('for ')) {
150
+ const m = /^for\s+(\w+)\s+in\s+([\w.]+)$/.exec(inner);
151
+ if (m) tokens.push({ type: 'for', varName: m[1], listName: m[2] });
152
+ } else if (inner === 'endfor') {
153
+ tokens.push({ type: 'endfor' });
154
+ } else if (inner.startsWith('if ')) {
155
+ tokens.push({ type: 'if', condName: inner.slice(3).trim() });
156
+ } else if (inner === 'else') {
157
+ tokens.push({ type: 'else' });
158
+ } else if (inner === 'endif') {
159
+ tokens.push({ type: 'endif' });
160
+ }
161
+ }
162
+ }
163
+
164
+ return tokens;
165
+ }
166
+
167
+ function evaluate(
168
+ tokens: Token[],
169
+ context: Record<string, unknown>,
170
+ startIdx: number
171
+ ): { output: string; nextIdx: number } {
172
+ let output = '';
173
+ let i = startIdx;
174
+
175
+ while (i < tokens.length) {
176
+ const token = tokens[i];
177
+
178
+ if (token.type === 'text') {
179
+ output += token.value;
180
+ i++;
181
+ } else if (token.type === 'var') {
182
+ const val = getNestedValue(context, token.name);
183
+ if (val !== undefined && val !== null) output += String(val);
184
+ i++;
185
+ } else if (token.type === 'for') {
186
+ const bodyStart = i + 1;
187
+ let depth = 1;
188
+ let j = bodyStart;
189
+ while (j < tokens.length && depth > 0) {
190
+ if (tokens[j].type === 'for') depth++;
191
+ if (tokens[j].type === 'endfor') depth--;
192
+ if (depth > 0) j++;
193
+ else break;
194
+ }
195
+ const bodyTokens = tokens.slice(bodyStart, j);
196
+ const list = getNestedValue(context, token.listName);
197
+ if (Array.isArray(list)) {
198
+ for (const item of list) {
199
+ const itemCtx: Record<string, unknown> = { ...context };
200
+ if (typeof item === 'object' && item !== null) {
201
+ itemCtx[token.varName] = item;
202
+ for (const [k, v] of Object.entries(item as Record<string, unknown>)) {
203
+ itemCtx[`${token.varName}.${k}`] = v;
204
+ }
205
+ } else {
206
+ itemCtx[token.varName] = item;
207
+ }
208
+ output += evaluate(bodyTokens, itemCtx, 0).output;
209
+ }
210
+ }
211
+ i = j + 1;
212
+ } else if (token.type === 'if') {
213
+ const bodyStart = i + 1;
214
+ let depth = 1;
215
+ let j = bodyStart;
216
+ let elseIdx = -1;
217
+ while (j < tokens.length && depth > 0) {
218
+ if (tokens[j].type === 'if') depth++;
219
+ if (tokens[j].type === 'endif') depth--;
220
+ if (depth === 1 && tokens[j].type === 'else') elseIdx = j;
221
+ if (depth > 0) j++;
222
+ else break;
223
+ }
224
+ const endifIdx = j;
225
+ const thenTokens = elseIdx >= 0 ? tokens.slice(bodyStart, elseIdx) : tokens.slice(bodyStart, endifIdx);
226
+ const elseTokens = elseIdx >= 0 ? tokens.slice(elseIdx + 1, endifIdx) : [];
227
+ const condValue = getNestedValue(context, token.condName);
228
+ if (isTruthy(condValue)) {
229
+ output += evaluate(thenTokens, context, 0).output;
230
+ } else {
231
+ output += evaluate(elseTokens, context, 0).output;
232
+ }
233
+ i = endifIdx + 1;
234
+ } else if (token.type === 'else' || token.type === 'endif' || token.type === 'endfor') {
235
+ break;
236
+ } else {
237
+ i++;
238
+ }
239
+ }
240
+
241
+ return { output, nextIdx: i };
242
+ }
243
+
244
+ function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
245
+ return path.split('.').reduce((current: unknown, key: string) => {
246
+ if (current !== null && typeof current === 'object') {
247
+ return (current as Record<string, unknown>)[key];
248
+ }
249
+ return undefined;
250
+ }, obj as unknown);
251
+ }
252
+
253
+ function isTruthy(value: unknown): boolean {
254
+ if (Array.isArray(value)) return value.length > 0;
255
+ if (value === null || value === undefined) return false;
256
+ if (typeof value === 'string') return value.length > 0;
257
+ return Boolean(value);
258
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * dare-llm-integration — AnthropicProvider
3
+ * Calls Anthropic Messages API using fetch (no external SDK).
4
+ * License: MIT
5
+ */
6
+
7
+ import type {
8
+ LLMProvider,
9
+ CompletionRequest,
10
+ CompletionResponse,
11
+ EmbedRequest,
12
+ EmbeddingResponse,
13
+ } from '../types.js';
14
+ import { LLMCache } from '../cache/llm_cache.js';
15
+ import { TokenBucket } from '../rate_limit/token_bucket.js';
16
+
17
+ export interface AnthropicProviderConfig {
18
+ apiKey: string;
19
+ cache?: LLMCache;
20
+ rateLimiter?: TokenBucket;
21
+ defaultCacheTtlMs?: number;
22
+ defaultTimeoutMs?: number;
23
+ anthropicVersion?: string;
24
+ onTokenUsage?: (model: string, input: number, output: number) => void;
25
+ }
26
+
27
+ export class AnthropicProvider implements LLMProvider {
28
+ private readonly apiKey: string;
29
+ private readonly cache: LLMCache | null;
30
+ private readonly rateLimiter: TokenBucket | null;
31
+ private readonly defaultCacheTtlMs: number;
32
+ private readonly defaultTimeoutMs: number;
33
+ private readonly anthropicVersion: string;
34
+ private readonly onTokenUsage: ((model: string, input: number, output: number) => void) | null;
35
+
36
+ constructor(config: AnthropicProviderConfig) {
37
+ this.apiKey = config.apiKey;
38
+ this.cache = config.cache ?? null;
39
+ this.rateLimiter = config.rateLimiter ?? null;
40
+ this.defaultCacheTtlMs = config.defaultCacheTtlMs ?? 24 * 60 * 60 * 1000;
41
+ this.defaultTimeoutMs = config.defaultTimeoutMs ?? 30_000;
42
+ this.anthropicVersion = config.anthropicVersion ?? '2023-06-01';
43
+ this.onTokenUsage = config.onTokenUsage ?? null;
44
+ }
45
+
46
+ async complete(request: CompletionRequest): Promise<CompletionResponse> {
47
+ const cacheKey = this.buildCacheKey('complete', request.model, request.prompt);
48
+
49
+ if (this.cache) {
50
+ const cached = this.cache.get(cacheKey);
51
+ if (cached) {
52
+ return cached.value as CompletionResponse;
53
+ }
54
+ }
55
+
56
+ if (this.rateLimiter) {
57
+ await this.rateLimiter.acquire();
58
+ }
59
+
60
+ const start = Date.now();
61
+ const result = await this.callMessagesAPI(request);
62
+ const latencyMs = Date.now() - start;
63
+
64
+ const response: CompletionResponse = { ...result, cached: false, latencyMs };
65
+
66
+ if (this.onTokenUsage) {
67
+ this.onTokenUsage(request.model, response.tokensUsed.input, response.tokensUsed.output);
68
+ }
69
+
70
+ if (this.cache) {
71
+ this.cache.set(cacheKey, response, this.defaultCacheTtlMs);
72
+ }
73
+
74
+ return response;
75
+ }
76
+
77
+ async embed(_request: EmbedRequest): Promise<EmbeddingResponse> {
78
+ // Anthropic does not provide an embeddings API (as of v1.0).
79
+ // Return empty embeddings to satisfy interface; callers should use OpenAI for embeddings.
80
+ throw new Error(
81
+ 'AnthropicProvider: embed() is not supported. Use OpenAIProvider for embeddings.'
82
+ );
83
+ }
84
+
85
+ private buildCacheKey(op: string, model: string, content: string): string {
86
+ const combined = `${op}:${model}:${content}`;
87
+ let hash = 0;
88
+ for (let i = 0; i < combined.length; i++) {
89
+ const char = combined.charCodeAt(i);
90
+ hash = ((hash << 5) - hash) + char;
91
+ hash |= 0;
92
+ }
93
+ return `llm:anthropic:${op}:${model}:${Math.abs(hash).toString(16)}`;
94
+ }
95
+
96
+ private async callMessagesAPI(request: CompletionRequest): Promise<CompletionResponse> {
97
+ const timeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;
98
+ const controller = new AbortController();
99
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
100
+
101
+ let data: unknown;
102
+ try {
103
+ const body: Record<string, unknown> = {
104
+ model: request.model,
105
+ max_tokens: request.maxTokens ?? 1024,
106
+ messages: [{ role: 'user', content: request.prompt }],
107
+ };
108
+
109
+ if (request.systemPrompt) {
110
+ body['system'] = request.systemPrompt;
111
+ }
112
+
113
+ if (request.temperature !== undefined) {
114
+ body['temperature'] = request.temperature;
115
+ }
116
+
117
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
118
+ method: 'POST',
119
+ headers: {
120
+ 'Content-Type': 'application/json',
121
+ 'x-api-key': this.apiKey,
122
+ 'anthropic-version': this.anthropicVersion,
123
+ },
124
+ body: JSON.stringify(body),
125
+ signal: controller.signal,
126
+ });
127
+
128
+ if (!response.ok) {
129
+ throw new Error(`Anthropic API error: ${response.status} ${response.statusText}`);
130
+ }
131
+
132
+ data = await response.json();
133
+ } finally {
134
+ clearTimeout(timer);
135
+ }
136
+
137
+ const d = data as {
138
+ content: Array<{ type: string; text: string }>;
139
+ usage: { input_tokens: number; output_tokens: number };
140
+ model: string;
141
+ };
142
+
143
+ const textContent = d.content.find((c) => c.type === 'text');
144
+ const text = textContent?.text ?? '';
145
+ const inputTokens = d.usage.input_tokens;
146
+ const outputTokens = d.usage.output_tokens;
147
+
148
+ return {
149
+ text,
150
+ tokensUsed: {
151
+ input: inputTokens,
152
+ output: outputTokens,
153
+ total: inputTokens + outputTokens,
154
+ },
155
+ cached: false,
156
+ model: d.model,
157
+ };
158
+ }
159
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * dare-llm-integration — DummyProvider
3
+ * Returns configurable responses without any network call.
4
+ * Use in tests and offline development.
5
+ * License: MIT
6
+ */
7
+
8
+ import type {
9
+ LLMProvider,
10
+ CompletionRequest,
11
+ CompletionResponse,
12
+ EmbedRequest,
13
+ EmbeddingResponse,
14
+ } from '../types.js';
15
+
16
+ export interface DummyProviderConfig {
17
+ /** Fixed text to return from complete(). Default: "dummy response" */
18
+ completionText?: string;
19
+ /** Fixed embedding vector. Default: [0.1, 0.2, 0.3] */
20
+ embedding?: number[];
21
+ /** Simulated latency in ms. Default: 0 */
22
+ latencyMs?: number;
23
+ /** If set, complete() will throw this error */
24
+ throwError?: Error;
25
+ /** Token counts to report */
26
+ tokenCounts?: { input: number; output: number };
27
+ /** Called every time complete() is invoked (for spy assertions) */
28
+ onComplete?: (request: CompletionRequest) => void;
29
+ /** Called every time embed() is invoked */
30
+ onEmbed?: (request: EmbedRequest) => void;
31
+ }
32
+
33
+ export class DummyProvider implements LLMProvider {
34
+ private readonly completionText: string;
35
+ private readonly embedding: number[];
36
+ private readonly latencyMs: number;
37
+ private readonly throwError: Error | null;
38
+ private readonly tokenCounts: { input: number; output: number };
39
+ private readonly onComplete: ((req: CompletionRequest) => void) | null;
40
+ private readonly onEmbed: ((req: EmbedRequest) => void) | null;
41
+
42
+ /** Tracks all requests received (useful for assertions) */
43
+ public readonly completionHistory: CompletionRequest[] = [];
44
+ public readonly embedHistory: EmbedRequest[] = [];
45
+
46
+ constructor(config: DummyProviderConfig = {}) {
47
+ this.completionText = config.completionText ?? 'dummy response';
48
+ this.embedding = config.embedding ?? [0.1, 0.2, 0.3];
49
+ this.latencyMs = config.latencyMs ?? 0;
50
+ this.throwError = config.throwError ?? null;
51
+ this.tokenCounts = config.tokenCounts ?? { input: 10, output: 20 };
52
+ this.onComplete = config.onComplete ?? null;
53
+ this.onEmbed = config.onEmbed ?? null;
54
+ }
55
+
56
+ async complete(request: CompletionRequest): Promise<CompletionResponse> {
57
+ this.completionHistory.push(request);
58
+
59
+ if (this.onComplete) {
60
+ this.onComplete(request);
61
+ }
62
+
63
+ if (this.latencyMs > 0) {
64
+ await delay(this.latencyMs);
65
+ }
66
+
67
+ if (this.throwError) {
68
+ throw this.throwError;
69
+ }
70
+
71
+ return {
72
+ text: this.completionText,
73
+ tokensUsed: {
74
+ input: this.tokenCounts.input,
75
+ output: this.tokenCounts.output,
76
+ total: this.tokenCounts.input + this.tokenCounts.output,
77
+ },
78
+ cached: false,
79
+ model: request.model,
80
+ latencyMs: this.latencyMs,
81
+ };
82
+ }
83
+
84
+ async embed(request: EmbedRequest): Promise<EmbeddingResponse> {
85
+ this.embedHistory.push(request);
86
+
87
+ if (this.onEmbed) {
88
+ this.onEmbed(request);
89
+ }
90
+
91
+ if (this.latencyMs > 0) {
92
+ await delay(this.latencyMs);
93
+ }
94
+
95
+ if (this.throwError) {
96
+ throw this.throwError;
97
+ }
98
+
99
+ const inputs = Array.isArray(request.input) ? request.input : [request.input];
100
+ const embeddings = inputs.map(() => [...this.embedding]);
101
+
102
+ return {
103
+ embeddings,
104
+ tokensUsed: inputs.length * 5,
105
+ model: request.model,
106
+ cached: false,
107
+ };
108
+ }
109
+ }
110
+
111
+ function delay(ms: number): Promise<void> {
112
+ return new Promise((resolve) => setTimeout(resolve, ms));
113
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * dare-llm-integration — LLMProvider interface
3
+ * License: MIT
4
+ */
5
+
6
+ export type { LLMProvider } from '../types.js';