@nac3/forge-cli 0.2.0-alpha.1

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 (561) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +371 -0
  3. package/dist/bin/yf.d.ts +5 -0
  4. package/dist/bin/yf.d.ts.map +1 -0
  5. package/dist/bin/yf.js +86 -0
  6. package/dist/bin/yf.js.map +1 -0
  7. package/dist/chat/claude.d.ts +100 -0
  8. package/dist/chat/claude.d.ts.map +1 -0
  9. package/dist/chat/claude.js +228 -0
  10. package/dist/chat/claude.js.map +1 -0
  11. package/dist/chat/ingest_session.d.ts +97 -0
  12. package/dist/chat/ingest_session.d.ts.map +1 -0
  13. package/dist/chat/ingest_session.js +99 -0
  14. package/dist/chat/ingest_session.js.map +1 -0
  15. package/dist/chat/panel.d.ts +15 -0
  16. package/dist/chat/panel.d.ts.map +1 -0
  17. package/dist/chat/panel.js +1526 -0
  18. package/dist/chat/panel.js.map +1 -0
  19. package/dist/chat/persistence.d.ts +37 -0
  20. package/dist/chat/persistence.d.ts.map +1 -0
  21. package/dist/chat/persistence.js +91 -0
  22. package/dist/chat/persistence.js.map +1 -0
  23. package/dist/chat/server.d.ts +34 -0
  24. package/dist/chat/server.d.ts.map +1 -0
  25. package/dist/chat/server.js +1540 -0
  26. package/dist/chat/server.js.map +1 -0
  27. package/dist/chat/spec_extract.d.ts +35 -0
  28. package/dist/chat/spec_extract.d.ts.map +1 -0
  29. package/dist/chat/spec_extract.js +152 -0
  30. package/dist/chat/spec_extract.js.map +1 -0
  31. package/dist/chat/spec_plan.d.ts +65 -0
  32. package/dist/chat/spec_plan.d.ts.map +1 -0
  33. package/dist/chat/spec_plan.js +160 -0
  34. package/dist/chat/spec_plan.js.map +1 -0
  35. package/dist/chat/spec_scaffold.d.ts +95 -0
  36. package/dist/chat/spec_scaffold.d.ts.map +1 -0
  37. package/dist/chat/spec_scaffold.js +220 -0
  38. package/dist/chat/spec_scaffold.js.map +1 -0
  39. package/dist/chat/tools/git.d.ts +59 -0
  40. package/dist/chat/tools/git.d.ts.map +1 -0
  41. package/dist/chat/tools/git.js +313 -0
  42. package/dist/chat/tools/git.js.map +1 -0
  43. package/dist/chat/tools/github.d.ts +59 -0
  44. package/dist/chat/tools/github.d.ts.map +1 -0
  45. package/dist/chat/tools/github.js +310 -0
  46. package/dist/chat/tools/github.js.map +1 -0
  47. package/dist/chat/tools/lifecycle.d.ts +82 -0
  48. package/dist/chat/tools/lifecycle.d.ts.map +1 -0
  49. package/dist/chat/tools/lifecycle.js +295 -0
  50. package/dist/chat/tools/lifecycle.js.map +1 -0
  51. package/dist/chat/tools/manual.d.ts +26 -0
  52. package/dist/chat/tools/manual.d.ts.map +1 -0
  53. package/dist/chat/tools/manual.js +164 -0
  54. package/dist/chat/tools/manual.js.map +1 -0
  55. package/dist/chat/tools/reader.d.ts +80 -0
  56. package/dist/chat/tools/reader.d.ts.map +1 -0
  57. package/dist/chat/tools/reader.js +471 -0
  58. package/dist/chat/tools/reader.js.map +1 -0
  59. package/dist/chat/tools.d.ts +106 -0
  60. package/dist/chat/tools.d.ts.map +1 -0
  61. package/dist/chat/tools.js +587 -0
  62. package/dist/chat/tools.js.map +1 -0
  63. package/dist/codegen/e2e.d.ts +106 -0
  64. package/dist/codegen/e2e.d.ts.map +1 -0
  65. package/dist/codegen/e2e.js +931 -0
  66. package/dist/codegen/e2e.js.map +1 -0
  67. package/dist/codegen/v3_flow_emit.d.ts +70 -0
  68. package/dist/codegen/v3_flow_emit.d.ts.map +1 -0
  69. package/dist/codegen/v3_flow_emit.js +225 -0
  70. package/dist/codegen/v3_flow_emit.js.map +1 -0
  71. package/dist/commands/_stub.d.ts +2 -0
  72. package/dist/commands/_stub.d.ts.map +1 -0
  73. package/dist/commands/_stub.js +21 -0
  74. package/dist/commands/_stub.js.map +1 -0
  75. package/dist/commands/app.d.ts +31 -0
  76. package/dist/commands/app.d.ts.map +1 -0
  77. package/dist/commands/app.js +331 -0
  78. package/dist/commands/app.js.map +1 -0
  79. package/dist/commands/chat.d.ts +18 -0
  80. package/dist/commands/chat.d.ts.map +1 -0
  81. package/dist/commands/chat.js +76 -0
  82. package/dist/commands/chat.js.map +1 -0
  83. package/dist/commands/deploy.d.ts +21 -0
  84. package/dist/commands/deploy.d.ts.map +1 -0
  85. package/dist/commands/deploy.js +121 -0
  86. package/dist/commands/deploy.js.map +1 -0
  87. package/dist/commands/doctor.d.ts +14 -0
  88. package/dist/commands/doctor.d.ts.map +1 -0
  89. package/dist/commands/doctor.js +280 -0
  90. package/dist/commands/doctor.js.map +1 -0
  91. package/dist/commands/figma.d.ts +32 -0
  92. package/dist/commands/figma.d.ts.map +1 -0
  93. package/dist/commands/figma.js +141 -0
  94. package/dist/commands/figma.js.map +1 -0
  95. package/dist/commands/gen-flow-tests.d.ts +8 -0
  96. package/dist/commands/gen-flow-tests.d.ts.map +1 -0
  97. package/dist/commands/gen-flow-tests.js +78 -0
  98. package/dist/commands/gen-flow-tests.js.map +1 -0
  99. package/dist/commands/gen-tests.d.ts +9 -0
  100. package/dist/commands/gen-tests.d.ts.map +1 -0
  101. package/dist/commands/gen-tests.js +118 -0
  102. package/dist/commands/gen-tests.js.map +1 -0
  103. package/dist/commands/license.d.ts +14 -0
  104. package/dist/commands/license.d.ts.map +1 -0
  105. package/dist/commands/license.js +182 -0
  106. package/dist/commands/license.js.map +1 -0
  107. package/dist/commands/log.d.ts +19 -0
  108. package/dist/commands/log.d.ts.map +1 -0
  109. package/dist/commands/log.js +101 -0
  110. package/dist/commands/log.js.map +1 -0
  111. package/dist/commands/migrate.d.ts +118 -0
  112. package/dist/commands/migrate.d.ts.map +1 -0
  113. package/dist/commands/migrate.js +1410 -0
  114. package/dist/commands/migrate.js.map +1 -0
  115. package/dist/commands/mobile.d.ts +27 -0
  116. package/dist/commands/mobile.d.ts.map +1 -0
  117. package/dist/commands/mobile.js +90 -0
  118. package/dist/commands/mobile.js.map +1 -0
  119. package/dist/commands/new.d.ts +32 -0
  120. package/dist/commands/new.d.ts.map +1 -0
  121. package/dist/commands/new.js +107 -0
  122. package/dist/commands/new.js.map +1 -0
  123. package/dist/commands/pilot.d.ts +8 -0
  124. package/dist/commands/pilot.d.ts.map +1 -0
  125. package/dist/commands/pilot.js +104 -0
  126. package/dist/commands/pilot.js.map +1 -0
  127. package/dist/commands/projects.d.ts +21 -0
  128. package/dist/commands/projects.d.ts.map +1 -0
  129. package/dist/commands/projects.js +238 -0
  130. package/dist/commands/projects.js.map +1 -0
  131. package/dist/commands/publish.d.ts +35 -0
  132. package/dist/commands/publish.d.ts.map +1 -0
  133. package/dist/commands/publish.js +194 -0
  134. package/dist/commands/publish.js.map +1 -0
  135. package/dist/commands/repo.d.ts +59 -0
  136. package/dist/commands/repo.d.ts.map +1 -0
  137. package/dist/commands/repo.js +178 -0
  138. package/dist/commands/repo.js.map +1 -0
  139. package/dist/commands/review-screens.d.ts +28 -0
  140. package/dist/commands/review-screens.d.ts.map +1 -0
  141. package/dist/commands/review-screens.js +345 -0
  142. package/dist/commands/review-screens.js.map +1 -0
  143. package/dist/commands/scenarios.d.ts +23 -0
  144. package/dist/commands/scenarios.d.ts.map +1 -0
  145. package/dist/commands/scenarios.js +304 -0
  146. package/dist/commands/scenarios.js.map +1 -0
  147. package/dist/commands/ship.d.ts +18 -0
  148. package/dist/commands/ship.d.ts.map +1 -0
  149. package/dist/commands/ship.js +41 -0
  150. package/dist/commands/ship.js.map +1 -0
  151. package/dist/commands/test.d.ts +29 -0
  152. package/dist/commands/test.d.ts.map +1 -0
  153. package/dist/commands/test.js +62 -0
  154. package/dist/commands/test.js.map +1 -0
  155. package/dist/commands/tunnel.d.ts +22 -0
  156. package/dist/commands/tunnel.d.ts.map +1 -0
  157. package/dist/commands/tunnel.js +77 -0
  158. package/dist/commands/tunnel.js.map +1 -0
  159. package/dist/commands/validate.d.ts +14 -0
  160. package/dist/commands/validate.d.ts.map +1 -0
  161. package/dist/commands/validate.js +51 -0
  162. package/dist/commands/validate.js.map +1 -0
  163. package/dist/commands/vault.d.ts +32 -0
  164. package/dist/commands/vault.d.ts.map +1 -0
  165. package/dist/commands/vault.js +489 -0
  166. package/dist/commands/vault.js.map +1 -0
  167. package/dist/commands/voice.d.ts +16 -0
  168. package/dist/commands/voice.d.ts.map +1 -0
  169. package/dist/commands/voice.js +69 -0
  170. package/dist/commands/voice.js.map +1 -0
  171. package/dist/core/cascade_router.d.ts +90 -0
  172. package/dist/core/cascade_router.d.ts.map +1 -0
  173. package/dist/core/cascade_router.js +131 -0
  174. package/dist/core/cascade_router.js.map +1 -0
  175. package/dist/core/cf_tunnel.d.ts +52 -0
  176. package/dist/core/cf_tunnel.d.ts.map +1 -0
  177. package/dist/core/cf_tunnel.js +134 -0
  178. package/dist/core/cf_tunnel.js.map +1 -0
  179. package/dist/core/gha_dispatcher.d.ts +48 -0
  180. package/dist/core/gha_dispatcher.d.ts.map +1 -0
  181. package/dist/core/gha_dispatcher.js +198 -0
  182. package/dist/core/gha_dispatcher.js.map +1 -0
  183. package/dist/core/logger.d.ts +89 -0
  184. package/dist/core/logger.d.ts.map +1 -0
  185. package/dist/core/logger.js +245 -0
  186. package/dist/core/logger.js.map +1 -0
  187. package/dist/core/mode.d.ts +26 -0
  188. package/dist/core/mode.d.ts.map +1 -0
  189. package/dist/core/mode.js +122 -0
  190. package/dist/core/mode.js.map +1 -0
  191. package/dist/core/pairing.d.ts +40 -0
  192. package/dist/core/pairing.d.ts.map +1 -0
  193. package/dist/core/pairing.js +145 -0
  194. package/dist/core/pairing.js.map +1 -0
  195. package/dist/core/pilot_setup.d.ts +29 -0
  196. package/dist/core/pilot_setup.d.ts.map +1 -0
  197. package/dist/core/pilot_setup.js +119 -0
  198. package/dist/core/pilot_setup.js.map +1 -0
  199. package/dist/core/polar.d.ts +81 -0
  200. package/dist/core/polar.d.ts.map +1 -0
  201. package/dist/core/polar.js +175 -0
  202. package/dist/core/polar.js.map +1 -0
  203. package/dist/core/project_picker.d.ts +56 -0
  204. package/dist/core/project_picker.d.ts.map +1 -0
  205. package/dist/core/project_picker.js +86 -0
  206. package/dist/core/project_picker.js.map +1 -0
  207. package/dist/core/projects.d.ts +58 -0
  208. package/dist/core/projects.d.ts.map +1 -0
  209. package/dist/core/projects.js +146 -0
  210. package/dist/core/projects.js.map +1 -0
  211. package/dist/core/projects_sync.d.ts +80 -0
  212. package/dist/core/projects_sync.d.ts.map +1 -0
  213. package/dist/core/projects_sync.js +278 -0
  214. package/dist/core/projects_sync.js.map +1 -0
  215. package/dist/core/remote_runner.d.ts +70 -0
  216. package/dist/core/remote_runner.d.ts.map +1 -0
  217. package/dist/core/remote_runner.js +133 -0
  218. package/dist/core/remote_runner.js.map +1 -0
  219. package/dist/core/repo_state.d.ts +24 -0
  220. package/dist/core/repo_state.d.ts.map +1 -0
  221. package/dist/core/repo_state.js +109 -0
  222. package/dist/core/repo_state.js.map +1 -0
  223. package/dist/core/target.d.ts +31 -0
  224. package/dist/core/target.d.ts.map +1 -0
  225. package/dist/core/target.js +121 -0
  226. package/dist/core/target.js.map +1 -0
  227. package/dist/deploy/aws.d.ts +43 -0
  228. package/dist/deploy/aws.d.ts.map +1 -0
  229. package/dist/deploy/aws.js +173 -0
  230. package/dist/deploy/aws.js.map +1 -0
  231. package/dist/figma/api.d.ts +35 -0
  232. package/dist/figma/api.d.ts.map +1 -0
  233. package/dist/figma/api.js +40 -0
  234. package/dist/figma/api.js.map +1 -0
  235. package/dist/figma/decorator.d.ts +74 -0
  236. package/dist/figma/decorator.d.ts.map +1 -0
  237. package/dist/figma/decorator.js +210 -0
  238. package/dist/figma/decorator.js.map +1 -0
  239. package/dist/figma/heuristics.d.ts +29 -0
  240. package/dist/figma/heuristics.d.ts.map +1 -0
  241. package/dist/figma/heuristics.js +110 -0
  242. package/dist/figma/heuristics.js.map +1 -0
  243. package/dist/figma/normalize.d.ts +33 -0
  244. package/dist/figma/normalize.d.ts.map +1 -0
  245. package/dist/figma/normalize.js +101 -0
  246. package/dist/figma/normalize.js.map +1 -0
  247. package/dist/figma/tokens.d.ts +23 -0
  248. package/dist/figma/tokens.d.ts.map +1 -0
  249. package/dist/figma/tokens.js +111 -0
  250. package/dist/figma/tokens.js.map +1 -0
  251. package/dist/figma/types.d.ts +118 -0
  252. package/dist/figma/types.d.ts.map +1 -0
  253. package/dist/figma/types.js +12 -0
  254. package/dist/figma/types.js.map +1 -0
  255. package/dist/i18n/index.d.ts +48 -0
  256. package/dist/i18n/index.d.ts.map +1 -0
  257. package/dist/i18n/index.js +135 -0
  258. package/dist/i18n/index.js.map +1 -0
  259. package/dist/i18n/types.d.ts +52 -0
  260. package/dist/i18n/types.d.ts.map +1 -0
  261. package/dist/i18n/types.js +85 -0
  262. package/dist/i18n/types.js.map +1 -0
  263. package/dist/index.d.ts +12 -0
  264. package/dist/index.d.ts.map +1 -0
  265. package/dist/index.js +11 -0
  266. package/dist/index.js.map +1 -0
  267. package/dist/lan/mdns_packet.d.ts +74 -0
  268. package/dist/lan/mdns_packet.d.ts.map +1 -0
  269. package/dist/lan/mdns_packet.js +247 -0
  270. package/dist/lan/mdns_packet.js.map +1 -0
  271. package/dist/lan/mdns_service.d.ts +102 -0
  272. package/dist/lan/mdns_service.d.ts.map +1 -0
  273. package/dist/lan/mdns_service.js +206 -0
  274. package/dist/lan/mdns_service.js.map +1 -0
  275. package/dist/license/activate.d.ts +33 -0
  276. package/dist/license/activate.d.ts.map +1 -0
  277. package/dist/license/activate.js +135 -0
  278. package/dist/license/activate.js.map +1 -0
  279. package/dist/license/fingerprint.d.ts +2 -0
  280. package/dist/license/fingerprint.d.ts.map +1 -0
  281. package/dist/license/fingerprint.js +29 -0
  282. package/dist/license/fingerprint.js.map +1 -0
  283. package/dist/license/hito4_client.d.ts +24 -0
  284. package/dist/license/hito4_client.d.ts.map +1 -0
  285. package/dist/license/hito4_client.js +103 -0
  286. package/dist/license/hito4_client.js.map +1 -0
  287. package/dist/license/index.d.ts +22 -0
  288. package/dist/license/index.d.ts.map +1 -0
  289. package/dist/license/index.js +125 -0
  290. package/dist/license/index.js.map +1 -0
  291. package/dist/license/types.d.ts +38 -0
  292. package/dist/license/types.d.ts.map +1 -0
  293. package/dist/license/types.js +9 -0
  294. package/dist/license/types.js.map +1 -0
  295. package/dist/migrate/ai-apply.d.ts +198 -0
  296. package/dist/migrate/ai-apply.d.ts.map +1 -0
  297. package/dist/migrate/ai-apply.js +833 -0
  298. package/dist/migrate/ai-apply.js.map +1 -0
  299. package/dist/migrate/ai-decorator.d.ts +87 -0
  300. package/dist/migrate/ai-decorator.d.ts.map +1 -0
  301. package/dist/migrate/ai-decorator.js +203 -0
  302. package/dist/migrate/ai-decorator.js.map +1 -0
  303. package/dist/migrate/apply.d.ts +28 -0
  304. package/dist/migrate/apply.d.ts.map +1 -0
  305. package/dist/migrate/apply.js +119 -0
  306. package/dist/migrate/apply.js.map +1 -0
  307. package/dist/migrate/audit.d.ts +9 -0
  308. package/dist/migrate/audit.d.ts.map +1 -0
  309. package/dist/migrate/audit.js +197 -0
  310. package/dist/migrate/audit.js.map +1 -0
  311. package/dist/migrate/diff.d.ts +28 -0
  312. package/dist/migrate/diff.d.ts.map +1 -0
  313. package/dist/migrate/diff.js +154 -0
  314. package/dist/migrate/diff.js.map +1 -0
  315. package/dist/migrate/html-orchestrator.d.ts +81 -0
  316. package/dist/migrate/html-orchestrator.d.ts.map +1 -0
  317. package/dist/migrate/html-orchestrator.js +233 -0
  318. package/dist/migrate/html-orchestrator.js.map +1 -0
  319. package/dist/migrate/html-walker.d.ts +93 -0
  320. package/dist/migrate/html-walker.d.ts.map +1 -0
  321. package/dist/migrate/html-walker.js +288 -0
  322. package/dist/migrate/html-walker.js.map +1 -0
  323. package/dist/migrate/js-template-walker.d.ts +118 -0
  324. package/dist/migrate/js-template-walker.d.ts.map +1 -0
  325. package/dist/migrate/js-template-walker.js +644 -0
  326. package/dist/migrate/js-template-walker.js.map +1 -0
  327. package/dist/migrate/manifest-validator.d.ts +30 -0
  328. package/dist/migrate/manifest-validator.d.ts.map +1 -0
  329. package/dist/migrate/manifest-validator.js +261 -0
  330. package/dist/migrate/manifest-validator.js.map +1 -0
  331. package/dist/migrate/overrides.d.ts +58 -0
  332. package/dist/migrate/overrides.d.ts.map +1 -0
  333. package/dist/migrate/overrides.js +193 -0
  334. package/dist/migrate/overrides.js.map +1 -0
  335. package/dist/migrate/plugin-scope.d.ts +42 -0
  336. package/dist/migrate/plugin-scope.d.ts.map +1 -0
  337. package/dist/migrate/plugin-scope.js +94 -0
  338. package/dist/migrate/plugin-scope.js.map +1 -0
  339. package/dist/migrate/types.d.ts +45 -0
  340. package/dist/migrate/types.d.ts.map +1 -0
  341. package/dist/migrate/types.js +9 -0
  342. package/dist/migrate/types.js.map +1 -0
  343. package/dist/migrate/verb-inference.d.ts +37 -0
  344. package/dist/migrate/verb-inference.d.ts.map +1 -0
  345. package/dist/migrate/verb-inference.js +274 -0
  346. package/dist/migrate/verb-inference.js.map +1 -0
  347. package/dist/nac3/attrs.d.ts +87 -0
  348. package/dist/nac3/attrs.d.ts.map +1 -0
  349. package/dist/nac3/attrs.js +134 -0
  350. package/dist/nac3/attrs.js.map +1 -0
  351. package/dist/nac3/scenario_dsl.d.ts +71 -0
  352. package/dist/nac3/scenario_dsl.d.ts.map +1 -0
  353. package/dist/nac3/scenario_dsl.js +191 -0
  354. package/dist/nac3/scenario_dsl.js.map +1 -0
  355. package/dist/nac3/tokens.d.ts +126 -0
  356. package/dist/nac3/tokens.d.ts.map +1 -0
  357. package/dist/nac3/tokens.js +138 -0
  358. package/dist/nac3/tokens.js.map +1 -0
  359. package/dist/reader/parsers/csv.d.ts +42 -0
  360. package/dist/reader/parsers/csv.d.ts.map +1 -0
  361. package/dist/reader/parsers/csv.js +221 -0
  362. package/dist/reader/parsers/csv.js.map +1 -0
  363. package/dist/reader/parsers/docx.d.ts +31 -0
  364. package/dist/reader/parsers/docx.d.ts.map +1 -0
  365. package/dist/reader/parsers/docx.js +51 -0
  366. package/dist/reader/parsers/docx.js.map +1 -0
  367. package/dist/reader/parsers/epub.d.ts +39 -0
  368. package/dist/reader/parsers/epub.d.ts.map +1 -0
  369. package/dist/reader/parsers/epub.js +265 -0
  370. package/dist/reader/parsers/epub.js.map +1 -0
  371. package/dist/reader/parsers/html.d.ts +40 -0
  372. package/dist/reader/parsers/html.d.ts.map +1 -0
  373. package/dist/reader/parsers/html.js +386 -0
  374. package/dist/reader/parsers/html.js.map +1 -0
  375. package/dist/reader/parsers/md.d.ts +30 -0
  376. package/dist/reader/parsers/md.d.ts.map +1 -0
  377. package/dist/reader/parsers/md.js +199 -0
  378. package/dist/reader/parsers/md.js.map +1 -0
  379. package/dist/reader/parsers/pdf.d.ts +39 -0
  380. package/dist/reader/parsers/pdf.d.ts.map +1 -0
  381. package/dist/reader/parsers/pdf.js +220 -0
  382. package/dist/reader/parsers/pdf.js.map +1 -0
  383. package/dist/reader/parsers/rtf.d.ts +37 -0
  384. package/dist/reader/parsers/rtf.d.ts.map +1 -0
  385. package/dist/reader/parsers/rtf.js +347 -0
  386. package/dist/reader/parsers/rtf.js.map +1 -0
  387. package/dist/reader/parsers/source.d.ts +32 -0
  388. package/dist/reader/parsers/source.d.ts.map +1 -0
  389. package/dist/reader/parsers/source.js +122 -0
  390. package/dist/reader/parsers/source.js.map +1 -0
  391. package/dist/reader/parsers/txt.d.ts +25 -0
  392. package/dist/reader/parsers/txt.d.ts.map +1 -0
  393. package/dist/reader/parsers/txt.js +56 -0
  394. package/dist/reader/parsers/txt.js.map +1 -0
  395. package/dist/reader/parsers/xlsx.d.ts +33 -0
  396. package/dist/reader/parsers/xlsx.d.ts.map +1 -0
  397. package/dist/reader/parsers/xlsx.js +143 -0
  398. package/dist/reader/parsers/xlsx.js.map +1 -0
  399. package/dist/reader/registry.d.ts +39 -0
  400. package/dist/reader/registry.d.ts.map +1 -0
  401. package/dist/reader/registry.js +172 -0
  402. package/dist/reader/registry.js.map +1 -0
  403. package/dist/reader/search.d.ts +27 -0
  404. package/dist/reader/search.d.ts.map +1 -0
  405. package/dist/reader/search.js +77 -0
  406. package/dist/reader/search.js.map +1 -0
  407. package/dist/reader/state.d.ts +56 -0
  408. package/dist/reader/state.d.ts.map +1 -0
  409. package/dist/reader/state.js +179 -0
  410. package/dist/reader/state.js.map +1 -0
  411. package/dist/reader/types.d.ts +119 -0
  412. package/dist/reader/types.d.ts.map +1 -0
  413. package/dist/reader/types.js +23 -0
  414. package/dist/reader/types.js.map +1 -0
  415. package/dist/ship/run.d.ts +26 -0
  416. package/dist/ship/run.d.ts.map +1 -0
  417. package/dist/ship/run.js +123 -0
  418. package/dist/ship/run.js.map +1 -0
  419. package/dist/template/index.d.ts +50 -0
  420. package/dist/template/index.d.ts.map +1 -0
  421. package/dist/template/index.js +140 -0
  422. package/dist/template/index.js.map +1 -0
  423. package/dist/ui/colors.d.ts +13 -0
  424. package/dist/ui/colors.d.ts.map +1 -0
  425. package/dist/ui/colors.js +26 -0
  426. package/dist/ui/colors.js.map +1 -0
  427. package/dist/validate/index.d.ts +19 -0
  428. package/dist/validate/index.d.ts.map +1 -0
  429. package/dist/validate/index.js +181 -0
  430. package/dist/validate/index.js.map +1 -0
  431. package/dist/vault/catalog.d.ts +55 -0
  432. package/dist/vault/catalog.d.ts.map +1 -0
  433. package/dist/vault/catalog.js +424 -0
  434. package/dist/vault/catalog.js.map +1 -0
  435. package/dist/vault/crypto.d.ts +82 -0
  436. package/dist/vault/crypto.d.ts.map +1 -0
  437. package/dist/vault/crypto.js +173 -0
  438. package/dist/vault/crypto.js.map +1 -0
  439. package/dist/vault/git_askpass.d.ts +26 -0
  440. package/dist/vault/git_askpass.d.ts.map +1 -0
  441. package/dist/vault/git_askpass.js +104 -0
  442. package/dist/vault/git_askpass.js.map +1 -0
  443. package/dist/vault/migrator.d.ts +57 -0
  444. package/dist/vault/migrator.d.ts.map +1 -0
  445. package/dist/vault/migrator.js +204 -0
  446. package/dist/vault/migrator.js.map +1 -0
  447. package/dist/vault/redactor.d.ts +73 -0
  448. package/dist/vault/redactor.d.ts.map +1 -0
  449. package/dist/vault/redactor.js +182 -0
  450. package/dist/vault/redactor.js.map +1 -0
  451. package/dist/vault/store.d.ts +132 -0
  452. package/dist/vault/store.d.ts.map +1 -0
  453. package/dist/vault/store.js +335 -0
  454. package/dist/vault/store.js.map +1 -0
  455. package/dist/version.d.ts +8 -0
  456. package/dist/version.d.ts.map +1 -0
  457. package/dist/version.js +8 -0
  458. package/dist/version.js.map +1 -0
  459. package/dist/voice/chunker.d.ts +43 -0
  460. package/dist/voice/chunker.d.ts.map +1 -0
  461. package/dist/voice/chunker.js +133 -0
  462. package/dist/voice/chunker.js.map +1 -0
  463. package/dist/voice/config.d.ts +14 -0
  464. package/dist/voice/config.d.ts.map +1 -0
  465. package/dist/voice/config.js +51 -0
  466. package/dist/voice/config.js.map +1 -0
  467. package/dist/voice/intents.d.ts +71 -0
  468. package/dist/voice/intents.d.ts.map +1 -0
  469. package/dist/voice/intents.js +0 -0
  470. package/dist/voice/intents.js.map +1 -0
  471. package/dist/voice/providers/elevenlabs.d.ts +53 -0
  472. package/dist/voice/providers/elevenlabs.d.ts.map +1 -0
  473. package/dist/voice/providers/elevenlabs.js +159 -0
  474. package/dist/voice/providers/elevenlabs.js.map +1 -0
  475. package/dist/voice/providers/google.d.ts +56 -0
  476. package/dist/voice/providers/google.d.ts.map +1 -0
  477. package/dist/voice/providers/google.js +253 -0
  478. package/dist/voice/providers/google.js.map +1 -0
  479. package/dist/voice/providers/whisper.d.ts +44 -0
  480. package/dist/voice/providers/whisper.d.ts.map +1 -0
  481. package/dist/voice/providers/whisper.js +179 -0
  482. package/dist/voice/providers/whisper.js.map +1 -0
  483. package/dist/voice/registry.d.ts +35 -0
  484. package/dist/voice/registry.d.ts.map +1 -0
  485. package/dist/voice/registry.js +48 -0
  486. package/dist/voice/registry.js.map +1 -0
  487. package/dist/voice/router.d.ts +62 -0
  488. package/dist/voice/router.d.ts.map +1 -0
  489. package/dist/voice/router.js +175 -0
  490. package/dist/voice/router.js.map +1 -0
  491. package/dist/voice/types.d.ts +116 -0
  492. package/dist/voice/types.d.ts.map +1 -0
  493. package/dist/voice/types.js +22 -0
  494. package/dist/voice/types.js.map +1 -0
  495. package/dist/voice/voiceprint/enrollment.d.ts +36 -0
  496. package/dist/voice/voiceprint/enrollment.d.ts.map +1 -0
  497. package/dist/voice/voiceprint/enrollment.js +71 -0
  498. package/dist/voice/voiceprint/enrollment.js.map +1 -0
  499. package/dist/voice/voiceprint/identify.d.ts +16 -0
  500. package/dist/voice/voiceprint/identify.d.ts.map +1 -0
  501. package/dist/voice/voiceprint/identify.js +20 -0
  502. package/dist/voice/voiceprint/identify.js.map +1 -0
  503. package/dist/voice/voiceprint/liveness.d.ts +90 -0
  504. package/dist/voice/voiceprint/liveness.d.ts.map +1 -0
  505. package/dist/voice/voiceprint/liveness.js +251 -0
  506. package/dist/voice/voiceprint/liveness.js.map +1 -0
  507. package/dist/voice/voiceprint/match.d.ts +54 -0
  508. package/dist/voice/voiceprint/match.d.ts.map +1 -0
  509. package/dist/voice/voiceprint/match.js +88 -0
  510. package/dist/voice/voiceprint/match.js.map +1 -0
  511. package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts +44 -0
  512. package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts.map +1 -0
  513. package/dist/voice/voiceprint/providers/local-mfcc-stub.js +92 -0
  514. package/dist/voice/voiceprint/providers/local-mfcc-stub.js.map +1 -0
  515. package/dist/voice/voiceprint/store.d.ts +60 -0
  516. package/dist/voice/voiceprint/store.d.ts.map +1 -0
  517. package/dist/voice/voiceprint/store.js +155 -0
  518. package/dist/voice/voiceprint/store.js.map +1 -0
  519. package/dist/voice/voiceprint/trust.d.ts +90 -0
  520. package/dist/voice/voiceprint/trust.d.ts.map +1 -0
  521. package/dist/voice/voiceprint/trust.js +150 -0
  522. package/dist/voice/voiceprint/trust.js.map +1 -0
  523. package/dist/voice/voiceprint/types.d.ts +100 -0
  524. package/dist/voice/voiceprint/types.d.ts.map +1 -0
  525. package/dist/voice/voiceprint/types.js +23 -0
  526. package/dist/voice/voiceprint/types.js.map +1 -0
  527. package/dist/voice/wake.d.ts +64 -0
  528. package/dist/voice/wake.d.ts.map +1 -0
  529. package/dist/voice/wake.js +143 -0
  530. package/dist/voice/wake.js.map +1 -0
  531. package/docs/manuals/manual.ar.html +91 -0
  532. package/docs/manuals/manual.de.html +100 -0
  533. package/docs/manuals/manual.en.html +118 -0
  534. package/docs/manuals/manual.es.html +120 -0
  535. package/docs/manuals/manual.fr.html +102 -0
  536. package/docs/manuals/manual.hi.html +93 -0
  537. package/docs/manuals/manual.it.html +93 -0
  538. package/docs/manuals/manual.ja.html +97 -0
  539. package/docs/manuals/manual.pt.html +103 -0
  540. package/docs/manuals/manual.zh.html +89 -0
  541. package/package.json +94 -0
  542. package/src/i18n/catalogs/ar.json +86 -0
  543. package/src/i18n/catalogs/de.json +86 -0
  544. package/src/i18n/catalogs/en.json +86 -0
  545. package/src/i18n/catalogs/es.json +86 -0
  546. package/src/i18n/catalogs/fr.json +86 -0
  547. package/src/i18n/catalogs/hi.json +86 -0
  548. package/src/i18n/catalogs/it.json +86 -0
  549. package/src/i18n/catalogs/ja.json +86 -0
  550. package/src/i18n/catalogs/pt.json +86 -0
  551. package/src/i18n/catalogs/zh.json +86 -0
  552. package/templates/react-app/README.md +43 -0
  553. package/templates/react-app/index.html +12 -0
  554. package/templates/react-app/package.json +35 -0
  555. package/templates/react-app/src/App.tsx +106 -0
  556. package/templates/react-app/src/main.tsx +21 -0
  557. package/templates/react-app/src/nac/manifest.ts +46 -0
  558. package/templates/react-app/src/styles.css +68 -0
  559. package/templates/react-app/tsconfig.json +19 -0
  560. package/templates/react-app/vite.config.ts +12 -0
  561. package/templates/react-app/yujin.forge.json +15 -0
