create-quiver 0.15.2 → 0.15.4

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 (324) hide show
  1. package/.github/workflows/ci.yml +3 -0
  2. package/AGENTS.md.es.template +37 -0
  3. package/README.md +1 -0
  4. package/README_FOR_AI.md +6 -4
  5. package/docs/AI_CONTEXT.md.es.template +35 -0
  6. package/docs/AI_ONBOARDING_PROMPT.md.es.template +22 -0
  7. package/docs/CLI_UX_GUIDE.md +47 -1
  8. package/docs/COMMANDS.md.es.template +28 -0
  9. package/docs/COMMANDS.md.template +14 -3
  10. package/docs/CONTEXTO.md.es.template +33 -0
  11. package/docs/DECISIONS.md.es.template +17 -0
  12. package/docs/GITFLOW_PR_GUIDE.md.es.template +25 -0
  13. package/docs/INDEX.md +96 -0
  14. package/docs/INDEX.md.es.template +36 -0
  15. package/docs/STATUS.md.es.template +28 -0
  16. package/docs/SUPPORT_MATRIX.md.es.template +22 -0
  17. package/docs/TESTING_GUIDE_FOR_AI.md.es.template +19 -0
  18. package/docs/TROUBLESHOOTING.md.es.template +35 -0
  19. package/docs/WORKFLOW.md.es.template +29 -0
  20. package/docs/WORKFLOW.md.template +2 -2
  21. package/docs/ai/LESSONS.md.es.template +24 -0
  22. package/docs/getting-started/installation.md +19 -0
  23. package/docs/reference/commands.md +68 -2
  24. package/package.json +3 -1
  25. package/specs/quiver-v34-cli-dashboard-status/EVIDENCE_REPORT.md +119 -0
  26. package/specs/quiver-v34-cli-dashboard-status/EXECUTION_PLAN.md +77 -0
  27. package/specs/quiver-v34-cli-dashboard-status/SPEC.md +152 -0
  28. package/specs/quiver-v34-cli-dashboard-status/STATUS.md +29 -0
  29. package/specs/quiver-v34-cli-dashboard-status/pr.md +99 -0
  30. package/specs/quiver-v34-cli-dashboard-status/slices/slice-00-dashboard-foundation/CLOSURE_BRIEF.md +19 -0
  31. package/specs/quiver-v34-cli-dashboard-status/slices/slice-00-dashboard-foundation/EXECUTION_BRIEF.md +49 -0
  32. package/specs/quiver-v34-cli-dashboard-status/slices/slice-00-dashboard-foundation/slice.json +68 -0
  33. package/specs/quiver-v34-cli-dashboard-status/slices/slice-01-dashboard-report-contract/CLOSURE_BRIEF.md +19 -0
  34. package/specs/quiver-v34-cli-dashboard-status/slices/slice-01-dashboard-report-contract/EXECUTION_BRIEF.md +53 -0
  35. package/specs/quiver-v34-cli-dashboard-status/slices/slice-01-dashboard-report-contract/slice.json +78 -0
  36. package/specs/quiver-v34-cli-dashboard-status/slices/slice-02-dashboard-command-rendering/CLOSURE_BRIEF.md +19 -0
  37. package/specs/quiver-v34-cli-dashboard-status/slices/slice-02-dashboard-command-rendering/EXECUTION_BRIEF.md +53 -0
  38. package/specs/quiver-v34-cli-dashboard-status/slices/slice-02-dashboard-command-rendering/slice.json +82 -0
  39. package/specs/quiver-v34-cli-dashboard-status/slices/slice-03-dashboard-edge-cases-and-guardrails/CLOSURE_BRIEF.md +19 -0
  40. package/specs/quiver-v34-cli-dashboard-status/slices/slice-03-dashboard-edge-cases-and-guardrails/EXECUTION_BRIEF.md +54 -0
  41. package/specs/quiver-v34-cli-dashboard-status/slices/slice-03-dashboard-edge-cases-and-guardrails/slice.json +77 -0
  42. package/specs/quiver-v34-cli-dashboard-status/slices/slice-04-docs-templates-and-scripts/CLOSURE_BRIEF.md +19 -0
  43. package/specs/quiver-v34-cli-dashboard-status/slices/slice-04-docs-templates-and-scripts/EXECUTION_BRIEF.md +54 -0
  44. package/specs/quiver-v34-cli-dashboard-status/slices/slice-04-docs-templates-and-scripts/slice.json +88 -0
  45. package/specs/quiver-v34-cli-dashboard-status/slices/slice-05-tests-smokes-release-readiness/CLOSURE_BRIEF.md +26 -0
  46. package/specs/quiver-v34-cli-dashboard-status/slices/slice-05-tests-smokes-release-readiness/EXECUTION_BRIEF.md +57 -0
  47. package/specs/quiver-v34-cli-dashboard-status/slices/slice-05-tests-smokes-release-readiness/slice.json +85 -0
  48. package/specs/quiver-v35-compact-dashboard-version-ux/EVIDENCE_REPORT.md +43 -0
  49. package/specs/quiver-v35-compact-dashboard-version-ux/EXECUTION_PLAN.md +85 -0
  50. package/specs/quiver-v35-compact-dashboard-version-ux/SPEC.md +147 -0
  51. package/specs/quiver-v35-compact-dashboard-version-ux/STATUS.md +33 -0
  52. package/specs/quiver-v35-compact-dashboard-version-ux/pr.md +106 -0
  53. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-00-foundation-and-doc-router/CLOSURE_BRIEF.md +19 -0
  54. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-00-foundation-and-doc-router/EXECUTION_BRIEF.md +38 -0
  55. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-00-foundation-and-doc-router/slice.json +64 -0
  56. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-01-dashboard-cli-contract/CLOSURE_BRIEF.md +19 -0
  57. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-01-dashboard-cli-contract/EXECUTION_BRIEF.md +49 -0
  58. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-01-dashboard-cli-contract/slice.json +80 -0
  59. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-02-dashboard-compact-renderer/CLOSURE_BRIEF.md +19 -0
  60. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-02-dashboard-compact-renderer/EXECUTION_BRIEF.md +48 -0
  61. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-02-dashboard-compact-renderer/slice.json +73 -0
  62. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-03-dashboard-details-sections/CLOSURE_BRIEF.md +19 -0
  63. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-03-dashboard-details-sections/EXECUTION_BRIEF.md +48 -0
  64. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-03-dashboard-details-sections/slice.json +72 -0
  65. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-04-version-command/CLOSURE_BRIEF.md +19 -0
  66. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-04-version-command/EXECUTION_BRIEF.md +49 -0
  67. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-04-version-command/slice.json +82 -0
  68. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-05-docs-help-generated-guidance/CLOSURE_BRIEF.md +19 -0
  69. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-05-docs-help-generated-guidance/EXECUTION_BRIEF.md +50 -0
  70. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-05-docs-help-generated-guidance/slice.json +90 -0
  71. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-06-tests-smokes-release-readiness/CLOSURE_BRIEF.md +21 -0
  72. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-06-tests-smokes-release-readiness/EXECUTION_BRIEF.md +51 -0
  73. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-06-tests-smokes-release-readiness/slice.json +85 -0
  74. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-07-package-and-cross-platform-smoke/CLOSURE_BRIEF.md +20 -0
  75. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-07-package-and-cross-platform-smoke/EXECUTION_BRIEF.md +49 -0
  76. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-07-package-and-cross-platform-smoke/slice.json +79 -0
  77. package/specs/quiver-v36-ai-run-watch-portable/EVIDENCE_REPORT.md +33 -0
  78. package/specs/quiver-v36-ai-run-watch-portable/EXECUTION_PLAN.md +39 -0
  79. package/specs/quiver-v36-ai-run-watch-portable/SPEC.md +166 -0
  80. package/specs/quiver-v36-ai-run-watch-portable/STATUS.md +31 -0
  81. package/specs/quiver-v36-ai-run-watch-portable/pr.md +78 -0
  82. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-00-foundation-and-handoffs/CLOSURE_BRIEF.md +19 -0
  83. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-00-foundation-and-handoffs/EXECUTION_BRIEF.md +43 -0
  84. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-00-foundation-and-handoffs/slice.json +61 -0
  85. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-01-run-schema-path-safety/CLOSURE_BRIEF.md +18 -0
  86. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-01-run-schema-path-safety/EXECUTION_BRIEF.md +43 -0
  87. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-01-run-schema-path-safety/slice.json +65 -0
  88. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-02-event-writer-redacted-logs/CLOSURE_BRIEF.md +18 -0
  89. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-02-event-writer-redacted-logs/EXECUTION_BRIEF.md +43 -0
  90. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-02-event-writer-redacted-logs/slice.json +66 -0
  91. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-03-provider-streaming-integration/CLOSURE_BRIEF.md +18 -0
  92. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-03-provider-streaming-integration/EXECUTION_BRIEF.md +41 -0
  93. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-03-provider-streaming-integration/slice.json +71 -0
  94. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-04-ai-run-watch-command/CLOSURE_BRIEF.md +18 -0
  95. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-04-ai-run-watch-command/EXECUTION_BRIEF.md +44 -0
  96. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-04-ai-run-watch-command/slice.json +73 -0
  97. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-05-main-progress-user-guidance/CLOSURE_BRIEF.md +18 -0
  98. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-05-main-progress-user-guidance/EXECUTION_BRIEF.md +44 -0
  99. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-05-main-progress-user-guidance/slice.json +71 -0
  100. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-06-docs-generated-guidance/CLOSURE_BRIEF.md +19 -0
  101. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-06-docs-generated-guidance/EXECUTION_BRIEF.md +45 -0
  102. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-06-docs-generated-guidance/slice.json +77 -0
  103. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-07-tests-cross-platform-readiness/CLOSURE_BRIEF.md +22 -0
  104. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-07-tests-cross-platform-readiness/EXECUTION_BRIEF.md +46 -0
  105. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-07-tests-cross-platform-readiness/slice.json +87 -0
  106. package/specs/quiver-v37-cli-i18n-foundation/EVIDENCE_REPORT.md +73 -0
  107. package/specs/quiver-v37-cli-i18n-foundation/EXECUTION_PLAN.md +34 -0
  108. package/specs/quiver-v37-cli-i18n-foundation/SPEC.md +91 -0
  109. package/specs/quiver-v37-cli-i18n-foundation/STATUS.md +29 -0
  110. package/specs/quiver-v37-cli-i18n-foundation/pr.md +97 -0
  111. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-00-foundation-and-program-roadmap/CLOSURE_BRIEF.md +10 -0
  112. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-00-foundation-and-program-roadmap/EXECUTION_BRIEF.md +22 -0
  113. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-00-foundation-and-program-roadmap/slice.json +54 -0
  114. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-01-language-resolution-contract/CLOSURE_BRIEF.md +30 -0
  115. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-01-language-resolution-contract/EXECUTION_BRIEF.md +27 -0
  116. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-01-language-resolution-contract/slice.json +66 -0
  117. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-02-message-catalog-interpolation/CLOSURE_BRIEF.md +27 -0
  118. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-02-message-catalog-interpolation/EXECUTION_BRIEF.md +26 -0
  119. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-02-message-catalog-interpolation/slice.json +62 -0
  120. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-03-config-language-command/CLOSURE_BRIEF.md +32 -0
  121. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-03-config-language-command/EXECUTION_BRIEF.md +25 -0
  122. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-03-config-language-command/slice.json +66 -0
  123. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-04-parser-help-error-foundation/CLOSURE_BRIEF.md +30 -0
  124. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-04-parser-help-error-foundation/EXECUTION_BRIEF.md +24 -0
  125. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-04-parser-help-error-foundation/slice.json +65 -0
  126. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-05-foundation-docs-tests-package-readiness/CLOSURE_BRIEF.md +27 -0
  127. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-05-foundation-docs-tests-package-readiness/EXECUTION_BRIEF.md +23 -0
  128. package/specs/quiver-v37-cli-i18n-foundation/slices/slice-05-foundation-docs-tests-package-readiness/slice.json +67 -0
  129. package/specs/quiver-v38-cli-i18n-read-only-commands/EVIDENCE_REPORT.md +69 -0
  130. package/specs/quiver-v38-cli-i18n-read-only-commands/EXECUTION_PLAN.md +25 -0
  131. package/specs/quiver-v38-cli-i18n-read-only-commands/SPEC.md +52 -0
  132. package/specs/quiver-v38-cli-i18n-read-only-commands/STATUS.md +24 -0
  133. package/specs/quiver-v38-cli-i18n-read-only-commands/pr.md +119 -0
  134. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-00-read-only-foundation/CLOSURE_BRIEF.md +10 -0
  135. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-00-read-only-foundation/EXECUTION_BRIEF.md +21 -0
  136. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-00-read-only-foundation/slice.json +53 -0
  137. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-01-version-dashboard-help/CLOSURE_BRIEF.md +17 -0
  138. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-01-version-dashboard-help/EXECUTION_BRIEF.md +23 -0
  139. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-01-version-dashboard-help/slice.json +69 -0
  140. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-02-flow-doctor-next-graph/CLOSURE_BRIEF.md +13 -0
  141. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-02-flow-doctor-next-graph/EXECUTION_BRIEF.md +22 -0
  142. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-02-flow-doctor-next-graph/slice.json +77 -0
  143. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-03-ai-inspection-and-export/CLOSURE_BRIEF.md +12 -0
  144. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-03-ai-inspection-and-export/EXECUTION_BRIEF.md +22 -0
  145. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-03-ai-inspection-and-export/slice.json +66 -0
  146. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-04-read-only-tests-smokes/CLOSURE_BRIEF.md +13 -0
  147. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-04-read-only-tests-smokes/EXECUTION_BRIEF.md +22 -0
  148. package/specs/quiver-v38-cli-i18n-read-only-commands/slices/slice-04-read-only-tests-smokes/slice.json +61 -0
  149. package/specs/quiver-v39-cli-i18n-setup-onboarding/EVIDENCE_REPORT.md +71 -0
  150. package/specs/quiver-v39-cli-i18n-setup-onboarding/EXECUTION_PLAN.md +25 -0
  151. package/specs/quiver-v39-cli-i18n-setup-onboarding/SPEC.md +53 -0
  152. package/specs/quiver-v39-cli-i18n-setup-onboarding/STATUS.md +24 -0
  153. package/specs/quiver-v39-cli-i18n-setup-onboarding/pr.md +104 -0
  154. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-00-setup-foundation/CLOSURE_BRIEF.md +10 -0
  155. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-00-setup-foundation/EXECUTION_BRIEF.md +21 -0
  156. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-00-setup-foundation/slice.json +53 -0
  157. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-01-init-interactive-language/CLOSURE_BRIEF.md +12 -0
  158. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-01-init-interactive-language/EXECUTION_BRIEF.md +23 -0
  159. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-01-init-interactive-language/slice.json +68 -0
  160. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-02-analyze-migrate-prepare/CLOSURE_BRIEF.md +12 -0
  161. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-02-analyze-migrate-prepare/EXECUTION_BRIEF.md +23 -0
  162. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-02-analyze-migrate-prepare/slice.json +62 -0
  163. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-03-demo-evidence-onboarding/CLOSURE_BRIEF.md +12 -0
  164. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-03-demo-evidence-onboarding/EXECUTION_BRIEF.md +22 -0
  165. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-03-demo-evidence-onboarding/slice.json +72 -0
  166. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-04-setup-tests-smokes/CLOSURE_BRIEF.md +15 -0
  167. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-04-setup-tests-smokes/EXECUTION_BRIEF.md +22 -0
  168. package/specs/quiver-v39-cli-i18n-setup-onboarding/slices/slice-04-setup-tests-smokes/slice.json +61 -0
  169. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/EVIDENCE_REPORT.md +50 -0
  170. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/EXECUTION_PLAN.md +25 -0
  171. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/SPEC.md +52 -0
  172. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/STATUS.md +24 -0
  173. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/pr.md +94 -0
  174. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-00-spec-slice-foundation/CLOSURE_BRIEF.md +10 -0
  175. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-00-spec-slice-foundation/EXECUTION_BRIEF.md +21 -0
  176. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-00-spec-slice-foundation/slice.json +53 -0
  177. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-01-spec-create-start-status/CLOSURE_BRIEF.md +12 -0
  178. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-01-spec-create-start-status/EXECUTION_BRIEF.md +22 -0
  179. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-01-spec-create-start-status/slice.json +63 -0
  180. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-02-spec-validate-close/CLOSURE_BRIEF.md +10 -0
  181. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-02-spec-validate-close/EXECUTION_BRIEF.md +22 -0
  182. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-02-spec-validate-close/slice.json +63 -0
  183. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-03-slice-lifecycle-handoffs/CLOSURE_BRIEF.md +10 -0
  184. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-03-slice-lifecycle-handoffs/EXECUTION_BRIEF.md +22 -0
  185. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-03-slice-lifecycle-handoffs/slice.json +72 -0
  186. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-04-spec-slice-tests-smokes/CLOSURE_BRIEF.md +15 -0
  187. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-04-spec-slice-tests-smokes/EXECUTION_BRIEF.md +22 -0
  188. package/specs/quiver-v40-cli-i18n-spec-slice-workflows/slices/slice-04-spec-slice-tests-smokes/slice.json +69 -0
  189. package/specs/quiver-v41-cli-i18n-ai-lifecycle/EVIDENCE_REPORT.md +90 -0
  190. package/specs/quiver-v41-cli-i18n-ai-lifecycle/EXECUTION_PLAN.md +33 -0
  191. package/specs/quiver-v41-cli-i18n-ai-lifecycle/SPEC.md +57 -0
  192. package/specs/quiver-v41-cli-i18n-ai-lifecycle/STATUS.md +26 -0
  193. package/specs/quiver-v41-cli-i18n-ai-lifecycle/pr.md +103 -0
  194. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-00-ai-i18n-foundation/CLOSURE_BRIEF.md +10 -0
  195. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-00-ai-i18n-foundation/EXECUTION_BRIEF.md +21 -0
  196. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-00-ai-i18n-foundation/slice.json +53 -0
  197. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-01-ai-run-status-resume/CLOSURE_BRIEF.md +13 -0
  198. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-01-ai-run-status-resume/EXECUTION_BRIEF.md +22 -0
  199. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-01-ai-run-status-resume/slice.json +68 -0
  200. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-02-ai-agent-models/CLOSURE_BRIEF.md +12 -0
  201. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-02-ai-agent-models/EXECUTION_BRIEF.md +22 -0
  202. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-02-ai-agent-models/slice.json +66 -0
  203. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-03-ai-planner-approval-review/CLOSURE_BRIEF.md +12 -0
  204. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-03-ai-planner-approval-review/EXECUTION_BRIEF.md +23 -0
  205. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-03-ai-planner-approval-review/slice.json +68 -0
  206. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-04-ai-execution-pr/CLOSURE_BRIEF.md +13 -0
  207. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-04-ai-execution-pr/EXECUTION_BRIEF.md +23 -0
  208. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-04-ai-execution-pr/slice.json +72 -0
  209. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-05-ai-tests-smokes/CLOSURE_BRIEF.md +15 -0
  210. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-05-ai-tests-smokes/EXECUTION_BRIEF.md +22 -0
  211. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-05-ai-tests-smokes/slice.json +66 -0
  212. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-06-ai-prepare-context-progress-i18n-fix/CLOSURE_BRIEF.md +11 -0
  213. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-06-ai-prepare-context-progress-i18n-fix/EXECUTION_BRIEF.md +22 -0
  214. package/specs/quiver-v41-cli-i18n-ai-lifecycle/slices/slice-06-ai-prepare-context-progress-i18n-fix/slice.json +70 -0
  215. package/specs/quiver-v42-cli-i18n-generated-docs/EVIDENCE_REPORT.md +64 -0
  216. package/specs/quiver-v42-cli-i18n-generated-docs/EXECUTION_PLAN.md +26 -0
  217. package/specs/quiver-v42-cli-i18n-generated-docs/SPEC.md +52 -0
  218. package/specs/quiver-v42-cli-i18n-generated-docs/STATUS.md +24 -0
  219. package/specs/quiver-v42-cli-i18n-generated-docs/pr.md +125 -0
  220. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-00-generated-docs-foundation/CLOSURE_BRIEF.md +10 -0
  221. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-00-generated-docs-foundation/EXECUTION_BRIEF.md +21 -0
  222. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-00-generated-docs-foundation/slice.json +53 -0
  223. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-01-template-language-routing/CLOSURE_BRIEF.md +10 -0
  224. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-01-template-language-routing/EXECUTION_BRIEF.md +22 -0
  225. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-01-template-language-routing/slice.json +62 -0
  226. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-02-init-docs-and-i18n-assets/CLOSURE_BRIEF.md +11 -0
  227. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-02-init-docs-and-i18n-assets/EXECUTION_BRIEF.md +22 -0
  228. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-02-init-docs-and-i18n-assets/slice.json +72 -0
  229. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-03-docs-reference-guides/CLOSURE_BRIEF.md +12 -0
  230. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-03-docs-reference-guides/EXECUTION_BRIEF.md +22 -0
  231. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-03-docs-reference-guides/slice.json +60 -0
  232. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-04-generated-docs-tests-smokes/CLOSURE_BRIEF.md +16 -0
  233. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-04-generated-docs-tests-smokes/EXECUTION_BRIEF.md +22 -0
  234. package/specs/quiver-v42-cli-i18n-generated-docs/slices/slice-04-generated-docs-tests-smokes/slice.json +62 -0
  235. package/specs/quiver-v43-cli-i18n-audit-release-readiness/EVIDENCE_REPORT.md +203 -0
  236. package/specs/quiver-v43-cli-i18n-audit-release-readiness/EXECUTION_PLAN.md +27 -0
  237. package/specs/quiver-v43-cli-i18n-audit-release-readiness/SPEC.md +54 -0
  238. package/specs/quiver-v43-cli-i18n-audit-release-readiness/STATUS.md +24 -0
  239. package/specs/quiver-v43-cli-i18n-audit-release-readiness/command-language-mode-matrix.json +545 -0
  240. package/specs/quiver-v43-cli-i18n-audit-release-readiness/cross-platform-smoke-matrix.md +35 -0
  241. package/specs/quiver-v43-cli-i18n-audit-release-readiness/pr.md +155 -0
  242. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-00-audit-foundation/CLOSURE_BRIEF.md +10 -0
  243. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-00-audit-foundation/EXECUTION_BRIEF.md +21 -0
  244. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-00-audit-foundation/slice.json +53 -0
  245. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-01-command-language-mode-matrix/CLOSURE_BRIEF.md +13 -0
  246. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-01-command-language-mode-matrix/EXECUTION_BRIEF.md +23 -0
  247. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-01-command-language-mode-matrix/slice.json +62 -0
  248. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-02-public-string-audit/CLOSURE_BRIEF.md +18 -0
  249. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-02-public-string-audit/EXECUTION_BRIEF.md +22 -0
  250. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-02-public-string-audit/slice.json +59 -0
  251. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-03-cross-platform-smokes/CLOSURE_BRIEF.md +15 -0
  252. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-03-cross-platform-smokes/EXECUTION_BRIEF.md +23 -0
  253. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-03-cross-platform-smokes/slice.json +62 -0
  254. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-04-package-release-readiness/CLOSURE_BRIEF.md +17 -0
  255. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-04-package-release-readiness/EXECUTION_BRIEF.md +24 -0
  256. package/specs/quiver-v43-cli-i18n-audit-release-readiness/slices/slice-04-package-release-readiness/slice.json +66 -0
  257. package/specs/quiver-v44-provider-live-output-tui-lite/EVIDENCE_REPORT.md +18 -0
  258. package/specs/quiver-v44-provider-live-output-tui-lite/EXECUTION_PLAN.md +31 -0
  259. package/specs/quiver-v44-provider-live-output-tui-lite/SPEC.md +118 -0
  260. package/specs/quiver-v44-provider-live-output-tui-lite/STATUS.md +25 -0
  261. package/specs/quiver-v44-provider-live-output-tui-lite/pr.md +92 -0
  262. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-00-tui-lite-contract-foundation/CLOSURE_BRIEF.md +11 -0
  263. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-00-tui-lite-contract-foundation/EXECUTION_BRIEF.md +23 -0
  264. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-00-tui-lite-contract-foundation/slice.json +64 -0
  265. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-01-provider-stream-hooks/CLOSURE_BRIEF.md +10 -0
  266. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-01-provider-stream-hooks/EXECUTION_BRIEF.md +24 -0
  267. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-01-provider-stream-hooks/slice.json +65 -0
  268. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-02-live-output-renderer/CLOSURE_BRIEF.md +10 -0
  269. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-02-live-output-renderer/EXECUTION_BRIEF.md +24 -0
  270. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-02-live-output-renderer/slice.json +70 -0
  271. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-03-prepare-context-integration/CLOSURE_BRIEF.md +10 -0
  272. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-03-prepare-context-integration/EXECUTION_BRIEF.md +25 -0
  273. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-03-prepare-context-integration/slice.json +70 -0
  274. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-04-planner-command-audit-adoption/CLOSURE_BRIEF.md +10 -0
  275. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-04-planner-command-audit-adoption/EXECUTION_BRIEF.md +26 -0
  276. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-04-planner-command-audit-adoption/slice.json +72 -0
  277. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-05-docs-tests-readiness/CLOSURE_BRIEF.md +15 -0
  278. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-05-docs-tests-readiness/EXECUTION_BRIEF.md +26 -0
  279. package/specs/quiver-v44-provider-live-output-tui-lite/slices/slice-05-docs-tests-readiness/slice.json +75 -0
  280. package/specs/quiver-v45-ci-actions-node24-readiness/EVIDENCE_REPORT.md +30 -0
  281. package/specs/quiver-v45-ci-actions-node24-readiness/EXECUTION_PLAN.md +22 -0
  282. package/specs/quiver-v45-ci-actions-node24-readiness/SPEC.md +49 -0
  283. package/specs/quiver-v45-ci-actions-node24-readiness/STATUS.md +21 -0
  284. package/specs/quiver-v45-ci-actions-node24-readiness/pr.md +67 -0
  285. package/specs/quiver-v45-ci-actions-node24-readiness/slices/slice-00-ci-actions-foundation/CLOSURE_BRIEF.md +10 -0
  286. package/specs/quiver-v45-ci-actions-node24-readiness/slices/slice-00-ci-actions-foundation/EXECUTION_BRIEF.md +21 -0
  287. package/specs/quiver-v45-ci-actions-node24-readiness/slices/slice-00-ci-actions-foundation/slice.json +48 -0
  288. package/specs/quiver-v45-ci-actions-node24-readiness/slices/slice-01-actions-node24-readiness/CLOSURE_BRIEF.md +14 -0
  289. package/specs/quiver-v45-ci-actions-node24-readiness/slices/slice-01-actions-node24-readiness/EXECUTION_BRIEF.md +23 -0
  290. package/specs/quiver-v45-ci-actions-node24-readiness/slices/slice-01-actions-node24-readiness/slice.json +65 -0
  291. package/src/create-quiver/commands/ai.js +485 -307
  292. package/src/create-quiver/commands/config.js +124 -0
  293. package/src/create-quiver/commands/dashboard.js +31 -0
  294. package/src/create-quiver/commands/demo.js +2 -2
  295. package/src/create-quiver/commands/evidence.js +6 -4
  296. package/src/create-quiver/commands/flow.js +108 -16
  297. package/src/create-quiver/commands/graph.js +1 -0
  298. package/src/create-quiver/commands/next.js +21 -18
  299. package/src/create-quiver/commands/plan.js +23 -6
  300. package/src/create-quiver/commands/spec.js +66 -57
  301. package/src/create-quiver/index.js +606 -136
  302. package/src/create-quiver/lib/ai/execution-plan.js +23 -20
  303. package/src/create-quiver/lib/ai/executor.js +60 -44
  304. package/src/create-quiver/lib/ai/export-state.js +75 -64
  305. package/src/create-quiver/lib/ai/github.js +24 -21
  306. package/src/create-quiver/lib/ai/run-state.js +28 -25
  307. package/src/create-quiver/lib/cli/ux-flags.js +6 -0
  308. package/src/create-quiver/lib/dashboard.js +939 -0
  309. package/src/create-quiver/lib/demo.js +14 -12
  310. package/src/create-quiver/lib/handoff.js +16 -7
  311. package/src/create-quiver/lib/i18n/catalog.js +152 -0
  312. package/src/create-quiver/lib/i18n/language.js +279 -0
  313. package/src/create-quiver/lib/i18n/messages/en.js +853 -0
  314. package/src/create-quiver/lib/i18n/messages/es.js +968 -0
  315. package/src/create-quiver/lib/i18n/read-only-format.js +26 -0
  316. package/src/create-quiver/lib/i18n/templates.js +172 -0
  317. package/src/create-quiver/lib/init-docs.js +24 -4
  318. package/src/create-quiver/lib/init-layout.js +8 -0
  319. package/src/create-quiver/lib/lifecycle.js +53 -46
  320. package/src/create-quiver/lib/project-state-resolver.js +22 -2
  321. package/src/create-quiver/lib/readiness.js +77 -67
  322. package/src/create-quiver/lib/renderers/tree.js +9 -6
  323. package/src/create-quiver/lib/spec-worktrees.js +34 -30
  324. package/src/create-quiver/lib/version.js +159 -0
@@ -15,6 +15,7 @@ const { parseContextProposalOutput } = require('../lib/ai/context-proposal');
15
15
  const { openEditor } = require('../lib/cli/editor');
16
16
  const { selectOption, promptText } = require('../lib/cli/selectors');
17
17
  const { createUx } = require('../lib/cli/ux');
18
+ const { createTranslator } = require('../lib/i18n/catalog');
18
19
  const { runExecuteSlice, runPromptSlice } = require('../lib/ai/executor');
19
20
  const { runExecutePlan } = require('../lib/ai/execution-plan');
20
21
  const { buildPrCreatePlan, formatPreflightReport, formatPrCreateReport, preflightGitHubPr, runGhPrCreate } = require('../lib/ai/github');
@@ -90,6 +91,7 @@ const {
90
91
  formatApprovalDecisionLines,
91
92
  } = require('../lib/ai/approval-candidates');
92
93
  const { assertPlannerPhaseReady, getPlannerPhaseDetails, normalizePlannerPhase, PlannerPhaseError } = require('../lib/ai/phase-gates');
94
+ const { formatStatus, translatorForHuman } = require('../lib/i18n/read-only-format');
93
95
  const { collectActiveSliceState, resolveProjectState } = require('../lib/project-state-resolver');
94
96
 
95
97
  const DEFAULT_ONBOARD_PROVIDER = 'codex';
@@ -106,6 +108,23 @@ function formatError(message) {
106
108
  return `create-quiver: ${message}`;
107
109
  }
108
110
 