@@ -0,0 +1,833 @@
1
+ /*
2
+ * src/migrate/ai-apply.ts
3
+ *
4
+ * Claude-powered JSX decoration. Per-file LLM call that adds
5
+ * data-nac-id / data-nac-role / data-nac-action attributes to JSX
6
+ * interactive elements, and returns a manifest fragment listing
7
+ * what it decorated. Forge aggregates all fragments into a single
8
+ * top-level manifest.json.
9
+ *
10
+ * The JSX-side counterpart to ai-decorator.ts (which works on
11
+ * static HTML). For React apps the source-level decoration is what
12
+ * persists across builds; static-HTML decoration would be lost on
13
+ * the next Vite build.
14
+ *
15
+ * Single-call per file. Sonnet 4.6 by default. temperature=0 for
16
+ * determinism (verified on calc baseline).
17
+ *
18
+ * ASCII-only.
19
+ */
20
+ import { promises as fs } from 'node:fs';
21
+ import path from 'node:path';
22
+ import Anthropic from '@anthropic-ai/sdk';
23
+ import { validateAiApplyFragment, formatFindingsForRetry, } from './manifest-validator.js';
24
+ const DEFAULT_MODEL = 'claude-sonnet-4-6';
25
+ const PRICING = {
26
+ 'claude-sonnet-4-6': { in: 3.00, out: 15.00 },
27
+ 'claude-haiku-4-5': { in: 0.80, out: 4.00 },
28
+ 'claude-haiku-4-5-20251001': { in: 0.80, out: 4.00 },
29
+ 'claude-opus-4-7': { in: 15.00, out: 75.00 },
30
+ };
31
+ function resolveAnthropicModelId(id) {
32
+ if (id === 'claude-haiku-4-5')
33
+ return 'claude-haiku-4-5-20251001';
34
+ return id;
35
+ }
36
+ function computeCost(model, usage) {
37
+ const p = PRICING[model] ?? PRICING['claude-sonnet-4-6'];
38
+ return (usage.input * p.in + usage.output * p.out) / 1_000_000;
39
+ }
40
+ const SYSTEM_PROMPT = `You are a NAC-3 (Native Agent Contract) JSX decorator. You receive one React JSX file and return:
41
+ (a) the SAME file with data-nac-id / data-nac-role / data-nac-action attributes added to every interactive element (buttons, inputs, selects, links that DO something, tabs, navigation items, form fields).
42
+ (b) the manifest fragment listing what you decorated.
43
+
44
+ ROLE TAXONOMY (critical):
45
+ - action: anything the user CLICKS to make something happen.
46
+ - field: an element that HOLDS a VALUE the user can read or write (inputs, textareas, output panels). NEVER classify a value-holding element as region.
47
+ - region: a CONTAINER that groups other elements.
48
+ - option: a single choice inside a select / radio group.
49
+ - tab: a tab-bar entry.
50
+ - navigation: nav link that changes route.
51
+
52
+ ID PATTERN: "<plugin_slug>.<role>.<thing>", snake_case, lowercase. Example: "conduit.action.submit", "conduit.field.email", "conduit.region.feed".
53
+
54
+ VERB NAMING: short imperative (submit, login, follow, favorite, publish, delete, save). Avoid prefixes like "do_" or "btn_".
55
+
56
+ PLUGIN SLUG: choose ONE for the whole app. Use the same slug across every file. Default: derive from the project name or a clear semantic guess (e.g. "conduit" for a Medium clone, "todo" for a todo app).
57
+
58
+ PARENT-CHILD ID-PREFIX CONTINUITY (H5, critical when both files in scope):
59
+
60
+ When a PARENT file declares per-instance ids for a CHILD component it
61
+ renders (e.g. <FeedToggler> emits manifest entries
62
+ "conduit.tab.feed__your_feed", "conduit.tab.feed__global_feed" for
63
+ its rendered <FeedNavLink> children), the CHILD component's own
64
+ template-literal decoration MUST use the SAME prefix the parent
65
+ declared. Do NOT derive the child's prefix from the child's component
66
+ name.
67
+
68
+ In the FeedToggler/FeedNavLink example:
69
+ - PARENT FeedToggler.jsx declares per-instance ids using prefix
70
+ "conduit.tab.feed__" (NOT "conduit.tab.feed_nav_link__").
71
+ - CHILD FeedNavLink.jsx must therefore decorate as:
72
+ data-nac-id={\`conduit.tab.feed__\${slugify(name)}\`}
73
+ data-nac-role="tab"
74
+ data-nac-action="activate"
75
+ NOT as conduit.tab.feed_nav_link__\${name}.
76
+ - In the child's manifest "elements" array, the entry's
77
+ "instance_pattern" field carries the SAME parent-prefixed pattern:
78
+ "instance_pattern": "conduit.tab.feed__\${qualifier}"
79
+
80
+ This rule applies to ANY role that supports per-instance qualifiers
81
+ (tab, navigation, action, field, region). The parent's naming wins
82
+ because the parent is the file that knows the user-meaningful names
83
+ of the instances; the child is generic and reused across contexts.
84
+
85
+ To detect parent-declared prefixes from a child's file alone:
86
+ - If the child is imported by exactly one parent in the input set
87
+ AND that parent emits per-instance entries with a common prefix
88
+ "<plugin>.<role>.<X>__" (where X is shorter than the child's
89
+ own component name), use prefix "<plugin>.<role>.<X>__" for the
90
+ child's template, NOT "<plugin>.<role>.<child_component>__".
91
+ - If the parent is NOT in the input set or the prefix can't be
92
+ inferred, fall back to the child-component-name prefix BUT emit
93
+ a warning: "Child component decorated without parent-prefix
94
+ context; ids may not match parent's per-instance declarations."
95
+
96
+ This closes a structural gap previously assumed only at runtime
97
+ (NAC.click_by_verb base→qualified fallback). The fix belongs in
98
+ decoration, not runtime: runtime can't reconcile two prefix schemes
99
+ that don't share a root.
100
+
101
+ REUSABLE COMPONENTS -- PER-INSTANCE QUALIFIER (critical for SPAs):
102
+
103
+ Many React components are written once but rendered N times. If you decorate
104
+ <NavItem> as data-nac-id="conduit.navigation.nav_item", the DOM ends up with
105
+ N copies of the SAME id (Home, Sign in, Sign up, Settings -- all collide).
106
+ An agent cannot disambiguate "click Sign in" from "click Home" in that
107
+ collision.
108
+
109
+ The fix is per-instance qualifier suffixing on data-nac-id. Decide one of
110
+ three strategies BASED ON THE COMPONENT, not the parent:
111
+
112
+ 1. STATIC: the component's identity is determined by a JSX prop / child
113
+ known at AUTHORING time (label="Home", to="/login", icon="trash"). Use
114
+ a JSX template literal that bakes the qualifier in:
115
+ data-nac-id={\`conduit.navigation.nav_item__\${slugify(label)}\`}
116
+ For this case, in your "elements" array, emit ONE entry per known
117
+ instance (look at how the parent uses the component to enumerate them
118
+ -- if the parent file is being decorated and uses <NavItem label="Home"/>,
119
+ <NavItem label="Sign in"/>, emit two entries:
120
+ conduit.navigation.nav_item__home
121
+ conduit.navigation.nav_item__sign_in
122
+ If you cannot see all the parents (the component is exported and the
123
+ parent file is not in this prompt), emit ONE element with the
124
+ "qualifier_source" pointing to the JSX expression you used:
125
+ { "id": "conduit.navigation.nav_item",
126
+ "role": "navigation",
127
+ "actions": [{ "verb": "navigate" }],
128
+ "instance_pattern": "conduit.navigation.nav_item__\${qualifier}",
129
+ "qualifier_source": "props.label",
130
+ "warning": "static qualifier inferred from props.label; see parents for enumerated instances" }
131
+
132
+ 2. DYNAMIC: the component's identity is from data that arrives at runtime
133
+ (an article slug, a comment id, a row's primary key). Use the JSX
134
+ expression that reads the data:
135
+ data-nac-id={\`conduit.action.favorite_article__\${article.slug}\`}
136
+ In your "elements" array, emit ONE template entry:
137
+ { "id": "conduit.action.favorite_article",
138
+ "role": "action",
139
+ "actions": [{ "verb": "favorite" }],
140
+ "instance_pattern": "conduit.action.favorite_article__\${article.slug}",
141
+ "qualifier_source": "article.slug" }
142
+
143
+ 3. UNINFERRABLE: you tried 1 and 2 and there is no obvious qualifier
144
+ (the component is reused but takes no identifying prop). Decorate with
145
+ the base id and emit a WARNING in the entry:
146
+ { "id": "conduit.action.unknown_action",
147
+ "role": "action",
148
+ "actions": [{ "verb": "click" }],
149
+ "warning": "component is reusable but no qualifier could be inferred; N DOM instances will share this id; consider adding a label/id prop or use yf migrate --ai-override to disambiguate manually" }
150
+ NEVER silently collapse. The warning is what keeps the case study honest.
151
+
152
+ QUALIFIER SOURCES, in preference order:
153
+ 1. props that identify content: \`label\`, \`name\`, \`id\`, \`slug\`, \`key\`, \`title\`
154
+ 2. routing props: \`to\`, \`href\` (slugify the path)
155
+ 3. primitive children: <Comp>Sign in</Comp> -> slugify the child text
156
+ 4. semantic icon hints: icon="trash" -> qualifier "trash"
157
+
158
+ SLUGIFY: lowercase, replace whitespace + non-alphanum with "_", trim leading/trailing "_".
159
+ E.g. "Sign in" -> "sign_in", "/login" -> "login", "How to train your dragon" -> "how_to_train_your_dragon".
160
+
161
+ VERB NAMING when adding qualifier: keep the verb generic across instances
162
+ (verb="navigate" for all NavItems, not verb="navigate_to_home"). The
163
+ qualifier is what disambiguates instance; the verb is what disambiguates intent.
164
+
165
+ WHAT TO SKIP:
166
+ - Pure layout components (header / footer / div wrappers with no event handlers).
167
+ - Components that ONLY render children (no own interactive elements).
168
+ - Static text / images that have no onClick / onChange / form handlers.
169
+
170
+ WHAT TO PRESERVE:
171
+ - Every existing attribute, prop, JSX expression. You ONLY add.
172
+ - File structure, imports, exports.
173
+ - All formatting outside the touched elements.
174
+
175
+ ROUTE AWARENESS (H1 -- critical for SPA test generation):
176
+
177
+ If this file declares routes (matches React Router patterns
178
+ <Route path="/something" element={<Component />} />, or
179
+ <Routes>...children...</Routes>, or a route config object), capture
180
+ the URL path the element corresponds to.
181
+
182
+ For each element you decorate, if you can determine the route(s)
183
+ under which the component MOUNTS, emit a "mounted_at" array on the
184
+ element:
185
+
186
+ - For elements declared in routes/Home.jsx that mount under "/":
187
+ "mounted_at": ["/"]
188
+ - For elements declared in routes/Login.jsx that mount under "/login":
189
+ "mounted_at": ["/login"]
190
+ - For nav elements that live in a top-level Navbar always visible:
191
+ "mounted_at": ["*"] (the "always available" sentinel)
192
+
193
+ If you cannot determine the route (the component is a leaf rendered
194
+ by a parent you can't see), omit mounted_at. The aggregator will
195
+ attempt to infer it later from the import graph.
196
+
197
+ If this file IS a Router root (Routes container or a config that
198
+ declares <Route> children), also emit a top-level "route_map" field
199
+ mapping component import path -> URL pattern, e.g.:
200
+ "route_map": {
201
+ "./routes/Home": ["/"],
202
+ "./routes/Login": ["/login"],
203
+ "./routes/ArticleEditor": ["/editor", "/editor/:slug"]
204
+ }
205
+ The Router root file is also the natural place to emit "fixtures"
206
+ (see FIXTURE SEEDING below).
207
+
208
+ ROUTER KIND (H1.v4 -- critical for test URL emission):
209
+ Detect which Router the SPA uses and emit a top-level "router_kind"
210
+ field on the response:
211
+ - <BrowserRouter> -> "router_kind": "browser" (URLs like "/login")
212
+ - <HashRouter> -> "router_kind": "hash" (URLs like "/#/login")
213
+ - <MemoryRouter> -> "router_kind": "memory"
214
+ - default / unsure -> "router_kind": "browser"
215
+ The emitter prefixes test URLs with "#" when router_kind is "hash"
216
+ so cy.visit hits the right SPA route.
217
+
218
+ AUTH GATING (H1 -- critical for test pre-steps):
219
+
220
+ If a route is wrapped in <RequireAuth> or any obvious auth-guard
221
+ component, mark the route as auth-gated in "route_map":
222
+ "/editor": { "paths": ["/editor"], "requires_auth": true }
223
+ And for elements that mount only under auth-gated routes, set on
224
+ the element:
225
+ "requires_auth": true
226
+
227
+ FIXTURE SEEDING (H1.v3 -- critical for dynamic-route tests):
228
+
229
+ If this file is a Router root AND any route contains a path
230
+ parameter (\`:slug\`, \`:username\`, \`:id\`, etc), an e2e test on that
231
+ route cannot simply visit it -- the resource must exist first.
232
+
233
+ For each path parameter, infer the API endpoint that CREATES a
234
+ resource matching that parameter, by inspecting:
235
+ - the file you are decorating right now (Router roots often
236
+ import service modules)
237
+ - other src/services/*.js modules referenced in the imports
238
+ - the JSX of the route component (does it call useParams() with
239
+ a name that maps to this param?)
240
+
241
+ Emit "fixtures" inside route_map for each :param you can resolve.
242
+ Each fixture has a CREATE endpoint and (when possible) a DELETE
243
+ endpoint so each test starts from a clean state and tears down
244
+ after itself. Schema:
245
+
246
+ "fixtures": {
247
+ ":slug": {
248
+ "create_endpoint": "POST /api/articles",
249
+ "create_payload": { "article": { "title": "Forge fixture",
250
+ "body": "Forge fixture body", "tagList": [] } },
251
+ "response_path": "article.slug",
252
+ "delete_endpoint": "DELETE /api/articles/{value}",
253
+ "requires_auth": true,
254
+ "unique_suffix_field": null
255
+ },
256
+ ":username": {
257
+ "create_endpoint": "POST /api/users",
258
+ "create_payload": { "user": { "username": "forge_fixture_{seed}",
259
+ "email": "forge-fixture-{seed}@yujin.app",
260
+ "password": "forge-fixture-2026" } },
261
+ "response_path": "user.username",
262
+ "delete_endpoint": null,
263
+ "requires_auth": false,
264
+ "unique_suffix_field": "user.username,user.email"
265
+ }
266
+ }
267
+
268
+ Conventions:
269
+ - "create_endpoint" is "<METHOD> <PATH>" the SPA's service module
270
+ hits to CREATE the resource.
271
+ - "create_payload" is the minimal body for a 200/201. Use literal
272
+ values. When "unique_suffix_field" is set, the emitter replaces
273
+ {seed} with a DETERMINISTIC per-test suffix derived from the
274
+ test\'s position (NOT Math.random -- the suite must be
275
+ reproducible byte-for-byte across runs).
276
+ - "response_path" is the dotted path from response body to the
277
+ value that fills the URL param (e.g. "article.slug").
278
+ - "delete_endpoint" is "<METHOD> <PATH>" where "{value}" is
279
+ substituted with the captured response value. Set to null only
280
+ when truly no delete exists (e.g. user registration). When set,
281
+ the emitter calls it in afterEach with failOnStatusCode=false
282
+ so a failed test cleans up defensively.
283
+ - "unique_suffix_field" is a CSV list of dotted paths inside
284
+ create_payload where {seed} is substituted with the deterministic
285
+ per-test suffix (e.g. "user.username,user.email"). Required when
286
+ delete_endpoint is null AND the resource persists across tests.
287
+ - "requires_auth" is true when CREATE needs an auth header.
288
+
289
+ DETERMINISM (non-negotiable): never use random data. The emitter
290
+ will pass a deterministic SEED integer per test (the test index
291
+ within the suite) so the same suite produces the same payloads
292
+ across runs. Tests for the same element get the same fixture.
293
+ ISOLATION (non-negotiable): every test creates its own fixture in
294
+ beforeEach and tears it down in afterEach. No state leaks across
295
+ tests; the suite is repeatable from a fresh DB.
296
+
297
+ If you cannot determine a fixture for a given :param, omit it.
298
+ The emitter will skip the corresponding tests with a TODO.
299
+
300
+ OUTPUT FORMAT: a single JSON object, no prose, no markdown fences:
301
+ {
302
+ "decorated_jsx": "<the FULL decorated file content as a single string>",
303
+ "plugin_slug": "<slug>",
304
+ "route_map": "<optional: only when this file is a Router root>",
305
+ "fixtures": "<optional: only when this file is a Router root AND any route uses :params>",
306
+ "elements": [
307
+ {
308
+ "id": "<dotted id, possibly with __qualifier suffix>",
309
+ "role": "action|field|option|tab|region|navigation",
310
+ "actions": [{ "verb": "<verb>" }],
311
+ "instance_pattern": "<optional: present for DYNAMIC qualifiers>",
312
+ "qualifier_source": "<optional: JSX expression backing the qualifier>",
313
+ "warning": "<optional: present for UNINFERRABLE reusable components>",
314
+ "mounted_at": "<optional: array of URL paths where this element appears>",
315
+ "requires_auth": "<optional: true if any of its mount routes is auth-gated>"
316
+ }
317
+ ],
318
+ "decorated": <true | false -- false if the file has no interactive elements>
319
+ }
320
+
321
+ If the file has zero interactive elements to decorate, return:
322
+ { "decorated_jsx": "<original file unchanged>", "plugin_slug": "<slug>", "elements": [], "decorated": false }
323
+ `;
324
+ /* I18N_PROMPT_EXTENSION -- appended to SYSTEM_PROMPT when the
325
+ --with-i18n-tags flag is set. Adds data-i18n="<key>" decoration
326
+ on user-facing text nodes IN PARALLEL with the existing NAC-3
327
+ decoration. The two attribute systems are independent: NAC ids
328
+ are for AGENTS (semantic, language-invariant); i18n keys are for
329
+ HUMAN VIEWERS (translated per locale at runtime via the host's
330
+ i18n shim).
331
+
332
+ Do NOT decorate the SAME element with conflicting keys. Do not
333
+ collide i18n keys across files (use the file's component context
334
+ as a namespace, e.g. "navbar.home", "login.sign_in_button"). */
335
+ const I18N_PROMPT_EXTENSION = `
336
+ I18N DECORATION (ADDITIONAL, runs in parallel with NAC-3 decoration):
337
+
338
+ For every JSX text node that is user-facing (button text, link text,
339
+ heading text, paragraph text, label text, placeholder, title attribute,
340
+ aria-label) AND that is a LITERAL STRING (not derived from a prop /
341
+ variable / expression), add:
342
+
343
+ - "data-i18n" attribute on the text-containing element, holding a
344
+ stable snake_case key. Format: "<surface>.<element>" where surface
345
+ is the namespace (nav, feed, profile, form, action, msg) and element
346
+ describes the specific text (sign_in, home, password, etc).
347
+ - "data-i18n-placeholder" attribute for placeholder= attributes.
348
+ - "data-i18n-title" attribute for title= attributes.
349
+ - "data-i18n-aria-label" for aria-label= attributes.
350
+
351
+ Example transformations:
352
+ Before: <button>Sign in</button>
353
+ After: <button data-nac-id="conduit.action.sign_in"
354
+ data-nac-role="action" data-nac-action="login"
355
+ data-i18n="action.sign_in">Sign in</button>
356
+
357
+ Before: <input placeholder="Email" />
358
+ After: <input data-nac-id="conduit.field.email"
359
+ data-nac-role="field"
360
+ data-i18n-placeholder="form.email"
361
+ placeholder="Email" />
362
+
363
+ DO NOT decorate:
364
+ - Text that is a prop / variable / expression / template (\`\${...}\`).
365
+ Those are dynamic and live in the rendered data, not the catalog.
366
+ - Text inside <code> / <pre> blocks (technical content, language-agnostic).
367
+ - Single-character symbols (×, ✓, +, etc).
368
+ - Numerals that are computed (counts, prices). Static numerals in
369
+ copy ARE decorated.
370
+
371
+ Return an ADDITIONAL field in the JSON response:
372
+
373
+ "i18n_catalog": {
374
+ "action.sign_in": "Sign in",
375
+ "form.email": "Email",
376
+ "msg.tagline": "A place to share your knowledge.",
377
+ ...
378
+ }
379
+
380
+ Keys MUST match what you placed in data-i18n / data-i18n-placeholder /
381
+ data-i18n-title / data-i18n-aria-label attributes. Values are the
382
+ ENGLISH source text (the original verbatim string from the JSX). The
383
+ catalog is per-file; the project-level aggregator merges them.
384
+
385
+ If the file has no user-facing literal text to decorate, return
386
+ "i18n_catalog": {} (an empty object).
387
+ `;
388
+ const _client = {};
389
+ function client() {
390
+ if (!_client.c)
391
+ _client.c = new Anthropic();
392
+ return _client.c;
393
+ }
394
+ function extractJsonObject(text) {
395
+ const fenced = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
396
+ if (fenced && fenced[1])
397
+ return fenced[1].trim();
398
+ const trimmed = text.trim();
399
+ if (trimmed.startsWith('{') && trimmed.endsWith('}'))
400
+ return trimmed;
401
+ const i = trimmed.indexOf('{');
402
+ const j = trimmed.lastIndexOf('}');
403
+ if (i >= 0 && j > i)
404
+ return trimmed.slice(i, j + 1);
405
+ return trimmed;
406
+ }
407
+ /* G2: maximum LLM attempts per file before we accept the last
408
+ response as-is and surface validation findings to the caller. */
409
+ export const AI_APPLY_MAX_ATTEMPTS = 3;
410
+ const defaultAiCaller = async (args) => {
411
+ const payload = {
412
+ model: args.model,
413
+ max_tokens: 8192,
414
+ system: args.system,
415
+ messages: args.messages,
416
+ };
417
+ if (!args.skipTemperature)
418
+ payload.temperature = 0;
419
+ const resp = await client().messages.create(payload);
420
+ const block = resp.content.find((b) => b.type === 'text');
421
+ return {
422
+ text: block?.text || '',
423
+ usage: {
424
+ input: resp.usage.input_tokens || 0,
425
+ output: resp.usage.output_tokens || 0,
426
+ },
427
+ };
428
+ };
429
+ /**
430
+ * G2: assemble the retry-turn user message. Includes the prior
431
+ * findings + the last attempt's raw output so Claude can target a
432
+ * fix instead of guessing what went wrong.
433
+ *
434
+ * `attempt` is the zero-based index of the JUST-FAILED attempt;
435
+ * the next attempt the model is about to make is therefore
436
+ * `attempt + 2` in human-counted "1 of N" terms.
437
+ */
438
+ function buildRetryUserMessage(args) {
439
+ return 'Your previous response failed contract validation. Findings:\n'
440
+ + formatFindingsForRetry(args.findings)
441
+ + '\n\nThis is retry attempt ' + (args.attempt + 2) + ' of '
442
+ + AI_APPLY_MAX_ATTEMPTS + '. Please re-emit the COMPLETE JSON object '
443
+ + '(decorated_jsx, elements, plugin_slug, etc) with the listed errors fixed. '
444
+ + 'Do not omit fields that were already correct.';
445
+ }
446
+ export async function aiApplyOne(opts) {
447
+ const model = opts.model || DEFAULT_MODEL;
448
+ const skipTemp = /^claude-opus-4-7/.test(model);
449
+ const system = opts.withI18n ? (SYSTEM_PROMPT + '\n\n' + I18N_PROMPT_EXTENSION) : SYSTEM_PROMPT;
450
+ const initialUserPrompt = '<FILE path="' + opts.relpath + '">\n' + opts.jsxSource + '\n</FILE>' +
451
+ (opts.pluginSlugHint ? '\n\nPlugin slug for this project: ' + opts.pluginSlugHint : '') +
452
+ (opts.withI18n ? '\n\nALSO emit i18n decoration per the i18n section of the system prompt.' : '');
453
+ const caller = opts.aiCaller || defaultAiCaller;
454
+ const t0 = Date.now();
455
+ let tokens_in = 0;
456
+ let tokens_out = 0;
457
+ const messages = [
458
+ { role: 'user', content: initialUserPrompt },
459
+ ];
460
+ let lastParsed = null;
461
+ let lastRaw = '';
462
+ let lastFindings = [];
463
+ let attempts = 0;
464
+ for (let attempt = 0; attempt < AI_APPLY_MAX_ATTEMPTS; attempt++) {
465
+ attempts = attempt + 1;
466
+ const result = await caller({
467
+ system,
468
+ messages,
469
+ model: resolveAnthropicModelId(model),
470
+ skipTemperature: skipTemp,
471
+ });
472
+ lastRaw = result.text;
473
+ tokens_in += result.usage.input;
474
+ tokens_out += result.usage.output;
475
+ const jsonText = extractJsonObject(lastRaw);
476
+ let parsed;
477
+ let parseFailed = false;
478
+ try {
479
+ parsed = JSON.parse(jsonText);
480
+ }
481
+ catch (_e) {
482
+ parseFailed = true;
483
+ }
484
+ if (parseFailed) {
485
+ lastParsed = null;
486
+ lastFindings = [{
487
+ severity: 'error',
488
+ code: 'response_not_json',
489
+ message: 'Response did not contain a parseable JSON object. ' +
490
+ 'Wrap the result in a single { ... } object (or a single ```json fenced block).',
491
+ }];
492
+ }
493
+ else {
494
+ lastParsed = parsed;
495
+ const validation = validateAiApplyFragment(parsed);
496
+ lastFindings = validation.findings;
497
+ if (validation.ok) {
498
+ // Success: break the loop, return below.
499
+ break;
500
+ }
501
+ }
502
+ // Failed attempt: prepare retry messages if there is a next round.
503
+ if (attempt + 1 < AI_APPLY_MAX_ATTEMPTS) {
504
+ messages.push({ role: 'assistant', content: lastRaw });
505
+ messages.push({
506
+ role: 'user',
507
+ content: buildRetryUserMessage({ findings: lastFindings, attempt }),
508
+ });
509
+ }
510
+ }
511
+ const latency_ms = Date.now() - t0;
512
+ const cost_usd = computeCost(model, { input: tokens_in, output: tokens_out });
513
+ /* Final fragment: if parsing failed all attempts, fall back to the
514
+ original source with no decorations. Otherwise reflect whatever
515
+ the last attempt produced -- the caller decides whether to act
516
+ on validation_findings (warn vs block) at aggregation time. */
517
+ if (!lastParsed) {
518
+ return {
519
+ relpath: opts.relpath,
520
+ decorated_jsx: opts.jsxSource,
521
+ elements: [],
522
+ plugin_slug_guess: opts.pluginSlugHint || 'unknown',
523
+ tokens_in,
524
+ tokens_out,
525
+ cost_usd,
526
+ latency_ms,
527
+ decorated: false,
528
+ attempts,
529
+ validation_findings: lastFindings,
530
+ };
531
+ }
532
+ return {
533
+ relpath: opts.relpath,
534
+ decorated_jsx: lastParsed.decorated_jsx || opts.jsxSource,
535
+ elements: Array.isArray(lastParsed.elements) ? lastParsed.elements : [],
536
+ plugin_slug_guess: lastParsed.plugin_slug || opts.pluginSlugHint || 'unknown',
537
+ route_map: (lastParsed.route_map && typeof lastParsed.route_map === 'object') ? lastParsed.route_map : undefined,
538
+ fixtures: (lastParsed.fixtures && typeof lastParsed.fixtures === 'object') ? lastParsed.fixtures : undefined,
539
+ router_kind: (lastParsed.router_kind === 'hash' || lastParsed.router_kind === 'memory' || lastParsed.router_kind === 'browser')
540
+ ? lastParsed.router_kind
541
+ : undefined,
542
+ i18n_catalog: (lastParsed.i18n_catalog && typeof lastParsed.i18n_catalog === 'object') ? lastParsed.i18n_catalog : undefined,
543
+ tokens_in,
544
+ tokens_out,
545
+ cost_usd,
546
+ latency_ms,
547
+ decorated: !!lastParsed.decorated,
548
+ attempts,
549
+ validation_findings: lastFindings,
550
+ };
551
+ }
552
+ /**
553
+ * Walk a project directory, decorate every JSX/TSX file, aggregate
554
+ * into a single manifest. Writes decorated files back in place (or
555
+ * to outDir when set). Sequential by default to keep API budgets
556
+ * predictable; can run with bounded concurrency via the
557
+ * `concurrency` option.
558
+ */
559
+ export async function aiApplyProject(opts) {
560
+ const root = opts.subdir
561
+ ? path.join(opts.projectRoot, opts.subdir)
562
+ : opts.projectRoot;
563
+ /* Collect .jsx / .tsx files. */
564
+ const files = [];
565
+ async function walk(dir) {
566
+ const entries = await fs.readdir(dir, { withFileTypes: true });
567
+ for (const e of entries) {
568
+ const p = path.join(dir, e.name);
569
+ if (e.isDirectory()) {
570
+ if (e.name === 'node_modules' || e.name === '.git' || e.name === 'dist')
571
+ continue;
572
+ await walk(p);
573
+ }
574
+ else if (/\.(jsx|tsx)$/.test(e.name)) {
575
+ files.push(p);
576
+ }
577
+ }
578
+ }
579
+ await walk(root);
580
+ const results = new Array(files.length);
581
+ const conc = Math.max(1, Math.min(opts.concurrency || 4, 8));
582
+ let next = 0;
583
+ async function worker() {
584
+ while (true) {
585
+ const i = next++;
586
+ if (i >= files.length)
587
+ return;
588
+ const abs = files[i];
589
+ const rel = path.relative(opts.projectRoot, abs);
590
+ const src = await fs.readFile(abs, 'utf-8');
591
+ const r = await aiApplyOne({
592
+ jsxSource: src,
593
+ relpath: rel,
594
+ pluginSlugHint: opts.pluginSlugHint,
595
+ model: opts.model,
596
+ withI18n: opts.withI18n,
597
+ });
598
+ results[i] = r;
599
+ if (opts.onProgress)
600
+ opts.onProgress(i + 1, files.length, rel, r);
601
+ if (!opts.dryRun && r.decorated) {
602
+ const dst = opts.outDir
603
+ ? path.join(opts.outDir, rel)
604
+ : abs;
605
+ await fs.mkdir(path.dirname(dst), { recursive: true });
606
+ await fs.writeFile(dst, r.decorated_jsx, 'utf-8');
607
+ }
608
+ else if (opts.outDir && !opts.dryRun) {
609
+ /* When outputting to a separate dir, copy the file verbatim
610
+ even if undecorated so the project is complete. */
611
+ const outBase = opts.outDir;
612
+ const dst = path.join(outBase, rel);
613
+ await fs.mkdir(path.dirname(dst), { recursive: true });
614
+ await fs.writeFile(dst, src, 'utf-8');
615
+ }
616
+ }
617
+ }
618
+ const workers = Array.from({ length: conc }, () => worker());
619
+ await Promise.all(workers);
620
+ /* Pick canonical plugin slug: most common across non-empty
621
+ decorated files; tiebreak by first occurrence. */
622
+ const slugCount = {};
623
+ for (const r of results) {
624
+ if (!r || !r.plugin_slug_guess)
625
+ continue;
626
+ slugCount[r.plugin_slug_guess] = (slugCount[r.plugin_slug_guess] || 0) + 1;
627
+ }
628
+ let canonicalSlug = opts.pluginSlugHint || 'app';
629
+ let bestCount = -1;
630
+ for (const slug of Object.keys(slugCount)) {
631
+ const cnt = slugCount[slug] ?? 0;
632
+ if (cnt > bestCount) {
633
+ canonicalSlug = slug;
634
+ bestCount = cnt;
635
+ }
636
+ }
637
+ const combinedRouteMap = {};
638
+ for (const r of results) {
639
+ if (!r || !r.route_map)
640
+ continue;
641
+ for (const k of Object.keys(r.route_map)) {
642
+ const v = r.route_map[k];
643
+ if (v === undefined)
644
+ continue;
645
+ const norm = Array.isArray(v)
646
+ ? { paths: v, requires_auth: false }
647
+ : { paths: v.paths || [], requires_auth: !!v.requires_auth };
648
+ const existing = combinedRouteMap[k];
649
+ if (existing) {
650
+ existing.paths = Array.from(new Set([...existing.paths, ...norm.paths]));
651
+ existing.requires_auth = existing.requires_auth || norm.requires_auth;
652
+ }
653
+ else {
654
+ combinedRouteMap[k] = norm;
655
+ }
656
+ }
657
+ }
658
+ /* H1: infer mounted_at for elements that didn't declare it. The
659
+ heuristic: if the element's file path matches one of the
660
+ route_map keys, propagate the paths + requires_auth. */
661
+ function inferMountedFor(file) {
662
+ const candidates = Object.keys(combinedRouteMap);
663
+ const norm = file.replace(/\\/g, '/').replace(/^src\//, '').replace(/\.(jsx|tsx)$/, '');
664
+ for (const k of candidates) {
665
+ const nk = k.replace(/^\.\//, '').replace(/\.(jsx|tsx)$/, '');
666
+ if (norm === nk || norm.endsWith('/' + nk) || nk.endsWith('/' + norm)) {
667
+ return combinedRouteMap[k] || null;
668
+ }
669
+ }
670
+ return null;
671
+ }
672
+ /* Aggregate elements. Dedupe by id (later file wins, but warn).
673
+ H2: collect warnings from individual elements (reusable components
674
+ without qualifier) for surfacing at end-of-apply.
675
+ H1: fill mounted_at + requires_auth from route_map where the
676
+ element omitted them. */
677
+ const seen = {};
678
+ const elements = [];
679
+ const warnings = [];
680
+ for (const r of results) {
681
+ if (!r || !r.elements)
682
+ continue;
683
+ const fileInfer = inferMountedFor(r.relpath);
684
+ for (const el of r.elements) {
685
+ if (!el || !el.id)
686
+ continue;
687
+ if (el.warning) {
688
+ warnings.push({ file: r.relpath, id: el.id, warning: el.warning });
689
+ }
690
+ /* H1: fill mounted_at + requires_auth if missing. */
691
+ if ((!el.mounted_at || el.mounted_at.length === 0) && fileInfer) {
692
+ el.mounted_at = fileInfer.paths;
693
+ if (fileInfer.requires_auth && el.requires_auth === undefined) {
694
+ el.requires_auth = true;
695
+ }
696
+ }
697
+ if (seen[el.id]) {
698
+ continue;
699
+ }
700
+ seen[el.id] = r;
701
+ elements.push(el);
702
+ }
703
+ }
704
+ /* H1.v3: aggregate fixtures across files. Multiple Router roots
705
+ could declare fixtures for the same :param; last write wins. */
706
+ const fixtures = {};
707
+ for (const r of results) {
708
+ if (!r || !r.fixtures)
709
+ continue;
710
+ for (const k of Object.keys(r.fixtures)) {
711
+ fixtures[k] = r.fixtures[k];
712
+ }
713
+ }
714
+ /* H1.v4: aggregate router_kind. Take the first non-undefined value
715
+ across all files. "hash" wins over "browser" if both were detected
716
+ (the Router root file's value is authoritative; this is just a
717
+ defensive fallback). */
718
+ let router_kind = undefined;
719
+ for (const r of results) {
720
+ if (!r || !r.router_kind)
721
+ continue;
722
+ if (!router_kind)
723
+ router_kind = r.router_kind;
724
+ else if (r.router_kind === 'hash')
725
+ router_kind = 'hash';
726
+ }
727
+ /* i18n: aggregate per-file catalogs into a single EN canonical, then
728
+ write i18n/<lang>.json under outDir (or projectRoot when outDir not
729
+ set). The EN catalog is the source of truth; other languages start
730
+ as a CLONE of EN so the keys are present; humans / downstream tools
731
+ translate the values. Collisions (same key, different value across
732
+ files) are resolved last-write-wins with a warning. */
733
+ const i18nCanonical = {};
734
+ const i18nWarnings = [];
735
+ if (opts.withI18n) {
736
+ for (const r of results) {
737
+ if (!r || !r.i18n_catalog)
738
+ continue;
739
+ for (const k of Object.keys(r.i18n_catalog)) {
740
+ const v = r.i18n_catalog[k];
741
+ if (v == null)
742
+ continue;
743
+ if (i18nCanonical[k] != null && i18nCanonical[k] !== v) {
744
+ i18nWarnings.push({
745
+ file: r.relpath,
746
+ key: k,
747
+ old_value: i18nCanonical[k],
748
+ new_value: v,
749
+ });
750
+ }
751
+ i18nCanonical[k] = v;
752
+ }
753
+ }
754
+ if (!opts.dryRun) {
755
+ const langs = (opts.i18nLangs && opts.i18nLangs.length > 0)
756
+ ? opts.i18nLangs
757
+ : ['en', 'es', 'pt', 'fr', 'it', 'de', 'ja', 'zh', 'hi', 'ar'];
758
+ const targetRoot = opts.outDir || opts.projectRoot;
759
+ const i18nDir = path.join(targetRoot, 'i18n');
760
+ await fs.mkdir(i18nDir, { recursive: true });
761
+ /* en.json -- authoritative source from the AI extraction. */
762
+ await fs.writeFile(path.join(i18nDir, 'en.json'), JSON.stringify(i18nCanonical, null, 2), 'utf-8');
763
+ /* Other locales -- start as a clone of EN. The user fills in
764
+ translations downstream (manually or by another LLM pass).
765
+ An empty stub {} is misleading -- it would make the runtime
766
+ shim fall back to EN silently. Cloning EN makes the
767
+ missing-translation state visible (the value is still EN,
768
+ not blank). */
769
+ for (const lang of langs) {
770
+ if (lang === 'en')
771
+ continue;
772
+ const langPath = path.join(i18nDir, lang + '.json');
773
+ /* If the file already exists, DO NOT overwrite -- it may
774
+ contain real translations the user already authored. */
775
+ try {
776
+ await fs.access(langPath);
777
+ continue;
778
+ }
779
+ catch {
780
+ /* not found -- fall through and write the EN clone */
781
+ }
782
+ await fs.writeFile(langPath, JSON.stringify(i18nCanonical, null, 2), 'utf-8');
783
+ }
784
+ /* index.json -- declares the available locale list for the
785
+ runtime shim. */
786
+ const indexPath = path.join(i18nDir, 'index.json');
787
+ try {
788
+ await fs.access(indexPath);
789
+ /* leave the existing index alone -- the user may have customised it */
790
+ }
791
+ catch {
792
+ const NATIVE_NAMES = {
793
+ en: 'English', es: 'Español', pt: 'Português', fr: 'Français',
794
+ it: 'Italiano', de: 'Deutsch', ja: '日本語', zh: '中文',
795
+ hi: 'हिन्दी', ar: 'العربية',
796
+ };
797
+ const index = {
798
+ default: 'en',
799
+ available: langs.map(l => ({
800
+ code: l,
801
+ name: l.toUpperCase(),
802
+ native: NATIVE_NAMES[l] || l,
803
+ })),
804
+ };
805
+ await fs.writeFile(indexPath, JSON.stringify(index, null, 2), 'utf-8');
806
+ }
807
+ }
808
+ }
809
+ return {
810
+ files: results,
811
+ manifest: {
812
+ plugin_slug: canonicalSlug,
813
+ version: '1.0.0',
814
+ nac_version: '1.0',
815
+ label_i18n: { es: canonicalSlug, en: canonicalSlug },
816
+ elements,
817
+ ...(Object.keys(fixtures).length > 0 ? { fixtures } : {}),
818
+ ...(router_kind ? { router_kind } : {}),
819
+ },
820
+ warnings,
821
+ i18n_catalog: opts.withI18n ? i18nCanonical : undefined,
822
+ i18n_warnings: opts.withI18n ? i18nWarnings : undefined,
823
+ total_tokens_in: results.reduce((a, b) => a + (b?.tokens_in || 0), 0),
824
+ total_tokens_out: results.reduce((a, b) => a + (b?.tokens_out || 0), 0),
825
+ total_cost_usd: results.reduce((a, b) => a + (b?.cost_usd || 0), 0),
826
+ total_latency_ms: results.reduce((a, b) => a + (b?.latency_ms || 0), 0),
827
+ files_clean_first_attempt: results.reduce((a, b) => a + ((b?.attempts === 1 && (b?.validation_findings?.length || 0) === 0) ? 1 : 0), 0),
828
+ files_retried: results.reduce((a, b) => a + (((b?.attempts || 0) > 1) ? 1 : 0), 0),
829
+ files_gave_up: results.reduce((a, b) => a + (((b?.attempts || 0) >= AI_APPLY_MAX_ATTEMPTS
830
+ && (b?.validation_findings?.filter((f) => f.severity === 'error').length || 0) > 0) ? 1 : 0), 0),
831
+ };
832
+ }
833
+ //# sourceMappingURL=ai-apply.js.map