111
+ function formatLocalizedActionableError({ failure, impact, fix, nextCommand } = {}, options = {}) {
112
+ const translator = createTranslator(options.language);
113
+ const lines = [`create-quiver: ${String(failure || 'operation failed').trim()}`];
114
+
115
+ if (impact) {
116
+ lines.push(`${translator.t('ai.actionable.impact')}: ${String(impact).trim()}`);
117
+ }
118
+ if (fix) {
119
+ lines.push(`${translator.t('ai.actionable.fix')}: ${String(fix).trim()}`);
120
+ }
121
+ if (nextCommand) {
122
+ lines.push(`${translator.t('ai.actionable.next_command')}: ${String(nextCommand).trim()}`);
123
+ }
124
+
125
+ return lines.join('\n');
126
+ }
127
+
109
128
  function readTextFile(filePath, repoRoot) {
110
129
  if (!filePath) {
111
130
  return '';
@@ -226,8 +245,12 @@ function shouldShowHumanProgress(ux, options = {}) {
226
245
  && ux?.mode?.decoration === true;
227
246
  }
228
247
 
229
- function plannerProgressTitle(action, runtimeProfile) {
230
- return `${action} con ${runtimeProfile.displayName || runtimeProfile.model || runtimeProfile.provider}`;
248
+ function plannerProgressTitle(action, runtimeProfile, options = {}) {
249
+ const translator = createTranslator(options.language);
250
+ return translator.t('ai.planner.progress.title', {
251
+ action,
252
+ profile: runtimeProfile.displayName || runtimeProfile.model || runtimeProfile.provider,
253
+ });
231
254
  }
232
255
 
233
256
  function writeProgressChecks(ux, enabled, title, checks = []) {
@@ -240,7 +263,7 @@ function writeProgressChecks(ux, enabled, title, checks = []) {
240
263
  }
241
264
  }
242
265
 
243
- async function runProviderWithProgress({ ux, enabled, message = 'Ejecutando agente...', successMessage = 'Agente finalizado', failureMessage = 'Fallo ejecutando agente', run }) {
266
+ async function runProviderWithProgress({ ux, enabled, message = 'Running agent...', successMessage = 'Agent finished', failureMessage = 'Agent failed', run }) {
244
267
  async function runAndFailOnProviderResult() {
245
268
  const result = await run();
246
269
  if (result && result.ok === false) {
@@ -327,77 +350,85 @@ function buildOnboardContext({ role, context, inputText, inputPath, repoRoot })
327
350
  };
328
351
  }
329
352
 
330
- function formatDryRunReport({ task, provider, role, contextPack, phase, invocation, onboardingPlan }) {
353
+ function formatDryRunReport({ task, provider, role, contextPack, phase, invocation, onboardingPlan, language = 'en' }) {
354
+ const translator = createTranslator(language);
355
+ const modelStatus = invocation.modelSelection?.supported
356
+ ? translator.t('ai_task.model_supported')
357
+ : translator.t('ai_task.model_unsupported');
331
358
  const lines = [
332
- `AI ${task} dry-run`,
333
- `Provider: ${provider}`,
334
- `Role: ${role}`,
335
- `Context pack: ${contextPack}`,
359
+ translator.t('ai_task.title.dry_run', { task }),
360
+ translator.t('ai_task.provider', { provider }),
361
+ translator.t('ai_task.role', { role }),
362
+ translator.t('ai_task.context_pack', { context: contextPack }),
336
363
  ];
337
364
 
338
365
  if (phase) {
339
- lines.push(`Phase: ${phase}`);
366
+ lines.push(translator.t('ai_task.phase', { phase }));
340
367
  }
341
368
 
342
- lines.push(`Command: ${invocation.command} ${invocation.args.join(' ')}`);
343
- lines.push(`Timeout: ${invocation.timeoutMs}ms`);
344
- lines.push(`Prompt transport: ${invocation.promptTransport.mode}`);
345
- lines.push(`Prompt length: ${invocation.promptLength} bytes`);
369
+ lines.push(translator.t('ai_task.command', { command: `${invocation.command} ${invocation.args.join(' ')}`.trim() }));
370
+ lines.push(translator.t('ai_task.timeout', { timeout: invocation.timeoutMs }));
371
+ lines.push(translator.t('ai_task.prompt_transport', { mode: invocation.promptTransport.mode }));
372
+ lines.push(translator.t('ai_task.prompt_length', { bytes: invocation.promptLength }));
346
373
  if (invocation.modelSelection && invocation.modelSelection.model) {
347
- lines.push(`Model: ${invocation.modelSelection.model}`);
348
- lines.push(`Model support: ${invocation.modelSelection.supported ? 'supported' : 'unsupported'} (${invocation.modelSelection.reason})`);
374
+ lines.push(translator.t('ai_task.model', { model: invocation.modelSelection.model }));
375
+ lines.push(translator.t('ai_task.model_support', { status: modelStatus, reason: invocation.modelSelection.reason }));
349
376
  }
350
377
 
351
378
  if (onboardingPlan) {
352
- lines.push(`Prompt source: ${onboardingPlan.promptSource}`);
353
- lines.push(`Selected docs: ${onboardingPlan.selectedDocs.length}`);
354
- lines.push(`Documentation debt: ${onboardingPlan.missingDocs.length}`);
379
+ lines.push(translator.t('ai_task.prompt_source', { source: onboardingPlan.promptSource }));
380
+ lines.push(translator.t('ai_task.selected_docs', { count: onboardingPlan.selectedDocs.length }));
381
+ lines.push(translator.t('ai_task.documentation_debt', { count: onboardingPlan.missingDocs.length }));
355
382
  }
356
383
 
357
384
  return `${lines.join('\n')}\n`;
358
385
  }
359
386
 
360
- function formatPromptOnlyReport({ task, provider, role, contextPack, phase, invocation, prompt, onboardingPlan, promptSource, inputPath, inputKind, inputVersion }) {
387
+ function formatPromptOnlyReport({ task, provider, role, contextPack, phase, invocation, prompt, onboardingPlan, promptSource, inputPath, inputKind, inputVersion, language = 'en' }) {
388
+ const translator = createTranslator(language);
389
+ const modelStatus = invocation.modelSelection?.supported
390
+ ? translator.t('ai_task.model_supported')
391
+ : translator.t('ai_task.model_unsupported');
361
392
  const lines = [
362
- `AI ${task} prompt-only`,
363
- `Provider: ${provider}`,
364
- `Role: ${role}`,
365
- `Context pack: ${contextPack}`,
393
+ translator.t('ai_task.title.prompt_only', { task }),
394
+ translator.t('ai_task.provider', { provider }),
395
+ translator.t('ai_task.role', { role }),
396
+ translator.t('ai_task.context_pack', { context: contextPack }),
366
397
  ];
367
398
 
368
399
  if (phase) {
369
- lines.push(`Phase: ${phase}`);
400
+ lines.push(translator.t('ai_task.phase', { phase }));
370
401
  }
371
402
 
372
- lines.push(`Command: ${invocation.command} ${invocation.args.join(' ')}`);
373
- lines.push(`Timeout: ${invocation.timeoutMs}ms`);
374
- lines.push(`Prompt transport: ${invocation.promptTransport.mode}`);
375
- lines.push(`Prompt length: ${invocation.promptLength} bytes`);
403
+ lines.push(translator.t('ai_task.command', { command: `${invocation.command} ${invocation.args.join(' ')}`.trim() }));
404
+ lines.push(translator.t('ai_task.timeout', { timeout: invocation.timeoutMs }));
405
+ lines.push(translator.t('ai_task.prompt_transport', { mode: invocation.promptTransport.mode }));
406
+ lines.push(translator.t('ai_task.prompt_length', { bytes: invocation.promptLength }));
376
407
  if (invocation.modelSelection && invocation.modelSelection.model) {
377
- lines.push(`Model: ${invocation.modelSelection.model}`);
378
- lines.push(`Model support: ${invocation.modelSelection.supported ? 'supported' : 'unsupported'} (${invocation.modelSelection.reason})`);
408
+ lines.push(translator.t('ai_task.model', { model: invocation.modelSelection.model }));
409
+ lines.push(translator.t('ai_task.model_support', { status: modelStatus, reason: invocation.modelSelection.reason }));
379
410
  }
380
411
 
381
412
  if (onboardingPlan) {
382
- lines.push(`Prompt source: ${onboardingPlan.promptSource}`);
383
- lines.push(`Selected docs: ${onboardingPlan.selectedDocs.length}`);
384
- lines.push(`Documentation debt: ${onboardingPlan.missingDocs.length}`);
413
+ lines.push(translator.t('ai_task.prompt_source', { source: onboardingPlan.promptSource }));
414
+ lines.push(translator.t('ai_task.selected_docs', { count: onboardingPlan.selectedDocs.length }));
415
+ lines.push(translator.t('ai_task.documentation_debt', { count: onboardingPlan.missingDocs.length }));
385
416
  }
386
417
 
387
418
  if (promptSource) {
388
- lines.push(`Prompt source: ${promptSource}`);
419
+ lines.push(translator.t('ai_task.prompt_source', { source: promptSource }));
389
420
  }
390
421
 
391
422
  if (inputPath) {
392
- lines.push(`Input file: ${inputPath}`);
423
+ lines.push(translator.t('ai_task.input_file', { path: inputPath }));
393
424
  }
394
425
 
395
426
  if (inputKind) {
396
- lines.push(`Input kind: ${inputKind}`);
427
+ lines.push(translator.t('ai_task.input_kind', { kind: inputKind }));
397
428
  }
398
429
 
399
430
  if (inputVersion) {
400
- lines.push(`Input version: v${inputVersion}`);
431
+ lines.push(translator.t('ai_task.input_version', { version: inputVersion }));
401
432
  }
402
433
 
403
434
  lines.push('--- PROMPT START ---');
@@ -415,21 +446,22 @@ function formatPathList(items, emptyLabel = 'none') {
415
446
  return items.map((item) => `- ${item}`);
416
447
  }
417
448
 
418
- function formatContextPreparationReport({ dryRun, plan, writePlan, writtenDocs, snapshot, completed = false }) {
449
+ function formatContextPreparationReport({ dryRun, plan, writePlan, writtenDocs, snapshot, completed = false, language = 'en' }) {
450
+ const translator = createTranslator(language);
419
451
  const lines = [
420
- dryRun ? 'AI prepare-context dry-run' : completed ? 'AI prepare-context completed' : 'AI prepare-context write plan',
421
- `Mode: ${dryRun ? 'dry-run' : 'live'}`,
422
- `Project: ${plan.projectName}`,
423
- `Project slug: ${plan.projectSlug}`,
424
- 'Writes: docs-only',
425
- 'Product code: untouched',
426
- `Proposed docs: ${writePlan.length > 0 ? writePlan.map((item) => item.path).join(', ') : 'none'}`,
452
+ dryRun ? translator.t('prepare_context.title.dry_run') : completed ? translator.t('prepare_context.title.completed') : translator.t('prepare_context.title.write_plan'),
453
+ translator.t('prepare_context.mode', { mode: dryRun ? 'dry-run' : 'live' }),
454
+ translator.t('prepare_context.project', { project: plan.projectName }),
455
+ translator.t('prepare_context.project_slug', { slug: plan.projectSlug }),
456
+ translator.t('prepare_context.writes_docs_only'),
457
+ translator.t('prepare_context.product_code_untouched'),
458
+ translator.t('prepare_context.proposed_docs', { docs: writePlan.length > 0 ? writePlan.map((item) => item.path).join(', ') : translator.t('common.none') }),
427
459
  ];
428
460
 
429
461
  if (!dryRun) {
430
- lines.push(`${completed ? 'Written docs' : 'Planned writes'}: ${writtenDocs.length > 0 ? writtenDocs.join(', ') : 'none'}`);
462
+ lines.push(translator.t(completed ? 'prepare_context.written_docs' : 'prepare_context.planned_writes', { docs: writtenDocs.length > 0 ? writtenDocs.join(', ') : translator.t('common.none') }));
431
463
  if (snapshot) {
432
- lines.push(`Snapshot: ${snapshot.root}`);
464
+ lines.push(translator.t('prepare_context.snapshot', { path: snapshot.root }));
433
465
  }
434
466
  }
435
467
 
@@ -438,21 +470,21 @@ function formatContextPreparationReport({ dryRun, plan, writePlan, writtenDocs,
438
470
  }
439
471
 
440
472
  lines.push(
441
- 'Proposed changes:',
473
+ translator.t('prepare_context.proposed_changes'),
442
474
  ...writePlan.map((item) => `- ${item.path}: ${item.action}${item.reason ? ` (${item.reason})` : ''}`),
443
- 'Diff preview:',
475
+ translator.t('prepare_context.diff_preview'),
444
476
  ...formatDiffPreview(writePlan),
445
- 'Files considered:',
446
- ...plan.filesConsidered.map((item) => `- ${item.path}: ${item.present ? 'present' : 'absent'}${item.reason ? ` (${item.reason})` : ''}`),
447
- 'Assumptions:',
448
- ...formatPathList(plan.assumptions),
449
- 'Risks:',
450
- ...formatPathList(plan.risks),
451
- 'Contradictions:',
452
- ...formatPathList(plan.contradictions),
453
- 'Omitted paths:',
454
- ...formatPathList(plan.omittedPaths),
455
- 'Uncertainty markers: TODO | Assumption | Pending confirmation',
477
+ translator.t('prepare_context.files_considered'),
478
+ ...plan.filesConsidered.map((item) => `- ${item.path}: ${item.present ? translator.t('prepare_context.present') : translator.t('prepare_context.absent')}${item.reason ? ` (${item.reason})` : ''}`),
479
+ translator.t('prepare_context.assumptions'),
480
+ ...formatPathList(plan.assumptions, translator.t('common.none')),
481
+ translator.t('prepare_context.risks'),
482
+ ...formatPathList(plan.risks, translator.t('common.none')),
483
+ translator.t('prepare_context.contradictions'),
484
+ ...formatPathList(plan.contradictions, translator.t('common.none')),
485
+ translator.t('prepare_context.omitted_paths'),
486
+ ...formatPathList(plan.omittedPaths, translator.t('common.none')),
487
+ translator.t('prepare_context.uncertainty_markers'),
456
488
  );
457
489
 
458
490
  return `${lines.join('\n')}\n`;
@@ -537,12 +569,13 @@ function buildPrepareContextPlannerPrompt({ pack, draftPack }) {
537
569
  };
538
570
  }
539
571
 
540
- function formatPrepareContextPlannerDryRunReport({ provider, role, context, invocation, promptInfo, review, interactive }) {
572
+ function formatPrepareContextPlannerDryRunReport({ provider, role, context, invocation, promptInfo, review, interactive, language = 'en' }) {
573
+ const translator = createTranslator(language);
541
574
  const plan = promptInfo.plan;
542
575
  const lines = [
543
- 'AI prepare-context planner dry-run',
544
- 'Mode: dry-run',
545
- 'Planner: enabled',
576
+ translator.t('prepare_context_planner.title.dry_run'),
577
+ translator.t('prepare_context.mode', { mode: 'dry-run' }),
578
+ translator.t('prepare_context_planner.enabled'),
546
579
  `Provider: ${provider}`,
547
580
  `Role: ${role}`,
548
581
  `Context pack: ${context}`,
@@ -554,18 +587,18 @@ function formatPrepareContextPlannerDryRunReport({ provider, role, context, invo
554
587
  invocation.modelSelection && invocation.modelSelection.model
555
588
  ? `Model support: ${invocation.modelSelection.supported ? 'supported' : 'unsupported'} (${invocation.modelSelection.reason})`
556
589
  : '',
557
- `Prompt source: ${promptInfo.promptSource}`,
558
- `Review requested: ${review ? 'yes' : 'no'}`,
559
- `Interactive requested: ${interactive ? 'yes' : 'no'}`,
560
- 'Provider execution: skipped',
561
- 'Writes: none',
562
- 'Product code: untouched',
563
- `Candidate docs: ${promptInfo.allowedPaths.join(', ')}`,
564
- 'Files considered:',
565
- ...plan.filesConsidered.map((item) => `- ${item.path}: ${item.present ? 'present' : 'absent'}`),
566
- 'Allowed docs-only paths:',
590
+ translator.t('prepare_context.prompt_source', { source: promptInfo.promptSource }),
591
+ translator.t('prepare_context_planner.review_requested', { value: review ? translator.t('common.yes') : translator.t('common.no') }),
592
+ translator.t('prepare_context_planner.interactive_requested', { value: interactive ? translator.t('common.yes') : translator.t('common.no') }),
593
+ translator.t('prepare_context_planner.provider_execution_skipped'),
594
+ translator.t('prepare_context.writes_none'),
595
+ translator.t('prepare_context.product_code_untouched'),
596
+ translator.t('prepare_context_planner.candidate_docs', { docs: promptInfo.allowedPaths.join(', ') }),
597
+ translator.t('prepare_context.files_considered'),
598
+ ...plan.filesConsidered.map((item) => `- ${item.path}: ${item.present ? translator.t('prepare_context.present') : translator.t('prepare_context.absent')}`),
599
+ translator.t('prepare_context_planner.allowed_docs_only_paths'),
567
600
  ...promptInfo.allowedPaths.map((item) => `- ${item}`),
568
- 'Next safe commands:',
601
+ translator.t('prepare_context_planner.next_safe_commands'),
569
602
  '- npx create-quiver ai prepare-context --with-planner --print-prompt',
570
603
  '- npx create-quiver ai prepare-context --with-planner --dry-run --review',
571
604
  '- npx create-quiver ai prepare-context --with-planner',
@@ -909,17 +942,18 @@ function writeDraftDocs(writePlan) {
909
942
  return writtenDocs;
910
943
  }
911
944
 
912
- function formatSpecDryRunReport({ manifest, repoRoot }) {
945
+ function formatSpecDryRunReport({ manifest, repoRoot, language }) {
946
+ const translator = createTranslator(language);
913
947
  const preview = describeSpecGeneration(manifest, repoRoot);
914
948
  const relativeSpecDir = path.relative(repoRoot, preview.specDir).split(path.sep).join('/');
915
949
  const lines = [
916
- 'AI plan dry-run',
917
- 'Phase: spec',
918
- `Spec slug: ${manifest.slug}`,
919
- `Title: ${manifest.title}`,
920
- `Input file: ${manifest.sourcePath}`,
921
- `Target: ${relativeSpecDir}`,
922
- `Planned files: ${preview.files.length}`,
950
+ translator.t('ai.plan.spec.dry_run.title'),
951
+ translator.t('ai_task.phase', { phase: 'spec' }),
952
+ translator.t('ai.plan.spec.slug', { slug: manifest.slug }),
953
+ translator.t('ai.plan.spec.title', { title: manifest.title }),
954
+ translator.t('ai_task.input_file', { path: manifest.sourcePath }),
955
+ translator.t('ai.plan.spec.target', { target: relativeSpecDir }),
956
+ translator.t('ai.plan.spec.planned_files', { count: preview.files.length }),
923
957
  ];
924
958
 
925
959
  for (const file of preview.files) {
@@ -929,13 +963,14 @@ function formatSpecDryRunReport({ manifest, repoRoot }) {
929
963
  return `${lines.join('\n')}\n`;
930
964
  }
931
965
 
932
- function formatSpecGenerationResult(result, repoRoot) {
966
+ function formatSpecGenerationResult(result, repoRoot, options = {}) {
967
+ const translator = createTranslator(options.language);
933
968
  const relativeSpecDir = path.relative(repoRoot, result.specDir).split(path.sep).join('/');
934
969
  const lines = [
935
- 'AI plan spec generation completed',
936
- `Spec slug: ${result.manifest.slug}`,
937
- `Target: ${relativeSpecDir}`,
938
- `Files written: ${result.files.length}`,
970
+ translator.t('ai.plan.spec.completed'),
971
+ translator.t('ai.plan.spec.slug', { slug: result.manifest.slug }),
972
+ translator.t('ai.plan.spec.target', { target: relativeSpecDir }),
973
+ translator.t('ai.plan.spec.files_written', { count: result.files.length }),
939
974
  ];
940
975
 
941
976
  for (const filePath of result.files) {
@@ -945,30 +980,32 @@ function formatSpecGenerationResult(result, repoRoot) {
945
980
  return `${lines.join('\n')}\n`;
946
981
  }
947
982
 
948
- function formatApprovalResult(result, repoRoot) {
983
+ function formatApprovalResult(result, repoRoot, options = {}) {
984
+ const translator = createTranslator(options.language);
949
985
  const relativePath = path.relative(repoRoot, result.filePath).split(path.sep).join('/');
950
986
  const lines = [
951
- 'AI approval saved',
952
- `Phase: ${result.phase}`,
953
- `Status: approved`,
954
- `Artifact: ${relativePath}`,
955
- `Source file: ${result.sourceFile}`,
956
- `Timestamp: ${result.createdAt}`,
987
+ translator.t('ai.approve.saved'),
988
+ translator.t('ai_task.phase', { phase: result.phase }),
989
+ `${translator.t('ai.table.status')}: ${translator.t('ai.approve.status.approved')}`,
990
+ `${translator.t('ai.approve.artifact')}: ${relativePath}`,
991
+ `${translator.t('ai.approvals.source_file')}: ${result.sourceFile}`,
992
+ `${translator.t('ai.approve.timestamp')}: ${result.createdAt}`,
957
993
  ];
958
994
  if (result.version) {
959
- lines.push(`Version: v${result.version}`);
995
+ lines.push(`${translator.t('ai.approve.version')}: v${result.version}`);
960
996
  }
961
997
 
962
998
  return `${lines.join('\n')}\n`;
963
999
  }
964
1000
 
965
- function formatApprovalDryRunResult({ phase, input, version }) {
966
- const lines = ['AI approval dry-run', `Phase: ${phase}`];
1001
+ function formatApprovalDryRunResult({ phase, input, version, language }) {
1002
+ const translator = createTranslator(language);
1003
+ const lines = [translator.t('ai.approve.dry_run.title'), translator.t('ai_task.phase', { phase })];
967
1004
  if (version) {
968
- lines.push(`Version: v${version}`);
1005
+ lines.push(`${translator.t('ai.approve.version')}: v${version}`);
969
1006
  }
970
1007
  if (input) {
971
- lines.push(`Input file: ${input}`);
1008
+ lines.push(translator.t('ai_task.input_file', { path: input }));
972
1009
  }
973
1010
  return `${lines.join('\n')}\n`;
974
1011
  }
@@ -1076,15 +1113,16 @@ function buildRepairPlanContext({ context, inputText, inputPath, repoRoot, role,
1076
1113
  };
1077
1114
  }
1078
1115
 
1079
- function formatRepairPlanResult(result, repoRoot) {
1116
+ function formatRepairPlanResult(result, repoRoot, options = {}) {
1117
+ const translator = createTranslator(options.language);
1080
1118
  const relativePath = path.relative(repoRoot, result.filePath).split(path.sep).join('/');
1081
1119
  return [
1082
- 'AI technical-plan repair draft saved',
1083
- `Draft: ${relativePath}`,
1084
- `Version: v${result.version}`,
1085
- `Source approved artifact: ${result.sourcePath}`,
1086
- 'Original approved artifact: preserved',
1087
- 'Next safe commands:',
1120
+ translator.t('ai.repair_plan.saved'),
1121
+ `${translator.t('ai.approvals.draft')}: ${relativePath}`,
1122
+ `${translator.t('ai.approve.version')}: v${result.version}`,
1123
+ `${translator.t('ai.repair_plan.source_approved_artifact')}: ${result.sourcePath}`,
1124
+ translator.t('ai.repair_plan.original_preserved'),
1125
+ `${translator.t('ai.label.next_safe_commands')}:`,
1088
1126
  '- npx create-quiver ai review-plan --dry-run',
1089
1127
  '- npx create-quiver ai review-plan',
1090
1128
  `- npx create-quiver ai approve --phase technical-plan --version ${result.version}`,
@@ -1188,16 +1226,17 @@ function classifyGlobalApprovalRelation(report, runApprovalRows) {
1188
1226
  return 'orphaned';
1189
1227
  }
1190
1228
 
1191
- function formatRunScopedApprovals(repoRoot, runApprovalRows) {
1229
+ function formatRunScopedApprovals(repoRoot, runApprovalRows, options = {}) {
1230
+ const translator = translatorForHuman(options);
1192
1231
  const runs = listAiRuns(repoRoot);
1193
1232
  const activeRun = resolveAiRun(repoRoot, '');
1194
1233
  const lines = [
1195
- 'Run-scoped approvals',
1196
- `Active run: ${activeRun ? activeRun.run_id : '(none)'}`,
1234
+ translator.t('ai.approvals.run_scoped'),
1235
+ `${translator.t('ai.approvals.active_run')}: ${activeRun ? activeRun.run_id : translator.t('ai.approvals.none_value')}`,
1197
1236
  ];
1198
1237
 
1199
1238
  if (runs.length === 0) {
1200
- lines.push('- no AI runs found');
1239
+ lines.push(`- ${translator.t('ai.approvals.no_ai_runs')}`);
1201
1240
  return `${lines.join('\n')}\n`;
1202
1241
  }
1203
1242
 
@@ -1208,9 +1247,9 @@ function formatRunScopedApprovals(repoRoot, runApprovalRows) {
1208
1247
  ? 'historical'
1209
1248
  : 'other-open';
1210
1249
  const approvals = runApprovalRows.filter((row) => row.run.run_id === run.run_id);
1211
- lines.push(`Run: ${run.run_id} (${relation}, phase: ${run.phase}, status: ${run.status})`);
1250
+ lines.push(`${translator.t('ai.run.run')}: ${run.run_id} (${translator.t(`ai.approvals.relation.${relation}`)}, ${translator.t('ai.run.phase').toLowerCase()}: ${run.phase}, ${translator.t('ai.run.status').toLowerCase()}: ${formatStatus(run.status, translator)})`);
1212
1251
  if (approvals.length === 0) {
1213
- lines.push('- no run-scoped approvals');
1252
+ lines.push(`- ${translator.t('ai.approvals.no_run_scoped')}`);
1214
1253
  continue;
1215
1254
  }
1216
1255
  for (const row of approvals) {
@@ -1223,18 +1262,76 @@ function formatRunScopedApprovals(repoRoot, runApprovalRows) {
1223
1262
  }
1224
1263
 
1225
1264
  function formatApprovalStatusReport(repoRoot) {
1265
+ return formatApprovalStatusReportWithOptions(repoRoot);
1266
+ }
1267
+
1268
+ function localizeApprovalSummary(text, translator) {
1269
+ if (translator.language === 'en') {
1270
+ return text;
1271
+ }
1272
+
1273
+ return String(text || '')
1274
+ .split('\n')
1275
+ .map((line) => {
1276
+ let match = line.match(/^Phase: (.+)$/);
1277
+ if (match) return `${translator.t('ai.run.phase')}: ${match[1]}`;
1278
+ match = line.match(/^Status: (.+)$/);
1279
+ if (match) return `${translator.t('ai.run.status')}: ${formatStatus(match[1], translator)}`;
1280
+ match = line.match(/^Draft( v\d+)?: (.+)$/);
1281
+ if (match) return `${translator.t('ai.approvals.draft')}${match[1] || ''}: ${match[2]}`;
1282
+ if (line === 'Draft history:') return `${translator.t('ai.approvals.draft_history')}:`;
1283
+ match = line.match(/^Approved( v\d+)?: (.+)$/);
1284
+ if (match) return `${translator.t('ai.approvals.approved')}${match[1] || ''}: ${match[2]}`;
1285
+ match = line.match(/^Source file: (.+)$/);
1286
+ if (match) return `${translator.t('ai.approvals.source_file')}: ${match[1]}`;
1287
+ match = line.match(/^Review: (.+)$/);
1288
+ if (match) return `${translator.t('ai.approvals.review')}: ${match[1]}`;
1289
+ match = line.match(/^Approval recommendation: (.+)$/);
1290
+ if (match) return `${translator.t('ai.approvals.approval_recommendation')}: ${match[1]}`;
1291
+ match = line.match(/^Blocking: (yes|no)$/);
1292
+ if (match) return `${translator.t('ai.approvals.blocking')}: ${translator.t(match[1] === 'yes' ? 'common.yes' : 'common.no')}`;
1293
+ match = line.match(/^Required fixes: (.+)$/);
1294
+ if (match) return `${translator.t('ai.approvals.required_fixes')}: ${match[1]}`;
1295
+ match = line.match(/^Optional hardening: (.+)$/);
1296
+ if (match) return `${translator.t('ai.approvals.optional_hardening')}: ${match[1]}`;
1297
+ match = line.match(/^Next command: (.+)$/);
1298
+ if (match) return `${translator.t('ai.approvals.next_command')}: ${match[1]}`;
1299
+ return line;
1300
+ })
1301
+ .join('\n');
1302
+ }
1303
+
1304
+ function localizeApprovalDecisionLine(line, translator) {
1305
+ if (translator.language === 'en') {
1306
+ return line;
1307
+ }
1308
+ return String(line || '')
1309
+ .replace(/^Candidates:/, translator.t('ai.approvals.candidates') + ':')
1310
+ .replace(/^Latest draft:/, translator.t('ai.approvals.latest_draft') + ':')
1311
+ .replace(/^Current candidate:/, translator.t('ai.approvals.current_candidate') + ':')
1312
+ .replace(/^Recommended approval:/, translator.t('ai.approvals.recommended_approval') + ':')
1313
+ .replace(/^Recommended next command:/, translator.t('ai.approvals.recommended_next_command') + ':')
1314
+ .replace(/^Review status:/, translator.t('ai.approvals.review_status') + ':');
1315
+ }
1316
+
1317
+ function formatApprovalStatusReportWithOptions(repoRoot, options = {}) {
1318
+ const translator = translatorForHuman(options);
1226
1319
  const runApprovalRows = collectRunApprovalRows(repoRoot);
1227
- const sections = ['AI approvals status', formatRunScopedApprovals(repoRoot, runApprovalRows).trimEnd(), 'Global planner approvals'];
1320
+ const sections = [
1321
+ translator.t('ai.approvals.title'),
1322
+ formatRunScopedApprovals(repoRoot, runApprovalRows, options).trimEnd(),
1323
+ translator.t('ai.approvals.global_planner'),
1324
+ ];
1228
1325
  for (const phase of PLANNER_APPROVAL_PHASES) {
1229
- const summary = summarizePlannerApproval(repoRoot, phase).trimEnd();
1326
+ const summary = localizeApprovalSummary(summarizePlannerApproval(repoRoot, phase).trimEnd(), translator);
1230
1327
  const relation = classifyGlobalApprovalRelation(readPhaseApproval(repoRoot, phase), runApprovalRows);
1231
1328
  const candidates = buildApprovalCandidateReport(repoRoot, phase);
1232
1329
  const decisionLines = formatApprovalDecisionLines(candidates)
1233
- .map((line) => `- ${line}`)
1330
+ .map((line) => `- ${localizeApprovalDecisionLine(line, translator)}`)
1234
1331
  .join('\n');
1235
- sections.push(`${summary}\nRun relation: ${relation}${decisionLines ? `\nApproval candidates:\n${decisionLines}` : ''}`);
1332
+ sections.push(`${summary}\n${translator.t('ai.approvals.run_relation')}: ${translator.t(`ai.approvals.relation.${relation}`)}${decisionLines ? `\n${translator.t('ai.approvals.approval_candidates')}:\n${decisionLines}` : ''}`);
1236
1333
  }
1237
- sections.push(summarizePlanReview(repoRoot).trimEnd());
1334
+ sections.push(localizeApprovalSummary(summarizePlanReview(repoRoot).trimEnd(), translator));
1238
1335
  return `${sections.join('\n\n')}\n`;
1239
1336
  }
1240
1337
 
@@ -1291,6 +1388,7 @@ async function runOnboard(repoRoot, options = {}) {
1291
1388
  role,
1292
1389
  contextPack: context,
1293
1390
  invocation,
1391
+ language: options.language,
1294
1392
  onboardingPlan: contextInfo.plan,
1295
1393
  profile: runtimeProfile,
1296
1394
  };
@@ -1305,6 +1403,7 @@ async function runOnboard(repoRoot, options = {}) {
1305
1403
  role,
1306
1404
  contextPack: context,
1307
1405
  invocation,
1406
+ language: options.language,
1308
1407
  onboardingPlan: contextInfo.plan,
1309
1408
  prompt,
1310
1409
  profile: runtimeProfile,
@@ -1315,11 +1414,16 @@ async function runOnboard(repoRoot, options = {}) {
1315
1414
 
1316
1415
  const ux = createCommandUx(options);
1317
1416
  const showProgress = shouldShowHumanProgress(ux, options);
1417
+ const progressTranslator = createTranslator(options.language);
1318
1418
  writeProgressChecks(
1319
1419
  ux,
1320
1420
  showProgress,
1321
- plannerProgressTitle('Ejecutando onboarding', runtimeProfile),
1322
- ['Leyendo docs base', 'Detectando estructura', 'Preparando prompt'],
1421
+ plannerProgressTitle(progressTranslator.t('ai.planner.progress.onboarding'), runtimeProfile, options),
1422
+ [
1423
+ progressTranslator.t('ai.planner.progress.reading_base_docs'),
1424
+ progressTranslator.t('ai.planner.progress.detecting_structure'),
1425
+ progressTranslator.t('ai.planner.progress.preparing_prompt'),
1426
+ ],
1323
1427
  );
1324
1428
 
1325
1429
  let result;
@@ -1327,6 +1431,9 @@ async function runOnboard(repoRoot, options = {}) {
1327
1431
  result = await runProviderWithProgress({
1328
1432
  ux,
1329
1433
  enabled: showProgress,
1434
+ message: progressTranslator.t('ai.planner.progress.running_agent'),
1435
+ successMessage: progressTranslator.t('ai.planner.progress.agent_finished'),
1436
+ failureMessage: progressTranslator.t('ai.planner.progress.agent_failed'),
1330
1437
  run: () => (options.runProviderFn || runProvider)(provider, {
1331
1438
  prompt,
1332
1439
  cwd: repoRoot,
@@ -1390,6 +1497,7 @@ async function runPrepareContext(repoRoot, options = {}) {
1390
1497
  plan: draftPack.plan,
1391
1498
  writePlan,
1392
1499
  writtenDocs: [],
1500
+ language: options.language,
1393
1501
  }));
1394
1502
  return report;
1395
1503
  }
@@ -1408,6 +1516,7 @@ async function runPrepareContext(repoRoot, options = {}) {
1408
1516
  writePlan,
1409
1517
  writtenDocs: plannedDocs,
1410
1518
  snapshot,
1519
+ language: options.language,
1411
1520
  }));
1412
1521
  const writtenDocs = writeDraftDocs(writePlan);
1413
1522
  updateAiRunPhase(repoRoot, lifecycleRun.run_id, 'onboarding-ready', {
@@ -1421,6 +1530,7 @@ async function runPrepareContext(repoRoot, options = {}) {
1421
1530
  writtenDocs,
1422
1531
  snapshot,
1423
1532
  completed: true,
1533
+ language: options.language,
1424
1534
  }));
1425
1535
 
1426
1536
  return {
@@ -1485,6 +1595,7 @@ async function runPrepareContextWithPlanner(repoRoot, options = {}) {
1485
1595
  promptInfo,
1486
1596
  review: options.review === true,
1487
1597
  interactive: options.interactive === true,
1598
+ language: options.language,
1488
1599
  }));
1489
1600
  return report;
1490
1601
  }
@@ -1506,11 +1617,16 @@ async function runPrepareContextWithPlanner(repoRoot, options = {}) {
1506
1617
 
1507
1618
  const ux = createCommandUx(options);
1508
1619
  const showProgress = shouldShowHumanProgress(ux, options);
1620
+ const progressTranslator = createTranslator(options.language);
1509
1621
  writeProgressChecks(
1510
1622
  ux,
1511
1623
  showProgress,
1512
- plannerProgressTitle('Ejecutando onboarding', runtimeProfile),
1513
- ['Leyendo docs base', 'Detectando estructura', 'Preparando prompt'],
1624
+ plannerProgressTitle(progressTranslator.t('ai.planner.progress.onboarding'), runtimeProfile, options),
1625
+ [
1626
+ progressTranslator.t('ai.planner.progress.reading_base_docs'),
1627
+ progressTranslator.t('ai.planner.progress.detecting_structure'),
1628
+ progressTranslator.t('ai.planner.progress.preparing_prompt'),
1629
+ ],
1514
1630
  );
1515
1631
 
1516
1632
  let result;
@@ -1518,6 +1634,9 @@ async function runPrepareContextWithPlanner(repoRoot, options = {}) {
1518
1634
  result = await runProviderWithProgress({
1519
1635
  ux,
1520
1636
  enabled: showProgress,
1637
+ message: progressTranslator.t('ai.planner.progress.running_agent'),
1638
+ successMessage: progressTranslator.t('ai.planner.progress.agent_finished'),
1639
+ failureMessage: progressTranslator.t('ai.planner.progress.agent_failed'),
1521
1640
  run: () => (options.runProviderFn || runProvider)(provider, {
1522
1641
  prompt,
1523
1642
  cwd: repoRoot,
@@ -1569,6 +1688,7 @@ async function runPrepareContextWithPlanner(repoRoot, options = {}) {
1569
1688
  writePlan,
1570
1689
  writtenDocs: plannedDocs,
1571
1690
  snapshot,
1691
+ language: options.language,
1572
1692
  }));
1573
1693
 
1574
1694
  const writtenDocs = writeDraftDocs(writePlan);
@@ -1583,6 +1703,7 @@ async function runPrepareContextWithPlanner(repoRoot, options = {}) {
1583
1703
  writtenDocs,
1584
1704
  snapshot,
1585
1705
  completed: true,
1706
+ language: options.language,
1586
1707
  }));
1587
1708
 
1588
1709
  return {
@@ -1628,8 +1749,9 @@ async function runPlan(repoRoot, options = {}) {
1628
1749
  phase,
1629
1750
  manifest,
1630
1751
  };
1631
- process.stdout.write('AI plan prompt-only\nPhase: spec\nNo provider prompt is used for spec generation; showing the local generation plan instead.\n');
1632
- process.stdout.write(formatSpecDryRunReport({ manifest, repoRoot }));
1752
+ const translator = createTranslator(options.language);
1753
+ process.stdout.write(`${translator.t('ai_task.title.prompt_only', { task: 'plan' })}\n${translator.t('ai_task.phase', { phase: 'spec' })}\n${translator.t('ai.plan.spec.prompt_only_no_provider')}\n`);
1754
+ process.stdout.write(formatSpecDryRunReport({ manifest, repoRoot, language: options.language }));
1633
1755
  return report;
1634
1756
  }
1635
1757
 
@@ -1639,7 +1761,7 @@ async function runPlan(repoRoot, options = {}) {
1639
1761
  phase,
1640
1762
  manifest,
1641
1763
  };
1642
- process.stdout.write(formatSpecDryRunReport({ manifest, repoRoot }));
1764
+ process.stdout.write(formatSpecDryRunReport({ manifest, repoRoot, language: options.language }));
1643
1765
  return report;
1644
1766
  }
1645
1767
 
@@ -1647,7 +1769,7 @@ async function runPlan(repoRoot, options = {}) {
1647
1769
  input: inputPath,
1648
1770
  specSlug: options.specSlug,
1649
1771
  });
1650
- process.stdout.write(formatSpecGenerationResult(result, repoRoot));
1772
+ process.stdout.write(formatSpecGenerationResult(result, repoRoot, options));
1651
1773
 
1652
1774
  return {
1653
1775
  task: 'plan',
@@ -1728,15 +1850,15 @@ async function runPlan(repoRoot, options = {}) {
1728
1850
  invocation,
1729
1851
  profile: runtimeProfile,
1730
1852
  };
1731
- process.stdout.write(formatDryRunReport(report));
1853
+ process.stdout.write(formatDryRunReport({ ...report, language: options.language }));
1732
1854
  if (options.withPlanner === true) {
1733
- process.stdout.write('Planner mode: already active for ai plan; --with-planner is accepted for UX consistency.\n');
1855
+ process.stdout.write(`${createTranslator(options.language).t('ai.plan.with_planner_already_active')}\n`);
1734
1856
  }
1735
1857
  if (options.review === true) {
1736
- process.stdout.write('Review requested: provider output will be opened for review before saving the draft in live mode.\n');
1858
+ process.stdout.write(`${createTranslator(options.language).t('ai.plan.review_requested')}\n`);
1737
1859
  }
1738
1860
  if (options.interactive === true) {
1739
- process.stdout.write('Interactive requested: live mode will ask before saving the draft.\n');
1861
+ process.stdout.write(`${createTranslator(options.language).t('ai.plan.interactive_requested')}\n`);
1740
1862
  }
1741
1863
  return report;
1742
1864
  }
@@ -1752,7 +1874,7 @@ async function runPlan(repoRoot, options = {}) {
1752
1874
  prompt,
1753
1875
  profile: runtimeProfile,
1754
1876
  };
1755
- process.stdout.write(formatPromptOnlyReport(report));
1877
+ process.stdout.write(formatPromptOnlyReport({ ...report, language: options.language }));
1756
1878
  return report;
1757
1879
  }
1758
1880
 
@@ -1761,8 +1883,12 @@ async function runPlan(repoRoot, options = {}) {
1761
1883
  writeProgressChecks(
1762
1884
  ux,
1763
1885
  showProgress,
1764
- plannerProgressTitle(`Ejecutando plan ${phase}`, runtimeProfile),
1765
- ['Leyendo entrada', 'Preparando contexto', 'Preparando prompt'],
1886
+ plannerProgressTitle(createTranslator(options.language).t('ai.planner.progress.plan', { phase }), runtimeProfile, options),
1887
+ [
1888
+ createTranslator(options.language).t('ai.planner.progress.reading_input'),
1889
+ createTranslator(options.language).t('ai.planner.progress.preparing_context'),
1890
+ createTranslator(options.language).t('ai.planner.progress.preparing_prompt'),
1891
+ ],
1766
1892
  );
1767
1893
 
1768
1894
  let result;
@@ -1905,12 +2031,14 @@ async function runReviewPlan(repoRoot, options = {}) {
1905
2031
  contextPack: pack.packName,
1906
2032
  phase: 'plan-review',
1907
2033
  invocation,
2034
+ language: options.language,
1908
2035
  }));
1909
- process.stdout.write(`Prompt source: ${built.promptSource}\n`);
1910
- process.stdout.write(`Input file: ${inputPath}\n`);
1911
- process.stdout.write(`Input kind: ${resolved.kind}\n`);
2036
+ const translator = createTranslator(options.language);
2037
+ process.stdout.write(`${translator.t('ai_task.prompt_source', { source: built.promptSource })}\n`);
2038
+ process.stdout.write(`${translator.t('ai_task.input_file', { path: inputPath })}\n`);
2039
+ process.stdout.write(`${translator.t('ai_task.input_kind', { kind: resolved.kind })}\n`);
1912
2040
  if (resolved.version) {
1913
- process.stdout.write(`Input version: v${resolved.version}\n`);
2041
+ process.stdout.write(`${translator.t('ai_task.input_version', { version: resolved.version })}\n`);
1914
2042
  }
1915
2043
  return report;
1916
2044
  }
@@ -1930,7 +2058,7 @@ async function runReviewPlan(repoRoot, options = {}) {
1930
2058
  inputVersion: resolved.version,
1931
2059
  profile: runtimeProfile,
1932
2060
  };
1933
- process.stdout.write(formatPromptOnlyReport(report));
2061
+ process.stdout.write(formatPromptOnlyReport({ ...report, language: options.language }));
1934
2062
  return report;
1935
2063
  }
1936
2064
 
@@ -1939,8 +2067,12 @@ async function runReviewPlan(repoRoot, options = {}) {
1939
2067
  writeProgressChecks(
1940
2068
  ux,
1941
2069
  showProgress,
1942
- plannerProgressTitle('Ejecutando revisión del plan', runtimeProfile),
1943
- ['Leyendo plan técnico', 'Preparando contexto', 'Preparando prompt'],
2070
+ plannerProgressTitle(createTranslator(options.language).t('ai.planner.progress.review_plan'), runtimeProfile, options),
2071
+ [
2072
+ createTranslator(options.language).t('ai.planner.progress.reading_technical_plan'),
2073
+ createTranslator(options.language).t('ai.planner.progress.preparing_context'),
2074
+ createTranslator(options.language).t('ai.planner.progress.preparing_prompt'),
2075
+ ],
1944
2076
  );
1945
2077
 
1946
2078
  let result;
@@ -1999,8 +2131,9 @@ async function runReviewPlan(repoRoot, options = {}) {
1999
2131
  rawArtifactPath: rawArtifact.path,
2000
2132
  });
2001
2133
  const relativePath = path.relative(repoRoot, saved.filePath).split(path.sep).join('/');
2002
- const summary = summarizePlanReview(repoRoot).trimEnd();
2003
- process.stdout.write(`AI plan review saved\nArtifact: ${relativePath}\nPrompt source: ${PLAN_REVIEW_PROMPT_SOURCE}\n${summary}\n`);
2134
+ const summary = localizeApprovalSummary(summarizePlanReview(repoRoot), createTranslator(options.language)).trimEnd();
2135
+ const translator = createTranslator(options.language);
2136
+ process.stdout.write(`${translator.t('ai.review_plan.saved')}\n${translator.t('ai.approve.artifact')}: ${relativePath}\n${translator.t('ai_task.prompt_source', { source: PLAN_REVIEW_PROMPT_SOURCE })}\n${summary}\n`);
2004
2137
 
2005
2138
  return {
2006
2139
  task: 'review-plan',
@@ -2060,9 +2193,10 @@ async function runRepairPlan(repoRoot, options = {}) {
2060
2193
  invocation,
2061
2194
  profile: runtimeProfile,
2062
2195
  };
2063
- process.stdout.write(formatDryRunReport(report));
2064
- process.stdout.write(`Source approved artifact: ${source.path}\n`);
2065
- process.stdout.write(`Validation failure: ${source.validationError}\n`);
2196
+ process.stdout.write(formatDryRunReport({ ...report, language: options.language }));
2197
+ const translator = createTranslator(options.language);
2198
+ process.stdout.write(`${translator.t('ai.repair_plan.source_approved_artifact')}: ${source.path}\n`);
2199
+ process.stdout.write(`${translator.t('ai.repair_plan.validation_failure')}: ${source.validationError}\n`);
2066
2200
  return report;
2067
2201
  }
2068
2202
 
@@ -2080,7 +2214,7 @@ async function runRepairPlan(repoRoot, options = {}) {
2080
2214
  inputVersion: source.approval.meta?.approved?.version || null,
2081
2215
  profile: runtimeProfile,
2082
2216
  };
2083
- process.stdout.write(formatPromptOnlyReport(report));
2217
+ process.stdout.write(formatPromptOnlyReport({ ...report, language: options.language }));
2084
2218
  return report;
2085
2219
  }
2086
2220
 
@@ -2089,15 +2223,23 @@ async function runRepairPlan(repoRoot, options = {}) {
2089
2223
  writeProgressChecks(
2090
2224
  ux,
2091
2225
  showProgress,
2092
- plannerProgressTitle('Ejecutando reparación del plan', runtimeProfile),
2093
- ['Leyendo plan aprobado', 'Preparando contexto', 'Preparando prompt'],
2226
+ plannerProgressTitle(createTranslator(options.language).t('ai.planner.progress.repair_plan'), runtimeProfile, options),
2227
+ [
2228
+ createTranslator(options.language).t('ai.planner.progress.reading_approved_plan'),
2229
+ createTranslator(options.language).t('ai.planner.progress.preparing_context'),
2230
+ createTranslator(options.language).t('ai.planner.progress.preparing_prompt'),
2231
+ ],
2094
2232
  );
2095
2233
 
2096
2234
  let providerResult;
2097
2235
  try {
2236
+ const progressTranslator = createTranslator(options.language);
2098
2237
  providerResult = await runProviderWithProgress({
2099
2238
  ux,
2100
2239
  enabled: showProgress,
2240
+ message: progressTranslator.t('ai.planner.progress.running_agent'),
2241
+ successMessage: progressTranslator.t('ai.planner.progress.agent_finished'),
2242
+ failureMessage: progressTranslator.t('ai.planner.progress.agent_failed'),
2101
2243
  run: () => (options.runProviderFn || runProvider)(provider, {
2102
2244
  prompt: built.prompt,
2103
2245
  cwd: repoRoot,
@@ -2164,7 +2306,7 @@ async function runRepairPlan(repoRoot, options = {}) {
2164
2306
  process.stdout.write(formatRepairPlanResult({
2165
2307
  ...draft,
2166
2308
  sourcePath: source.path,
2167
- }, repoRoot));
2309
+ }, repoRoot, options));
2168
2310
 
2169
2311
  return {
2170
2312
  task: 'repair-plan',
@@ -2181,14 +2323,18 @@ async function runRepairPlan(repoRoot, options = {}) {
2181
2323
  }
2182
2324
 
2183
2325
  async function runRevise(repoRoot, options = {}) {
2326
+ const translator = createTranslator(options.language);
2184
2327
  const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
2185
2328
  if (phase === 'spec') {
2186
- throw new Error(formatError(`ai revise does not support phase '${phase}'`));
2329
+ throw new Error(formatError(translator.t('ai.revise.error.unsupported_phase', { phase })));
2187
2330
  }
2188
2331
 
2189
2332
  const approval = readPhaseApproval(repoRoot, phase);
2190
2333
  if (approval.status !== 'draft' && approval.status !== 'stale') {
2191
- throw new Error(formatError(`ai revise --phase ${phase} requires an existing draft; current status is ${approval.status}. Run \`npx create-quiver ai plan --phase ${phase} --input <file>\` first.`));
2334
+ throw new Error(formatError(translator.t('ai.revise.error.requires_draft', {
2335
+ phase,
2336
+ status: approval.status,
2337
+ })));
2192
2338
  }
2193
2339
 
2194
2340
  return runPlan(repoRoot, {
@@ -2198,10 +2344,11 @@ async function runRevise(repoRoot, options = {}) {
2198
2344
  });
2199
2345
  }
2200
2346
 
2201
- function formatApprovalCandidateHint(candidate) {
2347
+ function formatApprovalCandidateHint(candidate, options = {}) {
2348
+ const translator = createTranslator(options.language);
2202
2349
  const parts = [];
2203
2350
  if (candidate.current) {
2204
- parts.push('current');
2351
+ parts.push(translator.t('ai.approvals.current_candidate').toLowerCase());
2205
2352
  }
2206
2353
  if (candidate.created_at) {
2207
2354
  parts.push(candidate.created_at);
@@ -2210,10 +2357,10 @@ function formatApprovalCandidateHint(candidate) {
2210
2357
  parts.push(`review=${candidate.review.recommendation}`);
2211
2358
  }
2212
2359
  if (candidate.review?.required_fixes_count) {
2213
- parts.push(`required fixes=${candidate.review.required_fixes_count}`);
2360
+ parts.push(`${translator.t('ai.approvals.required_fixes').toLowerCase()}=${candidate.review.required_fixes_count}`);
2214
2361
  }
2215
2362
  if (candidate.review?.optional_hardening_count) {
2216
- parts.push(`optional=${candidate.review.optional_hardening_count}`);
2363
+ parts.push(`${translator.t('ai.approvals.optional_hardening').toLowerCase()}=${candidate.review.optional_hardening_count}`);
2217
2364
  }
2218
2365
  if (candidate.review?.risks_count) {
2219
2366
  parts.push(`risks=${candidate.review.risks_count}`);
@@ -2222,17 +2369,19 @@ function formatApprovalCandidateHint(candidate) {
2222
2369
  return parts.filter(Boolean).join(', ');
2223
2370
  }
2224
2371
 
2225
- function approvalSelectionOptions(report) {
2372
+ function approvalSelectionOptions(report, options = {}) {
2373
+ const translator = createTranslator(options.language);
2226
2374
  return report.candidates.map((candidate) => ({
2227
- label: `${candidate.label}${candidate.recommended ? ' (recommended)' : candidate.current ? ' (current)' : ' (history)'}`,
2375
+ label: `${candidate.label}${candidate.recommended ? ` (${translator.t('ai.approvals.recommended_approval').toLowerCase()})` : candidate.current ? ` (${translator.t('ai.approvals.current_candidate').toLowerCase()})` : ` (${translator.t('ai.approvals.draft_history').toLowerCase()})`}`,
2228
2376
  value: String(candidate.version || ''),
2229
- hint: formatApprovalCandidateHint(candidate),
2377
+ hint: formatApprovalCandidateHint(candidate, options),
2230
2378
  default: candidate.recommended === true,
2231
2379
  raw: candidate,
2232
2380
  }));
2233
2381
  }
2234
2382
 
2235
2383
  async function resolveApprovalVersion(repoRoot, phase, options = {}) {
2384
+ const translator = createTranslator(options.language);
2236
2385
  if (options.version) {
2237
2386
  return options.version;
2238
2387
  }
@@ -2243,24 +2392,24 @@ async function resolveApprovalVersion(repoRoot, phase, options = {}) {
2243
2392
 
2244
2393
  if (!shouldPrompt || !canPrompt) {
2245
2394
  const recommended = report.recommended?.version || report.latest_version || '<n>';
2246
- throw new Error(formatActionableError({
2247
- failure: `ai approve --phase ${phase} requires --version <n> when prompts are not available.`,
2248
- impact: 'Quiver cannot safely guess which saved planner draft the human approved.',
2249
- fix: 'Review drafts with `npx create-quiver ai approvals`, then pass the version explicitly.',
2395
+ throw new Error(formatLocalizedActionableError({
2396
+ failure: translator.t('ai.approve.error.no_prompt.failure', { phase }),
2397
+ impact: translator.t('ai.approve.error.no_prompt.impact'),
2398
+ fix: translator.t('ai.approve.error.no_prompt.fix'),
2250
2399
  nextCommand: `npx create-quiver ai approve --phase ${phase} --version ${recommended}`,
2251
- }));
2400
+ }, options));
2252
2401
  }
2253
2402
 
2254
2403
  if (report.candidates.length === 0) {
2255
- throw new Error(formatActionableError({
2256
- failure: `ai approve --phase ${phase} has no saved drafts to approve.`,
2257
- impact: 'There is no planner artifact that can pass the approval gate.',
2258
- fix: `Generate a ${phase} draft first.`,
2404
+ throw new Error(formatLocalizedActionableError({
2405
+ failure: translator.t('ai.approve.error.no_drafts.failure', { phase }),
2406
+ impact: translator.t('ai.approve.error.no_drafts.impact'),
2407
+ fix: translator.t('ai.approve.error.no_drafts.fix', { phase }),
2259
2408
  nextCommand: `npx create-quiver ai plan --phase ${phase}${phase === 'acceptance' ? ' --input <requirements.md>' : ''} --dry-run`,
2260
- }));
2409
+ }, options));
2261
2410
  }
2262
2411
 
2263
- const selected = await selectOption(`¿Qué ${phase} draft querés aprobar?`, approvalSelectionOptions(report), {
2412
+ const selected = await selectOption(translator.t('ai.approve.prompt.version', { phase }), approvalSelectionOptions(report, options), {
2264
2413
  env: options.env,
2265
2414
  error: options.error,
2266
2415
  input: options.input,
@@ -2279,27 +2428,28 @@ async function resolveApprovalVersion(repoRoot, phase, options = {}) {
2279
2428
 
2280
2429
  const candidate = selected.raw;
2281
2430
  if (!candidate?.approvable) {
2282
- throw new Error(formatActionableError({
2283
- failure: `${phase} draft ${selected.label} is not approvable.`,
2431
+ throw new Error(formatLocalizedActionableError({
2432
+ failure: translator.t('ai.approve.error.not_approvable.failure', { phase, label: selected.label }),
2284
2433
  impact: candidate?.review?.blocking
2285
- ? 'The current review gate blocks technical-plan approval.'
2286
- : 'Quiver only approves the current eligible draft version.',
2287
- fix: candidate?.reason || 'Inspect planner approvals before approving.',
2434
+ ? translator.t('ai.approve.error.not_approvable.impact_blocked')
2435
+ : translator.t('ai.approve.error.not_approvable.impact'),
2436
+ fix: candidate?.reason || translator.t('ai.approve.error.not_approvable.fix'),
2288
2437
  nextCommand: candidate?.next_command || `npx create-quiver ai approvals`,
2289
- }));
2438
+ }, options));
2290
2439
  }
2291
2440
 
2292
2441
  return selected.value;
2293
2442
  }
2294
2443
 
2295
2444
  async function runApprove(repoRoot, options = {}) {
2445
+ const translator = createTranslator(options.language);
2296
2446
  const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
2297
2447
  if (phase === 'spec') {
2298
- throw new Error(formatError(`ai approve does not support phase '${phase}'`));
2448
+ throw new Error(formatError(translator.t('ai.approve.error.unsupported_phase', { phase })));
2299
2449
  }
2300
2450
 
2301
2451
  if (options.input) {
2302
- throw new Error(formatError(`ai approve --phase ${phase} approves saved draft versions only. Use \`npx create-quiver ai revise --phase ${phase} --input ${options.input}\` to create a new draft first.`));
2452
+ throw new Error(formatError(translator.t('ai.approve.error.input_not_supported', { phase, input: options.input })));
2303
2453
  }
2304
2454
 
2305
2455
  const version = await resolveApprovalVersion(repoRoot, phase, options);
@@ -2307,12 +2457,16 @@ async function runApprove(repoRoot, options = {}) {
2307
2457
  if (phase === 'technical-plan') {
2308
2458
  const review = readPlanReview(repoRoot);
2309
2459
  if (review.status !== 'unapproved' && review.status !== 'reviewed') {
2310
- throw new Error(formatError(`ai approve --phase technical-plan requires a production review for the current draft; current review status is ${review.status}. Run \`npx create-quiver ai review-plan --dry-run\`, then \`npx create-quiver ai review-plan\`.`));
2460
+ throw new Error(formatError(translator.t('ai.approve.error.review_required', { status: review.status })));
2311
2461
  }
2312
2462
  if (reviewBlocksApproval(review)) {
2313
2463
  const result = review.meta.review_result;
2314
2464
  const requiredFixes = Array.isArray(result.required_fixes) ? result.required_fixes.length : 0;
2315
- throw new Error(formatError(`ai approve --phase technical-plan is blocked by plan review; approval recommendation is ${result.approval_recommendation}. Required fixes: ${requiredFixes}. Next command: ${result.next_command}`));
2465
+ throw new Error(formatError(translator.t('ai.approve.error.review_blocked', {
2466
+ recommendation: result.approval_recommendation,
2467
+ fixes: requiredFixes,
2468
+ command: result.next_command,
2469
+ })));
2316
2470
  }
2317
2471
  assertTechnicalPlanDraftHasSpecContract(repoRoot, version);
2318
2472
  }
@@ -2320,7 +2474,7 @@ async function runApprove(repoRoot, options = {}) {
2320
2474
  const inputText = '';
2321
2475
 
2322
2476
  if (options.dryRun) {
2323
- process.stdout.write(formatApprovalDryRunResult({ phase, input: options.input, version }));
2477
+ process.stdout.write(formatApprovalDryRunResult({ phase, input: options.input, version, language: options.language }));
2324
2478
  return {
2325
2479
  task: 'approve',
2326
2480
  phase,
@@ -2351,7 +2505,7 @@ async function runApprove(repoRoot, options = {}) {
2351
2505
  process.stdout.write(formatApprovalResult({
2352
2506
  ...result,
2353
2507
  sourceFile: options.input || `draft version ${version}`,
2354
- }, repoRoot));
2508
+ }, repoRoot, options));
2355
2509
 
2356
2510
  return {
2357
2511
  task: 'approve',
@@ -2363,8 +2517,8 @@ async function runApprove(repoRoot, options = {}) {
2363
2517
  };
2364
2518
  }
2365
2519
 
2366
- async function runApprovalStatus(repoRoot) {
2367
- const report = formatApprovalStatusReport(repoRoot);
2520
+ async function runApprovalStatus(repoRoot, options = {}) {
2521
+ const report = formatApprovalStatusReportWithOptions(repoRoot, options);
2368
2522
  process.stdout.write(report);
2369
2523
  return {
2370
2524
  task: 'approval-status',
@@ -2374,7 +2528,7 @@ async function runApprovalStatus(repoRoot) {
2374
2528
 
2375
2529
  function runLifecycleStatus(repoRoot, options = {}) {
2376
2530
  const run = resolveAiRun(repoRoot, options.runId || '');
2377
- const report = formatAiRunStatus(repoRoot, run);
2531
+ const report = formatAiRunStatus(repoRoot, run, options);
2378
2532
  process.stdout.write(report);
2379
2533
  return {
2380
2534
  task: 'status',
@@ -2385,7 +2539,7 @@ function runLifecycleStatus(repoRoot, options = {}) {
2385
2539
 
2386
2540
  function runLifecycleResume(repoRoot, options = {}) {
2387
2541
  const run = resolveAiRun(repoRoot, options.runId || '');
2388
- const report = formatAiRunResume(repoRoot, run);
2542
+ const report = formatAiRunResume(repoRoot, run, options);
2389
2543
  process.stdout.write(report);
2390
2544
  return {
2391
2545
  task: 'resume',
@@ -2398,7 +2552,7 @@ function runInspect(repoRoot, options = {}) {
2398
2552
  const report = collectLifecycleExport(repoRoot, {
2399
2553
  includeCompleted: options.includeCompleted === true,
2400
2554
  });
2401
- process.stdout.write(formatLifecycleInspect(report));
2555
+ process.stdout.write(formatLifecycleInspect(report, options));
2402
2556
  return {
2403
2557
  task: 'inspect',
2404
2558
  report,
@@ -2421,7 +2575,7 @@ function runExport(repoRoot, options = {}) {
2421
2575
  }
2422
2576
 
2423
2577
  if (format === 'markdown' || format === 'md') {
2424
- process.stdout.write(formatLifecycleExportMarkdown(report));
2578
+ process.stdout.write(formatLifecycleExportMarkdown(report, options));
2425
2579
  return {
2426
2580
  task: 'export',
2427
2581
  format: 'markdown',
@@ -2439,7 +2593,7 @@ function runSpecsList(repoRoot, options = {}) {
2439
2593
  if (options.json === true) {
2440
2594
  process.stdout.write(`${JSON.stringify({ specs: report.specs }, null, 2)}\n`);
2441
2595
  } else {
2442
- process.stdout.write(formatSpecsList(report));
2596
+ process.stdout.write(formatSpecsList(report, options));
2443
2597
  }
2444
2598
  return {
2445
2599
  task: 'specs',
@@ -2454,7 +2608,7 @@ function runSlicesList(repoRoot, options = {}) {
2454
2608
  if (options.json === true) {
2455
2609
  process.stdout.write(`${JSON.stringify({ slices: report.slices }, null, 2)}\n`);
2456
2610
  } else {
2457
- process.stdout.write(formatSlicesList(report));
2611
+ process.stdout.write(formatSlicesList(report, options));
2458
2612
  }
2459
2613
  return {
2460
2614
  task: 'slices',
@@ -2466,7 +2620,7 @@ function runTraceReport(repoRoot, options = {}) {
2466
2620
  const report = collectLifecycleExport(repoRoot, {
2467
2621
  includeCompleted: options.includeCompleted === true,
2468
2622
  });
2469
- process.stdout.write(formatTraceReport(report));
2623
+ process.stdout.write(formatTraceReport(report, options));
2470
2624
  return {
2471
2625
  task: 'trace',
2472
2626
  report,
@@ -2496,22 +2650,23 @@ function runActiveSlice(repoRoot, options = {}) {
2496
2650
  }
2497
2651
 
2498
2652
  function runLifecycleRun(repoRoot, options = {}) {
2653
+ const translator = createTranslator(options.language);
2499
2654
  const command = String(options.command || '').trim().toLowerCase();
2500
2655
  if (command !== 'create' && command !== 'close') {
2501
- throw new Error(formatError(`unsupported ai run subcommand: ${command}. Supported tasks: create, close`));
2656
+ throw new Error(formatError(translator.t('ai.run.error.unsupported_subcommand', { command })));
2502
2657
  }
2503
2658
  if (command === 'create' && !options.input) {
2504
- throw new Error(formatError('ai run create requires --input <requirements.md>'));
2659
+ throw new Error(formatError(translator.t('ai.run.error.create_requires_input')));
2505
2660
  }
2506
2661
  if (command === 'close') {
2507
2662
  const current = resolveAiRun(repoRoot, options.runId || '');
2508
2663
  if (!current) {
2509
- throw new Error(formatError('ai run close requires an active run or --run <id>'));
2664
+ throw new Error(formatError(translator.t('ai.run.error.close_requires_run')));
2510
2665
  }
2511
2666
  const run = updateAiRunPhase(repoRoot, current.run_id, 'closed', {
2512
2667
  command: 'ai run close',
2513
2668
  });
2514
- const report = `AI run closed\n${formatAiRunStatus(repoRoot, run)}`;
2669
+ const report = `${translator.t('ai.run.closed.title')}\n${formatAiRunStatus(repoRoot, run, options)}`;
2515
2670
  process.stdout.write(report);
2516
2671
  return {
2517
2672
  task: 'run',
@@ -2526,7 +2681,7 @@ function runLifecycleRun(repoRoot, options = {}) {
2526
2681
  runId: options.runId,
2527
2682
  specSlug: options.specSlug,
2528
2683
  });
2529
- const report = formatAiRunStatus(repoRoot, run);
2684
+ const report = formatAiRunStatus(repoRoot, run, options);
2530
2685
  process.stdout.write(report);
2531
2686
  return {
2532
2687
  task: 'run',
@@ -2543,7 +2698,7 @@ function isInteractiveAgentPromptAvailable(options = {}) {
2543
2698
  return stdinIsTTY && stdoutIsTTY && ci !== '1' && ci !== 'true';
2544
2699
  }
2545
2700
 
2546
- function providerInstallHint(repoRoot, providerId, options = {}) {
2701
+ function providerInstallStatus(repoRoot, providerId, options = {}) {
2547
2702
  const probe = options.preflightProvider || preflightProvider;
2548
2703
  try {
2549
2704
  probe(providerId, {
@@ -2554,19 +2709,23 @@ function providerInstallHint(repoRoot, providerId, options = {}) {
2554
2709
  return 'installed';
2555
2710
  } catch (error) {
2556
2711
  if (error && error.code === 'MISSING_PROVIDER_CLI') {
2557
- return 'not installed';
2712
+ return 'not_installed';
2558
2713
  }
2559
- return 'not verified';
2714
+ return 'not_verified';
2560
2715
  }
2561
2716
  }
2562
2717
 
2563
2718
  function buildAgentProviderChoices(repoRoot, options = {}) {
2719
+ const translator = createTranslator(options.language);
2564
2720
  return listCatalogProviders().map((provider) => {
2565
- const status = providerInstallHint(repoRoot, provider.id, options);
2721
+ const status = providerInstallStatus(repoRoot, provider.id, options);
2566
2722
  return {
2567
2723
  label: provider.displayName,
2568
2724
  value: provider.id,
2569
- hint: `${provider.modelCount} models, ${status}`,
2725
+ hint: translator.t('ai.agent.choice.provider_hint', {
2726
+ count: provider.modelCount,
2727
+ status: translator.t(`ai.agent.install_status.${status}`),
2728
+ }),
2570
2729
  raw: {
2571
2730
  ...provider,
2572
2731
  installStatus: status,
@@ -2575,12 +2734,15 @@ function buildAgentProviderChoices(repoRoot, options = {}) {
2575
2734
  });
2576
2735
  }
2577
2736
 
2578
- function buildAgentModelChoices(provider, role) {
2737
+ function buildAgentModelChoices(provider, role, options = {}) {
2738
+ const translator = createTranslator(options.language);
2579
2739
  return getKnownModelsForProvider(provider, { role }).map((model) => {
2580
- const recommended = model.recommendedFor.includes(role) ? 'recommended' : 'available';
2740
+ const recommended = model.recommendedFor.includes(role)
2741
+ ? translator.t('ai.agent.choice.model_recommended')
2742
+ : translator.t('ai.agent.choice.model_available');
2581
2743
  const tier = [model.costTier, model.qualityTier, model.stability].filter(Boolean).join('/');
2582
2744
  return {
2583
- label: model.displayName,
2745
+ label: model.id === 'custom' ? translator.t('ai.agent.choice.model_custom') : model.displayName,
2584
2746
  value: model.id,
2585
2747
  hint: [recommended, tier].filter(Boolean).join(', '),
2586
2748
  default: model.recommendedFor.includes(role),
@@ -2590,6 +2752,7 @@ function buildAgentModelChoices(provider, role) {
2590
2752
  }
2591
2753
 
2592
2754
  async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2755
+ const translator = createTranslator(options.language);
2593
2756
  const role = normalizeAgentProfileRole(options.role);
2594
2757
  const hasProvider = Boolean(String(options.provider || '').trim());
2595
2758
  const hasModel = Boolean(String(options.model || '').trim());
@@ -2603,12 +2766,12 @@ async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2603
2766
  const canPrompt = isInteractiveAgentPromptAvailable(options);
2604
2767
  const shouldPrompt = options.interactive === true || canPrompt;
2605
2768
  if (!shouldPrompt || !canPrompt) {
2606
- throw new Error(formatActionableError({
2607
- failure: `ai agent set ${role} requires --provider and --model when prompts are not available.`,
2608
- impact: 'Quiver cannot safely guess the provider or technical model id for an agent profile.',
2609
- fix: 'Pass both flags explicitly, or rerun from an interactive terminal with --interactive.',
2769
+ throw new Error(formatLocalizedActionableError({
2770
+ failure: translator.t('ai.agent.error.no_prompt.failure', { role }),
2771
+ impact: translator.t('ai.agent.error.no_prompt.impact'),
2772
+ fix: translator.t('ai.agent.error.no_prompt.fix'),
2610
2773
  nextCommand: `npx create-quiver ai agent set ${role} --provider codex --model gpt-5.5`,
2611
- }));
2774
+ }, options));
2612
2775
  }
2613
2776
 
2614
2777
  const promptOptions = {
@@ -2633,16 +2796,16 @@ async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2633
2796
  const ux = createCommandUx(promptOptions);
2634
2797
  ux.summary(existingProfiles.map((profile) => ({
2635
2798
  label: profile.id,
2636
- value: `${profile.provider} ${profile.model || '(no model)'}${profile.default ? ' default' : ''}`,
2799
+ value: `${profile.provider} ${profile.model || translator.t('ai.agent.value.no_model')}${profile.default ? ` ${translator.t('ai.agent.value.default')}` : ''}`,
2637
2800
  })), {
2638
- title: `Existing ${role} profiles`,
2801
+ title: translator.t('ai.agent.prompt.existing_title', { role }),
2639
2802
  });
2640
2803
 
2641
- const action = await selectOption(`Ya existe al menos un perfil ${role}. ¿Qué querés hacer?`, [
2642
- { label: 'Actualizar perfil actual', value: 'update-current', hint: 'Reutiliza el perfil default', default: true },
2643
- { label: 'Crear perfil nuevo', value: 'create-new', hint: 'Permite guardar otro modelo o provider' },
2644
- { label: 'Cambiar default', value: 'change-default', hint: 'Marca un perfil existente como default' },
2645
- { label: 'Cancelar', value: 'cancel', hint: 'No escribe archivos' },
2804
+ const action = await selectOption(translator.t('ai.agent.prompt.existing_action', { role }), [
2805
+ { label: translator.t('ai.agent.prompt.action.update_current.label'), value: 'update-current', hint: translator.t('ai.agent.prompt.action.update_current.hint'), default: true },
2806
+ { label: translator.t('ai.agent.prompt.action.create_new.label'), value: 'create-new', hint: translator.t('ai.agent.prompt.action.create_new.hint') },
2807
+ { label: translator.t('ai.agent.prompt.action.change_default.label'), value: 'change-default', hint: translator.t('ai.agent.prompt.action.change_default.hint') },
2808
+ { label: translator.t('ai.agent.prompt.action.cancel.label'), value: 'cancel', hint: translator.t('ai.agent.prompt.action.cancel.hint') },
2646
2809
  ], {
2647
2810
  ...promptOptions,
2648
2811
  name: 'agent profile action',
@@ -2650,14 +2813,14 @@ async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2650
2813
  });
2651
2814
 
2652
2815
  if (action.value === 'cancel') {
2653
- throw new Error(formatError('ai agent set canceled. No files were written.'));
2816
+ throw new Error(formatError(translator.t('ai.agent.error.canceled')));
2654
2817
  }
2655
2818
 
2656
2819
  if (action.value === 'change-default') {
2657
- const profile = await selectOption(`Elegí el perfil ${role} default`, existingProfiles.map((item) => ({
2820
+ const profile = await selectOption(translator.t('ai.agent.prompt.default_profile', { role }), existingProfiles.map((item) => ({
2658
2821
  label: resolveAgentProfileDisplayName(item),
2659
2822
  value: item.id,
2660
- hint: `${item.provider} ${item.model || '(no model)'}`,
2823
+ hint: `${item.provider} ${item.model || translator.t('ai.agent.value.no_model')}`,
2661
2824
  default: item.default === true,
2662
2825
  raw: item,
2663
2826
  })), {
@@ -2688,7 +2851,7 @@ async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2688
2851
  }
2689
2852
 
2690
2853
  if (action.value === 'create-new') {
2691
- const id = await promptText(`ID para el nuevo perfil ${role}`, {
2854
+ const id = await promptText(translator.t('ai.agent.prompt.new_id', { role }), {
2692
2855
  ...promptOptions,
2693
2856
  name: 'agent profile id',
2694
2857
  flag: '--id',
@@ -2700,7 +2863,7 @@ async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2700
2863
  }
2701
2864
 
2702
2865
  if (!next.provider) {
2703
- const selectedProvider = await selectOption(`Elegí un provider para ${role}`, buildAgentProviderChoices(repoRoot, options), {
2866
+ const selectedProvider = await selectOption(translator.t('ai.agent.prompt.provider', { role }), buildAgentProviderChoices(repoRoot, options), {
2704
2867
  ...promptOptions,
2705
2868
  name: 'agent provider',
2706
2869
  flag: '--provider',
@@ -2709,19 +2872,19 @@ async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2709
2872
  }
2710
2873
 
2711
2874
  if (!next.model) {
2712
- const selectedModel = await selectOption(`Elegí un modelo para ${role}`, buildAgentModelChoices(next.provider, role), {
2875
+ const selectedModel = await selectOption(translator.t('ai.agent.prompt.model', { role }), buildAgentModelChoices(next.provider, role, options), {
2713
2876
  ...promptOptions,
2714
2877
  name: 'agent model',
2715
2878
  flag: '--model',
2716
2879
  });
2717
2880
  if (selectedModel.value === 'custom') {
2718
- const model = await promptText(`Modelo custom para ${next.provider}`, {
2881
+ const model = await promptText(translator.t('ai.agent.prompt.custom_model', { provider: next.provider }), {
2719
2882
  ...promptOptions,
2720
2883
  name: 'agent model',
2721
2884
  flag: '--model',
2722
2885
  placeholder: `${next.provider}-model-id`,
2723
2886
  });
2724
- const displayName = await promptText('Nombre visible del modelo custom', {
2887
+ const displayName = await promptText(translator.t('ai.agent.prompt.custom_model_display'), {
2725
2888
  ...promptOptions,
2726
2889
  name: 'agent model display name',
2727
2890
  flag: '--display-name',
@@ -2742,48 +2905,53 @@ async function resolveInteractiveAgentSetOptions(repoRoot, options = {}) {
2742
2905
  };
2743
2906
  }
2744
2907
 
2745
- function formatAgentProfile(profile) {
2908
+ function formatAgentProfile(profile, options = {}) {
2909
+ const translator = createTranslator(options.language);
2746
2910
  const lines = [
2747
- `ID: ${profile.id || 'default'}`,
2748
- `Role: ${profile.role}`,
2749
- `Provider: ${profile.provider}`,
2750
- `Model: ${profile.model || '(not set)'}`,
2751
- `Label: ${profile.label || '(not set)'}`,
2752
- `Display name: ${resolveAgentProfileDisplayName(profile) || '(not set)'}`,
2753
- `Default: ${profile.default === true ? 'yes' : 'no'}`,
2754
- `Context: ${profile.context || '(not set)'}`,
2755
- `Updated: ${profile.updated_at}`,
2911
+ `${translator.t('ai.agent.field.id')}: ${profile.id || translator.t('ai.agent.value.default')}`,
2912
+ `${translator.t('ai.agent.field.role')}: ${profile.role}`,
2913
+ `${translator.t('ai.agent.field.provider')}: ${profile.provider}`,
2914
+ `${translator.t('ai.agent.field.model')}: ${profile.model || translator.t('ai.agent.value.not_set')}`,
2915
+ `${translator.t('ai.agent.field.label')}: ${profile.label || translator.t('ai.agent.value.not_set')}`,
2916
+ `${translator.t('ai.agent.field.display_name')}: ${resolveAgentProfileDisplayName(profile) || translator.t('ai.agent.value.not_set')}`,
2917
+ `${translator.t('ai.agent.field.default')}: ${profile.default === true ? translator.t('ai.agent.value.yes') : translator.t('ai.agent.value.no')}`,
2918
+ `${translator.t('ai.agent.field.context')}: ${profile.context || translator.t('ai.agent.value.not_set')}`,
2919
+ `${translator.t('ai.agent.field.updated')}: ${profile.updated_at}`,
2756
2920
  ];
2757
2921
  return `${lines.join('\n')}\n`;
2758
2922
  }
2759
2923
 
2760
- function formatAgentProfileList(profiles) {
2761
- const lines = ['AI agent profiles'];
2924
+ function formatAgentProfileList(profiles, options = {}) {
2925
+ const translator = createTranslator(options.language);
2926
+ const lines = [translator.t('ai.agent.list.title')];
2762
2927
  for (const item of profiles) {
2763
2928
  if (!item.configured) {
2764
- lines.push(`- ${item.role}: not configured`);
2929
+ lines.push(`- ${item.role}: ${translator.t('ai.agent.list.not_configured')}`);
2765
2930
  continue;
2766
2931
  }
2767
2932
  const model = item.profile.model ? ` model=${item.profile.model}` : '';
2768
2933
  const label = item.profile.label ? ` label=${item.profile.label}` : '';
2769
2934
  const displayName = resolveAgentProfileDisplayName(item.profile);
2770
- const alternatives = item.profiles.length > 1 ? ` options=${item.profiles.length}` : '';
2771
- lines.push(`- ${item.role}: provider=${item.profile.provider}${model}${label} displayName=${displayName}${alternatives}`);
2935
+ const alternatives = item.profiles.length > 1 ? ` ${translator.t('ai.agent.list.options')}=${item.profiles.length}` : '';
2936
+ lines.push(`- ${item.role}: provider=${item.profile.provider}${model}${label} ${translator.t('ai.agent.list.display_name')}=${displayName}${alternatives}`);
2772
2937
  }
2773
2938
  return `${lines.join('\n')}\n`;
2774
2939
  }
2775
2940
 
2776
- function formatAgentProfileDryRun(repoRoot, result) {
2941
+ function formatAgentProfileDryRun(repoRoot, result, options = {}) {
2942
+ const translator = createTranslator(options.language);
2777
2943
  const relativePath = path.relative(repoRoot, result.filePath).split(path.sep).join('/');
2778
- const verb = result.action === 'update' ? 'update' : 'create';
2944
+ const verb = result.action === 'update'
2945
+ ? translator.t('ai.agent.dry_run.verb_update')
2946
+ : translator.t('ai.agent.dry_run.verb_create');
2779
2947
  return [
2780
- 'AI agent profile dry-run',
2781
- '- Writes: none',
2782
- `- Would ${verb}: ${relativePath}`,
2948
+ translator.t('ai.agent.dry_run.title'),
2949
+ `- ${translator.t('ai.agent.dry_run.writes')}: ${translator.t('ai.agent.value.none')}`,
2950
+ `- ${translator.t('ai.agent.dry_run.would', { verb, path: relativePath })}`,
2783
2951
  '',
2784
- formatAgentProfile(result.profile).trimEnd(),
2952
+ formatAgentProfile(result.profile, options).trimEnd(),
2785
2953
  '',
2786
- 'No secrets or provider credentials are stored in agent profiles.',
2954
+ translator.t('ai.agent.dry_run.no_secrets'),
2787
2955
  '',
2788
2956
  ].join('\n');
2789
2957
  }
@@ -2794,58 +2962,60 @@ function agentDoctorSymbol(status) {
2794
2962
  return 'OK';
2795
2963
  }
2796
2964
 
2797
- function formatAgentDoctorReport(report) {
2965
+ function formatAgentDoctorReport(report, options = {}) {
2966
+ const translator = createTranslator(options.language);
2798
2967
  const lines = [
2799
- 'Quiver Agent Doctor',
2968
+ translator.t('ai.agent.doctor.title'),
2800
2969
  '',
2801
- 'Checks',
2970
+ translator.t('ai.agent.doctor.checks'),
2802
2971
  ];
2803
2972
 
2804
2973
  if (report.checks.length === 0) {
2805
- lines.push(' ! No agent profiles configured');
2974
+ lines.push(` ! ${translator.t('ai.agent.doctor.no_profiles')}`);
2806
2975
  }
2807
2976
 
2808
2977
  for (const check of report.checks) {
2809
2978
  const target = `${check.role}/${check.id}`;
2810
- const model = check.model || '(no model)';
2811
- const provider = check.provider || '(no provider)';
2812
- const defaultText = check.default ? ' default' : '';
2979
+ const model = check.model || translator.t('ai.agent.value.no_model');
2980
+ const provider = check.provider || translator.t('ai.agent.value.no_provider');
2981
+ const defaultText = check.default ? ` ${translator.t('ai.agent.value.default')}` : '';
2813
2982
  lines.push(` ${agentDoctorSymbol(check.status)} ${target}: provider=${provider} model=${model}${defaultText}`);
2814
2983
  for (const finding of check.findings.filter((item) => item.severity !== 'info')) {
2815
2984
  lines.push(` - ${finding.severity}: ${finding.message}`);
2816
2985
  }
2817
2986
  }
2818
2987
 
2819
- lines.push('', 'Suggested fixes');
2988
+ lines.push('', translator.t('ai.agent.doctor.suggested_fixes'));
2820
2989
  if (report.suggestedFixes.length === 0) {
2821
- lines.push(' OK No fixes suggested.');
2990
+ lines.push(` OK ${translator.t('ai.agent.doctor.no_fixes')}`);
2822
2991
  } else {
2823
2992
  for (const fix of report.suggestedFixes) {
2824
2993
  lines.push(` - ${fix}`);
2825
2994
  }
2826
2995
  }
2827
2996
  lines.push('');
2828
- lines.push(`Summary: profiles=${report.summary.profiles} errors=${report.summary.errors} warnings=${report.summary.warnings} info=${report.summary.info}`);
2997
+ lines.push(`${translator.t('ai.agent.doctor.summary')}: profiles=${report.summary.profiles} errors=${report.summary.errors} warnings=${report.summary.warnings} info=${report.summary.info}`);
2829
2998
  lines.push('');
2830
2999
  return `${lines.join('\n')}\n`;
2831
3000
  }
2832
3001
 
2833
- function formatAgentRepairPlan(plan) {
3002
+ function formatAgentRepairPlan(plan, options = {}) {
3003
+ const translator = createTranslator(options.language);
2834
3004
  const lines = [
2835
- 'AI agent profile repair dry-run',
2836
- '- Writes: none',
3005
+ translator.t('ai.agent.repair.title'),
3006
+ `- ${translator.t('ai.agent.dry_run.writes')}: ${translator.t('ai.agent.value.none')}`,
2837
3007
  '',
2838
- 'Proposed changes',
3008
+ translator.t('ai.agent.repair.proposed_changes'),
2839
3009
  ];
2840
3010
 
2841
3011
  if (plan.changes.length === 0) {
2842
- lines.push(' OK No safe repairs found.');
3012
+ lines.push(` OK ${translator.t('ai.agent.repair.no_repairs')}`);
2843
3013
  }
2844
3014
 
2845
3015
  for (const change of plan.changes) {
2846
3016
  lines.push(` - ${change.role}/${change.profileId}: ${change.reason}`);
2847
- lines.push(` Before: model=${change.before.model || '(not set)'} displayName=${change.before.displayName || '(not set)'}`);
2848
- lines.push(` After: model=${change.after.model || '(not set)'} displayName=${change.after.displayName || '(not set)'}`);
3017
+ lines.push(` ${translator.t('ai.agent.repair.before')}: model=${change.before.model || translator.t('ai.agent.value.not_set')} displayName=${change.before.displayName || translator.t('ai.agent.value.not_set')}`);
3018
+ lines.push(` ${translator.t('ai.agent.repair.after')}: model=${change.after.model || translator.t('ai.agent.value.not_set')} displayName=${change.after.displayName || translator.t('ai.agent.value.not_set')}`);
2849
3019
  }
2850
3020
 
2851
3021
  lines.push('');
@@ -2853,13 +3023,17 @@ function formatAgentRepairPlan(plan) {
2853
3023
  }
2854
3024
 
2855
3025
  function buildModelsListReport(options = {}) {
3026
+ const translator = createTranslator(options.language);
2856
3027
  const providerFilter = String(options.provider || '').trim().toLowerCase();
2857
3028
  const providers = providerFilter
2858
3029
  ? [listCatalogProviders().find((provider) => provider.id === providerFilter)].filter(Boolean)
2859
3030
  : listCatalogProviders();
2860
3031
 
2861
3032
  if (providerFilter && providers.length === 0) {
2862
- throw new Error(formatError(`unsupported provider filter '${options.provider}'. Supported providers: ${listCatalogProviders().map((provider) => provider.id).join(', ')}.`));
3033
+ throw new Error(formatError(translator.t('ai.models.error.unsupported_provider_filter', {
3034
+ provider: options.provider,
3035
+ providers: listCatalogProviders().map((provider) => provider.id).join(', '),
3036
+ })));
2863
3037
  }
2864
3038
 
2865
3039
  return {
@@ -2882,21 +3056,22 @@ function buildModelsListReport(options = {}) {
2882
3056
  };
2883
3057
  }
2884
3058
 
2885
- function formatModelsListReport(report) {
3059
+ function formatModelsListReport(report, options = {}) {
3060
+ const translator = createTranslator(options.language);
2886
3061
  const lines = [
2887
- 'AI models known by Quiver',
2888
- `Catalog version: ${report.catalogVersion}`,
2889
- `Last updated: ${report.lastUpdated}`,
2890
- 'Note: Models are known by Quiver; provider account access is not guaranteed.',
3062
+ translator.t('ai.models.title'),
3063
+ `${translator.t('ai.models.catalog_version')}: ${report.catalogVersion}`,
3064
+ `${translator.t('ai.models.last_updated')}: ${report.lastUpdated}`,
3065
+ `${translator.t('ai.models.note')}`,
2891
3066
  '',
2892
3067
  ];
2893
3068
 
2894
3069
  for (const provider of report.providers) {
2895
3070
  lines.push(`${provider.displayName} (${provider.id})`);
2896
3071
  for (const model of provider.models) {
2897
- const roles = model.recommendedFor.length > 0 ? model.recommendedFor.join(', ') : 'custom/manual';
3072
+ const roles = model.recommendedFor.length > 0 ? model.recommendedFor.join(', ') : translator.t('ai.models.roles.custom_manual');
2898
3073
  lines.push(` - ${model.id} (${model.displayName})`);
2899
- lines.push(` roles: ${roles}`);
3074
+ lines.push(` ${translator.t('ai.models.roles')}: ${roles}`);
2900
3075
  lines.push(` cost=${model.costTier} quality=${model.qualityTier} stability=${model.stability}`);
2901
3076
  }
2902
3077
  lines.push('');
@@ -2907,12 +3082,13 @@ function formatModelsListReport(report) {
2907
3082
 
2908
3083
  function runModelsList(options = {}) {
2909
3084
  const report = buildModelsListReport({
3085
+ language: options.language,
2910
3086
  provider: options.provider,
2911
3087
  });
2912
3088
  if (options.json) {
2913
3089
  process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
2914
3090
  } else {
2915
- process.stdout.write(formatModelsListReport(report));
3091
+ process.stdout.write(formatModelsListReport(report, options));
2916
3092
  }
2917
3093
  return {
2918
3094
  task: 'models',
@@ -2922,11 +3098,12 @@ function runModelsList(options = {}) {
2922
3098
  }
2923
3099
 
2924
3100
  async function runAgent(repoRoot, options = {}) {
3101
+ const translator = createTranslator(options.language);
2925
3102
  const command = String(options.command || '').trim().toLowerCase();
2926
3103
 
2927
3104
  if (command === 'set') {
2928
3105
  if (!options.role) {
2929
- throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent set <planner|executor|reviewer|doctor> --provider <provider> --model <model>'));
3106
+ throw new Error(formatError(translator.t('ai.agent.error.missing_set_role')));
2930
3107
  }
2931
3108
  const profileOptions = await resolveInteractiveAgentSetOptions(repoRoot, options);
2932
3109
  if (options.dryRun) {
@@ -2939,7 +3116,7 @@ async function runAgent(repoRoot, options = {}) {
2939
3116
  model: profileOptions.model,
2940
3117
  provider: profileOptions.provider,
2941
3118
  });
2942
- process.stdout.write(formatAgentProfileDryRun(repoRoot, preview));
3119
+ process.stdout.write(formatAgentProfileDryRun(repoRoot, preview, options));
2943
3120
  return {
2944
3121
  task: 'agent',
2945
3122
  command,
@@ -2960,13 +3137,13 @@ async function runAgent(repoRoot, options = {}) {
2960
3137
  });
2961
3138
  const ux = createCommandUx(profileOptions);
2962
3139
  ux.summary([
2963
- { label: 'Role', value: preview.profile.role },
2964
- { label: 'Provider', value: preview.profile.provider },
2965
- { label: 'Model', value: preview.profile.model || '(not set)' },
2966
- { label: 'Display name', value: resolveAgentProfileDisplayName(preview.profile) || '(not set)' },
2967
- { label: 'Default', value: preview.profile.default === true ? 'yes' : 'no' },
3140
+ { label: translator.t('ai.agent.field.role'), value: preview.profile.role },
3141
+ { label: translator.t('ai.agent.field.provider'), value: preview.profile.provider },
3142
+ { label: translator.t('ai.agent.field.model'), value: preview.profile.model || translator.t('ai.agent.value.not_set') },
3143
+ { label: translator.t('ai.agent.field.display_name'), value: resolveAgentProfileDisplayName(preview.profile) || translator.t('ai.agent.value.not_set') },
3144
+ { label: translator.t('ai.agent.field.default'), value: preview.profile.default === true ? translator.t('ai.agent.value.yes') : translator.t('ai.agent.value.no') },
2968
3145
  ], {
2969
- title: 'Profile to save',
3146
+ title: translator.t('ai.agent.profile_to_save.title'),
2970
3147
  });
2971
3148
  }
2972
3149
  const result = setAgentProfile(repoRoot, profileOptions.role, {
@@ -2978,9 +3155,9 @@ async function runAgent(repoRoot, options = {}) {
2978
3155
  model: profileOptions.model,
2979
3156
  provider: profileOptions.provider,
2980
3157
  });
2981
- process.stdout.write('AI agent profile saved\n');
2982
- process.stdout.write(formatAgentProfile(result.profile));
2983
- process.stdout.write(`State: ${path.relative(repoRoot, result.filePath).split(path.sep).join('/')}\n`);
3158
+ process.stdout.write(`${translator.t('ai.agent.saved.title')}\n`);
3159
+ process.stdout.write(formatAgentProfile(result.profile, options));
3160
+ process.stdout.write(`${translator.t('ai.agent.field.state')}: ${path.relative(repoRoot, result.filePath).split(path.sep).join('/')}\n`);
2984
3161
  return {
2985
3162
  task: 'agent',
2986
3163
  command,
@@ -2991,22 +3168,22 @@ async function runAgent(repoRoot, options = {}) {
2991
3168
 
2992
3169
  if (command === 'show') {
2993
3170
  if (!options.role) {
2994
- throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent show <planner|executor|reviewer|doctor>'));
3171
+ throw new Error(formatError(translator.t('ai.agent.error.missing_show_role')));
2995
3172
  }
2996
3173
  const profile = options.id
2997
3174
  ? getAgentProfileById(repoRoot, options.role, options.id)
2998
3175
  : getAgentProfile(repoRoot, options.role);
2999
3176
  if (!profile) {
3000
- throw new Error(formatActionableError({
3177
+ throw new Error(formatLocalizedActionableError({
3001
3178
  failure: options.id
3002
- ? `agent profile '${options.role}/${options.id}' is not configured.`
3003
- : `agent profile '${options.role}' is not configured.`,
3004
- impact: 'Quiver will fall back to default provider behavior and may use the wrong model/cost profile.',
3005
- fix: `Configure the ${options.role} profile with a supported provider and technical model id.`,
3179
+ ? translator.t('ai.agent.error.missing_profile_id.failure', { role: options.role, id: options.id })
3180
+ : translator.t('ai.agent.error.missing_profile.failure', { role: options.role }),
3181
+ impact: translator.t('ai.agent.error.missing_profile.impact'),
3182
+ fix: translator.t('ai.agent.error.missing_profile.fix', { role: options.role }),
3006
3183
  nextCommand: `npx create-quiver ai agent set ${options.role} --provider <provider> --model <model-id>`,
3007
- }));
3184
+ }, options));
3008
3185
  }
3009
- process.stdout.write(formatAgentProfile(profile));
3186
+ process.stdout.write(formatAgentProfile(profile, options));
3010
3187
  return {
3011
3188
  task: 'agent',
3012
3189
  command,
@@ -3016,8 +3193,8 @@ async function runAgent(repoRoot, options = {}) {
3016
3193
 
3017
3194
  if (command === 'list' || command === 'ls' || command === '') {
3018
3195
  const profiles = listAgentProfiles(repoRoot);
3019
- process.stdout.write(formatAgentProfileList(profiles));
3020
- process.stdout.write(`State: ${path.relative(repoRoot, agentProfilesPath(repoRoot)).split(path.sep).join('/')}\n`);
3196
+ process.stdout.write(formatAgentProfileList(profiles, options));
3197
+ process.stdout.write(`${translator.t('ai.agent.field.state')}: ${path.relative(repoRoot, agentProfilesPath(repoRoot)).split(path.sep).join('/')}\n`);
3021
3198
  return {
3022
3199
  task: 'agent',
3023
3200
  command: 'list',
@@ -3030,7 +3207,7 @@ async function runAgent(repoRoot, options = {}) {
3030
3207
  if (options.json) {
3031
3208
  process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
3032
3209
  } else {
3033
- process.stdout.write(formatAgentDoctorReport(report));
3210
+ process.stdout.write(formatAgentDoctorReport(report, options));
3034
3211
  }
3035
3212
  if (report.summary.errors > 0) {
3036
3213
  process.exitCode = 1;
@@ -3044,7 +3221,7 @@ async function runAgent(repoRoot, options = {}) {
3044
3221
 
3045
3222
  if (command === 'repair') {
3046
3223
  if (options.dryRun !== true) {
3047
- throw new Error(formatError('ai agent repair only supports --dry-run for now. No files were written.'));
3224
+ throw new Error(formatError(translator.t('ai.agent.error.repair_requires_dry_run')));
3048
3225
  }
3049
3226
  const plan = buildAgentProfileRepairPlan(repoRoot, {
3050
3227
  includeState: options.json === true,
@@ -3052,7 +3229,7 @@ async function runAgent(repoRoot, options = {}) {
3052
3229
  if (options.json) {
3053
3230
  process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`);
3054
3231
  } else {
3055
- process.stdout.write(formatAgentRepairPlan(plan));
3232
+ process.stdout.write(formatAgentRepairPlan(plan, options));
3056
3233
  }
3057
3234
  return {
3058
3235
  task: 'agent',
@@ -3062,7 +3239,7 @@ async function runAgent(repoRoot, options = {}) {
3062
3239
  };
3063
3240
  }
3064
3241
 
3065
- throw new Error(formatError(`unsupported ai agent subcommand: ${command}. Supported tasks: set, list, show, doctor, repair`));
3242
+ throw new Error(formatError(translator.t('ai.agent.error.unsupported_subcommand', { command })));
3066
3243
  }
3067
3244
 
3068
3245
  async function runGitHubTask(repoRoot, options = {}, mode = 'pr') {
@@ -3086,7 +3263,7 @@ async function runGitHubTask(repoRoot, options = {}, mode = 'pr') {
3086
3263
  throw annotateGitHubError(error, mode);
3087
3264
  }
3088
3265
 
3089
- process.stdout.write(formatPreflightReport(report, { mode, dryRun }));
3266
+ process.stdout.write(formatPreflightReport(report, { mode, dryRun, language: options.language }));
3090
3267
 
3091
3268
  return {
3092
3269
  task: mode,
@@ -3098,10 +3275,11 @@ async function runGitHubTask(repoRoot, options = {}, mode = 'pr') {
3098
3275
  async function runPr(repoRoot, options = {}) {
3099
3276
  const dryRun = options.dryRun === true;
3100
3277
  const create = options.create === true;
3278
+ const translator = createTranslator(options.language);
3101
3279
  const ux = createCommandUx(options);
3102
3280
  const showProgress = create && !dryRun && shouldShowHumanProgress(ux, options);
3103
3281
  if (showProgress) {
3104
- ux.heading('Creando PR con gh');
3282
+ ux.heading(translator.t('ai.github.progress.heading'));
3105
3283
  }
3106
3284
  let preflight;
3107
3285
 
@@ -3109,9 +3287,9 @@ async function runPr(repoRoot, options = {}) {
3109
3287
  preflight = await runProviderWithProgress({
3110
3288
  ux,
3111
3289
  enabled: showProgress,
3112
- message: 'Ejecutando preflight de GitHub...',
3113
- successMessage: 'Preflight de GitHub listo',
3114
- failureMessage: 'Falló preflight de GitHub',
3290
+ message: translator.t('ai.github.progress.preflight.running'),
3291
+ successMessage: translator.t('ai.github.progress.preflight.done'),
3292
+ failureMessage: translator.t('ai.github.progress.preflight.failed'),
3115
3293
  run: () => (options.preflightFn || preflightGitHubPr)(repoRoot, {
3116
3294
  remote: options.remote,
3117
3295
  sshHostAlias: options.sshHostAlias,
@@ -3139,7 +3317,7 @@ async function runPr(repoRoot, options = {}) {
3139
3317
  title: options.title,
3140
3318
  });
3141
3319
  if (showProgress) {
3142
- ux.check('Cuerpo del PR preparado');
3320
+ ux.check(translator.t('ai.github.progress.body_ready'));
3143
3321
  }
3144
3322
  } catch (error) {
3145
3323
  throw annotateGitHubError(error, 'pr');
@@ -3171,7 +3349,7 @@ async function runPr(repoRoot, options = {}) {
3171
3349
  }
3172
3350
 
3173
3351
  if (dryRun || !create) {
3174
- process.stdout.write(formatPrCreateReport({ preflight, plan }, { dryRun, create }));
3352
+ process.stdout.write(formatPrCreateReport({ preflight, plan }, { dryRun, create, language: options.language }));
3175
3353
  return {
3176
3354
  task: 'pr',
3177
3355
  dryRun,
@@ -3188,9 +3366,9 @@ async function runPr(repoRoot, options = {}) {
3188
3366
  result = await runProviderWithProgress({
3189
3367
  ux,
3190
3368
  enabled: showProgress,
3191
- message: 'Creando PR en GitHub...',
3192
- successMessage: 'PR creado',
3193
- failureMessage: 'Falló creación del PR',
3369
+ message: translator.t('ai.github.progress.create.running'),
3370
+ successMessage: translator.t('ai.github.progress.create.done'),
3371
+ failureMessage: translator.t('ai.github.progress.create.failed'),
3194
3372
  run: () => runGhPrCreate(plan, {
3195
3373
  ghCreateRunner: options.ghCreateRunner,
3196
3374
  }),
@@ -3199,7 +3377,7 @@ async function runPr(repoRoot, options = {}) {
3199
3377
  throw annotateGitHubError(error, 'pr');
3200
3378
  }
3201
3379
 
3202
- process.stdout.write(formatPrCreateReport({ preflight, plan, result }, { dryRun: false, create: true }));
3380
+ process.stdout.write(formatPrCreateReport({ preflight, plan, result }, { dryRun: false, create: true, language: options.language }));
3203
3381
  return {
3204
3382
  task: 'pr',
3205
3383
  dryRun: false,