reprompt-cli 2.0.2__tar.gz → 2.2.0__tar.gz

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 (315) hide show
  1. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/CHANGELOG.md +23 -0
  2. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/PKG-INFO +4 -2
  3. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/README.md +3 -1
  4. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/pyproject.toml +1 -1
  5. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/cli.py +204 -2
  6. reprompt_cli-2.2.0/src/reprompt/core/build.py +246 -0
  7. reprompt_cli-2.2.0/src/reprompt/core/check.py +115 -0
  8. reprompt_cli-2.2.0/src/reprompt/core/explain.py +208 -0
  9. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/lint.py +170 -4
  10. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/scorer.py +21 -10
  11. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/suggestions.py +3 -0
  12. reprompt_cli-2.2.0/src/reprompt/output/build_terminal.py +52 -0
  13. reprompt_cli-2.2.0/src/reprompt/output/check_terminal.py +105 -0
  14. reprompt_cli-2.2.0/src/reprompt/output/explain_terminal.py +58 -0
  15. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/rewrite_terminal.py +47 -0
  16. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/terminal.py +1 -3
  17. reprompt_cli-2.2.0/tests/test_build.py +270 -0
  18. reprompt_cli-2.2.0/tests/test_build_cli.py +126 -0
  19. reprompt_cli-2.2.0/tests/test_build_output.py +59 -0
  20. reprompt_cli-2.2.0/tests/test_check.py +101 -0
  21. reprompt_cli-2.2.0/tests/test_check_cli.py +80 -0
  22. reprompt_cli-2.2.0/tests/test_explain.py +100 -0
  23. reprompt_cli-2.2.0/tests/test_explain_cli.py +56 -0
  24. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_lint.py +176 -0
  25. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/uv.lock +1 -1
  26. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.editorconfig +0 -0
  27. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  28. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  29. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  30. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  31. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  32. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/dependabot.yml +0 -0
  33. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/workflows/ci.yml +0 -0
  34. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.github/workflows/publish.yml +0 -0
  35. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.gitignore +0 -0
  36. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.pre-commit-config.yaml +0 -0
  37. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.pre-commit-hooks.yaml +0 -0
  38. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.testmondata-shm +0 -0
  39. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/.testmondata-wal +0 -0
  40. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/CODE_OF_CONDUCT.md +0 -0
  41. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/CONTRIBUTING.md +0 -0
  42. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/LICENSE +0 -0
  43. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/SECURITY.md +0 -0
  44. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/Screenshot 2026-03-24 at 09.45.03.png +0 -0
  45. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/action.yml +0 -0
  46. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/demo.gif +0 -0
  47. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon-128.png +0 -0
  48. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon-16.png +0 -0
  49. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon-256.png +0 -0
  50. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon-32.png +0 -0
  51. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon-48.png +0 -0
  52. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon-512.png +0 -0
  53. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon-96.png +0 -0
  54. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/brand-icon.svg +0 -0
  55. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon-128.png +0 -0
  56. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon-16.png +0 -0
  57. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon-256.png +0 -0
  58. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon-32.png +0 -0
  59. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon-48.png +0 -0
  60. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon-512.png +0 -0
  61. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon-96.png +0 -0
  62. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-favicon.svg +0 -0
  63. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon-128.png +0 -0
  64. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon-16.png +0 -0
  65. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon-256.png +0 -0
  66. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon-32.png +0 -0
  67. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon-48.png +0 -0
  68. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon-512.png +0 -0
  69. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon-96.png +0 -0
  70. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/cli-icon.svg +0 -0
  71. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon-128.png +0 -0
  72. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon-16.png +0 -0
  73. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon-256.png +0 -0
  74. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon-32.png +0 -0
  75. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon-48.png +0 -0
  76. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon-512.png +0 -0
  77. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon-96.png +0 -0
  78. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/favicon.svg +0 -0
  79. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/icons/generate.sh +0 -0
  80. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/superpowers/specs/2026-03-24-v14-command-consolidation-design.md +0 -0
  81. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/docs/superpowers/specs/2026-03-25-v1.5-dashboard-design.md +0 -0
  82. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/scripts/generate_demo_data.py +0 -0
  83. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/__init__.py +0 -0
  84. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/__init__.py +0 -0
  85. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/aider.py +0 -0
  86. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/base.py +0 -0
  87. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/chatgpt.py +0 -0
  88. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/claude_chat.py +0 -0
  89. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/claude_code.py +0 -0
  90. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/cline.py +0 -0
  91. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/codex.py +0 -0
  92. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/cursor.py +0 -0
  93. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/filters.py +0 -0
  94. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/gemini.py +0 -0
  95. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/adapters/openclaw.py +0 -0
  96. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/bridge/__init__.py +0 -0
  97. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/bridge/handler.py +0 -0
  98. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/bridge/host.py +0 -0
  99. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/bridge/manifest.py +0 -0
  100. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/bridge/protocol.py +0 -0
  101. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/commands/__init__.py +0 -0
  102. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/commands/telemetry.py +0 -0
  103. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/commands/wrapped.py +0 -0
  104. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/config.py +0 -0
  105. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/__init__.py +0 -0
  106. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/agent.py +0 -0
  107. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/analyzer.py +0 -0
  108. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/compress.py +0 -0
  109. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/conversation.py +0 -0
  110. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/cost.py +0 -0
  111. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/dashboard.py +0 -0
  112. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/dedup.py +0 -0
  113. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/digest.py +0 -0
  114. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/distill.py +0 -0
  115. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/effectiveness.py +0 -0
  116. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/extractors.py +0 -0
  117. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/extractors_zh.py +0 -0
  118. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/insights.py +0 -0
  119. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/lang_detect.py +0 -0
  120. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/library.py +0 -0
  121. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/merge_view.py +0 -0
  122. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/models.py +0 -0
  123. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/persona.py +0 -0
  124. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/pipeline.py +0 -0
  125. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/privacy.py +0 -0
  126. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/privacy_scan.py +0 -0
  127. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/prompt_dna.py +0 -0
  128. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/recommend.py +0 -0
  129. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/repetition.py +0 -0
  130. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/rewrite.py +0 -0
  131. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/segmenter.py +0 -0
  132. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/session_meta.py +0 -0
  133. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/session_quality.py +0 -0
  134. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/session_type.py +0 -0
  135. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/style.py +0 -0
  136. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/templates.py +0 -0
  137. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/timeutil.py +0 -0
  138. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/trends.py +0 -0
  139. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/core/wrapped.py +0 -0
  140. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/demo.py +0 -0
  141. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/embeddings/__init__.py +0 -0
  142. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/embeddings/base.py +0 -0
  143. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/embeddings/local_embed.py +0 -0
  144. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/embeddings/ollama.py +0 -0
  145. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/embeddings/openai_embed.py +0 -0
  146. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/embeddings/tfidf.py +0 -0
  147. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/mcp.py +0 -0
  148. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/mcp_main.py +0 -0
  149. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/__init__.py +0 -0
  150. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/agent_terminal.py +0 -0
  151. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/chartjs.min.js +0 -0
  152. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/compress_terminal.py +0 -0
  153. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/dashboard_terminal.py +0 -0
  154. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/distill_terminal.py +0 -0
  155. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/export.py +0 -0
  156. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/html_report.py +0 -0
  157. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/json_out.py +0 -0
  158. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/markdown.py +0 -0
  159. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/projects_terminal.py +0 -0
  160. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/repetition_terminal.py +0 -0
  161. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/sessions_terminal.py +0 -0
  162. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/wrapped_html.py +0 -0
  163. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/output/wrapped_terminal.py +0 -0
  164. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/py.typed +0 -0
  165. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/sharing/__init__.py +0 -0
  166. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/sharing/client.py +0 -0
  167. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/sharing/clipboard.py +0 -0
  168. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/storage/__init__.py +0 -0
  169. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/storage/db.py +0 -0
  170. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/telemetry/__init__.py +0 -0
  171. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/telemetry/collector.py +0 -0
  172. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/telemetry/consent.py +0 -0
  173. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/telemetry/events.py +0 -0
  174. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/telemetry/prompt.py +0 -0
  175. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/telemetry/queue.py +0 -0
  176. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/src/reprompt/telemetry/sender.py +0 -0
  177. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/__init__.py +0 -0
  178. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/conftest.py +0 -0
  179. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/aider_chat_history.md +0 -0
  180. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/chatgpt_conversations.json +0 -0
  181. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/claude_chat_export.json +0 -0
  182. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/claude_session.jsonl +0 -0
  183. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/cline_task/api_conversation_history.json +0 -0
  184. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/export/default_export.md +0 -0
  185. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/export/full_export.md +0 -0
  186. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/gemini_session.json +0 -0
  187. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/fixtures/openclaw_session.jsonl +0 -0
  188. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_adapter_aider.py +0 -0
  189. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_adapter_chatgpt.py +0 -0
  190. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_adapter_claude.py +0 -0
  191. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_adapter_claude_chat.py +0 -0
  192. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_adapter_cline.py +0 -0
  193. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_adapter_gemini.py +0 -0
  194. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_adapter_openclaw.py +0 -0
  195. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_agent.py +0 -0
  196. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_agent_cli.py +0 -0
  197. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_analyzer.py +0 -0
  198. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_bridge_cli.py +0 -0
  199. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_bridge_e2e.py +0 -0
  200. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_bridge_handler.py +0 -0
  201. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_bridge_integration.py +0 -0
  202. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_bridge_manifest.py +0 -0
  203. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_bridge_protocol.py +0 -0
  204. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_cli.py +0 -0
  205. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_cli_deprecations.py +0 -0
  206. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_cli_library_effectiveness.py +0 -0
  207. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_clipboard.py +0 -0
  208. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_codex_adapter.py +0 -0
  209. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_compare_best_worst.py +0 -0
  210. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_compress.py +0 -0
  211. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_compress_cli.py +0 -0
  212. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_compress_dna.py +0 -0
  213. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_compress_html.py +0 -0
  214. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_compress_insights.py +0 -0
  215. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_config.py +0 -0
  216. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_conversation.py +0 -0
  217. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_copy_flag.py +0 -0
  218. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_cost.py +0 -0
  219. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_coverage_boost.py +0 -0
  220. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_cursor_adapter.py +0 -0
  221. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_dashboard.py +0 -0
  222. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_db.py +0 -0
  223. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_db_digest.py +0 -0
  224. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_db_effectiveness.py +0 -0
  225. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_db_session_quality.py +0 -0
  226. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_db_trends.py +0 -0
  227. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_dedup.py +0 -0
  228. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_demo.py +0 -0
  229. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_deprecated_commands.py +0 -0
  230. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_digest.py +0 -0
  231. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_digest_cli.py +0 -0
  232. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_distill.py +0 -0
  233. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_distill_cli.py +0 -0
  234. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_distill_weights.py +0 -0
  235. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_e2e.py +0 -0
  236. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_effectiveness.py +0 -0
  237. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_embeddings_local.py +0 -0
  238. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_embeddings_ollama.py +0 -0
  239. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_embeddings_openai.py +0 -0
  240. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_empty_state.py +0 -0
  241. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_export.py +0 -0
  242. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_export_cli.py +0 -0
  243. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_export_snapshot.py +0 -0
  244. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_extractors.py +0 -0
  245. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_extractors_routing.py +0 -0
  246. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_extractors_zh.py +0 -0
  247. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_extractors_zh_e2e.py +0 -0
  248. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_html_report.py +0 -0
  249. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_import_cli.py +0 -0
  250. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_import_e2e.py +0 -0
  251. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_init_cli.py +0 -0
  252. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_insights.py +0 -0
  253. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_insights_cli.py +0 -0
  254. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_insights_expanded.py +0 -0
  255. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_install_hook.py +0 -0
  256. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_lang_detect.py +0 -0
  257. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_library.py +0 -0
  258. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_lint_cli.py +0 -0
  259. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_markdown.py +0 -0
  260. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_mcp.py +0 -0
  261. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_merge_view.py +0 -0
  262. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_models.py +0 -0
  263. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_output.py +0 -0
  264. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_parse_conversation_base.py +0 -0
  265. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_parse_conversation_chatgpt.py +0 -0
  266. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_parse_conversation_claude.py +0 -0
  267. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_persona.py +0 -0
  268. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_pipeline.py +0 -0
  269. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_privacy.py +0 -0
  270. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_privacy_cli.py +0 -0
  271. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_privacy_e2e.py +0 -0
  272. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_privacy_output.py +0 -0
  273. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_privacy_scan.py +0 -0
  274. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_projects.py +0 -0
  275. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_prompt_dna.py +0 -0
  276. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_public_api.py +0 -0
  277. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_recommend.py +0 -0
  278. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_repetition.py +0 -0
  279. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_repetition_cli.py +0 -0
  280. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_repetition_output.py +0 -0
  281. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_rewrite.py +0 -0
  282. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_schema_version.py +0 -0
  283. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_score_cli.py +0 -0
  284. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_scorer.py +0 -0
  285. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_segmenter.py +0 -0
  286. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_session_quality.py +0 -0
  287. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_session_type.py +0 -0
  288. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_sessions_cli.py +0 -0
  289. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_sessions_output.py +0 -0
  290. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_share_e2e.py +0 -0
  291. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_sharing_client.py +0 -0
  292. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_source_filter.py +0 -0
  293. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_style.py +0 -0
  294. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_style_trends.py +0 -0
  295. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_suggestions.py +0 -0
  296. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_cli.py +0 -0
  297. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_collector.py +0 -0
  298. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_consent.py +0 -0
  299. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_e2e.py +0 -0
  300. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_events.py +0 -0
  301. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_prompt.py +0 -0
  302. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_queue.py +0 -0
  303. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_telemetry_sender.py +0 -0
  304. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_template_cli.py +0 -0
  305. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_templates.py +0 -0
  306. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_timeutil.py +0 -0
  307. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_trends.py +0 -0
  308. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_trends_cli.py +0 -0
  309. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_use_cli.py +0 -0
  310. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_wrapped.py +0 -0
  311. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_wrapped_cli.py +0 -0
  312. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_wrapped_e2e.py +0 -0
  313. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_wrapped_html.py +0 -0
  314. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_wrapped_output.py +0 -0
  315. {reprompt_cli-2.0.2 → reprompt_cli-2.2.0}/tests/test_wrapped_share.py +0 -0
@@ -2,6 +2,29 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.2.0] - 2026-04-01
6
+
7
+ ### Added
8
+ - **Prompt builder** — `reprompt build "task" --file src/auth.ts --error "TypeError" --constraint "keep tests"` assembles well-scored prompts from components. Model-aware formatting: XML tags for Claude, markdown headers for GPT. Shows score, tier, and suggestions for missing components.
9
+ - **Unified diagnostic** — `reprompt check "prompt"` runs score + lint + rewrite in one command. Shows dimensional breakdown, strengths, suggestions with point values, lint issues, and auto-rewrite preview.
10
+ - **Prompt explainer** — `reprompt explain "prompt"` explains what makes a prompt good or bad in plain English. Educational feedback with research-backed insights per dimension.
11
+ - Tests: 1741 → 1846
12
+
13
+ ## [2.1.0] - 2026-04-01
14
+
15
+ ### Added
16
+ - **Model-specific lint rules** — `reprompt lint --model claude/gpt/gemini` checks prompts against model-specific best practices. 8 rules: XML tag preference (Claude), markdown structure (GPT), JSON instruction requirements (GPT), CoT anti-pattern for o-series (GPT), prompt length limits (Gemini), broad negative detection (Gemini). Based on official model documentation.
17
+ - **Diff preview for rewrite** — `reprompt rewrite --diff` shows a git-style unified diff between original and rewritten prompt. Color-coded: red removals, green additions, cyan range markers.
18
+ - **Token budget lint** — `reprompt lint --max-tokens 4096` warns when prompts exceed a token budget. Configurable via `.reprompt.toml` (`max-tokens`) or CLI flag. Uses locale-aware token estimation from cost module.
19
+ - Tests: 1716 → 1741
20
+
21
+ ## [2.0.2] - 2026-04-01
22
+
23
+ ### Changed
24
+ - **Scoring rebalance** — Structure weight reduced 25→15, Clarity increased 15→25. Plain-text prompts now score 55-65 instead of 35-45. Real-world conversational prompts are no longer penalized for lacking markdown structure.
25
+ - **Tier labels** — Scores display as EXPERT (85+), STRONG (70+), GOOD (50+), BASIC (30+), DRAFT (<30) instead of raw numbers. Applied across CLI, extension badge, popup, and HTML dashboard.
26
+ - **Positive UX feedback** — Score output now includes "Strengths" section showing what the prompt does well. Suggestions show expected point gain (`+N pts`). Badge color thresholds adjusted: 85/60/40/25.
27
+
5
28
  ## [2.0.1] - 2026-03-31
6
29
 
7
30
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reprompt-cli
3
- Version: 2.0.2
3
+ Version: 2.2.0
4
4
  Summary: Discover, analyze, and optimize your prompts from AI coding sessions
5
5
  Project-URL: Homepage, https://github.com/reprompt-dev/reprompt
6
6
  Project-URL: Repository, https://github.com/reprompt-dev/reprompt
@@ -104,6 +104,7 @@ $ reprompt
104
104
  |---------|-------------|
105
105
  | `reprompt` | Instant dashboard -- prompts, sessions, avg score, top categories |
106
106
  | `reprompt scan` | Auto-discover prompts from 9 AI tools |
107
+ | `reprompt check "prompt"` | **Full diagnostic** -- score + lint + rewrite preview in one command |
107
108
  | `reprompt score "prompt"` | Research-backed 0-100 scoring with 30+ features |
108
109
  | `reprompt compare "a" "b"` | Side-by-side prompt analysis (or `--best-worst` for auto-selection) |
109
110
  | `reprompt insights` | Personal patterns vs research-optimal benchmarks |
@@ -117,6 +118,7 @@ $ reprompt
117
118
 
118
119
  | Command | Description |
119
120
  |---------|-------------|
121
+ | `reprompt build "task"` | **Build prompts from components** -- task, context, files, errors, constraints. Model-aware (Claude/GPT/Gemini) |
120
122
  | `reprompt rewrite "prompt"` | **Rewrite prompts to score higher** -- filler removal, restructuring, hedging cleanup |
121
123
  | `reprompt compress "prompt"` | 4-layer prompt compression (40-60% token savings typical) |
122
124
  | `reprompt distill` | Extract important turns from conversations with 6-signal scoring |
@@ -228,7 +230,7 @@ Captured prompts sync locally via Native Messaging -- nothing leaves your machin
228
230
  # .pre-commit-config.yaml
229
231
  repos:
230
232
  - repo: https://github.com/reprompt-dev/reprompt
231
- rev: v2.0.1
233
+ rev: v2.2.0
232
234
  hooks:
233
235
  - id: reprompt-lint
234
236
  ```
@@ -59,6 +59,7 @@ $ reprompt
59
59
  |---------|-------------|
60
60
  | `reprompt` | Instant dashboard -- prompts, sessions, avg score, top categories |
61
61
  | `reprompt scan` | Auto-discover prompts from 9 AI tools |
62
+ | `reprompt check "prompt"` | **Full diagnostic** -- score + lint + rewrite preview in one command |
62
63
  | `reprompt score "prompt"` | Research-backed 0-100 scoring with 30+ features |
63
64
  | `reprompt compare "a" "b"` | Side-by-side prompt analysis (or `--best-worst` for auto-selection) |
64
65
  | `reprompt insights` | Personal patterns vs research-optimal benchmarks |
@@ -72,6 +73,7 @@ $ reprompt
72
73
 
73
74
  | Command | Description |
74
75
  |---------|-------------|
76
+ | `reprompt build "task"` | **Build prompts from components** -- task, context, files, errors, constraints. Model-aware (Claude/GPT/Gemini) |
75
77
  | `reprompt rewrite "prompt"` | **Rewrite prompts to score higher** -- filler removal, restructuring, hedging cleanup |
76
78
  | `reprompt compress "prompt"` | 4-layer prompt compression (40-60% token savings typical) |
77
79
  | `reprompt distill` | Extract important turns from conversations with 6-signal scoring |
@@ -183,7 +185,7 @@ Captured prompts sync locally via Native Messaging -- nothing leaves your machin
183
185
  # .pre-commit-config.yaml
184
186
  repos:
185
187
  - repo: https://github.com/reprompt-dev/reprompt
186
- rev: v2.0.1
188
+ rev: v2.2.0
187
189
  hooks:
188
190
  - id: reprompt-lint
189
191
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "reprompt-cli"
3
- version = "2.0.2"
3
+ version = "2.2.0"
4
4
  description = "Discover, analyze, and optimize your prompts from AI coding sessions"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -797,6 +797,12 @@ def lint(
797
797
  score_threshold: int = typer.Option(
798
798
  0, "--score-threshold", help="Fail if avg prompt score < threshold (CI mode)"
799
799
  ),
800
+ model: str = typer.Option(
801
+ None, "--model", "-m", help="Target model for model-specific rules (claude/gpt/gemini)"
802
+ ),
803
+ max_tokens: int = typer.Option(
804
+ 0, "--max-tokens", help="Warn when prompts exceed token budget (0 = disabled)"
805
+ ),
800
806
  copy: bool = typer.Option(False, "--copy", help="Copy result to clipboard"),
801
807
  ) -> None:
802
808
  """Check prompt quality against lint rules.
@@ -806,6 +812,12 @@ def lint(
806
812
  - short-prompt: prompts under 40 chars (warning)
807
813
  - vague-prompt: overly vague prompts like "fix it"
808
814
  - debug-needs-reference: debug prompts without file/function references
815
+ - max-tokens: prompt exceeds token budget
816
+
817
+ Model-specific rules (--model):
818
+ - claude: suggests XML tags for structure
819
+ - gpt: warns on XML tags (may echo verbatim), prefers markdown
820
+ - gemini: warns on very long prompts
809
821
 
810
822
  CI mode: use --score-threshold to fail if average score is below a threshold.
811
823
 
@@ -813,6 +825,8 @@ def lint(
813
825
 
814
826
  reprompt lint # lint stored prompts
815
827
 
828
+ reprompt lint --model claude # with Claude-specific hints
829
+
816
830
  reprompt lint --score-threshold 50 # fail if avg score < 50 (CI mode)
817
831
 
818
832
  reprompt lint --strict --json # strict mode with JSON output
@@ -832,6 +846,10 @@ def lint(
832
846
 
833
847
  # CLI flags override config file
834
848
  effective_threshold = score_threshold if score_threshold > 0 else lint_config.score_threshold
849
+ if model:
850
+ lint_config.model = model.lower()
851
+ if max_tokens > 0:
852
+ lint_config.max_tokens = max_tokens
835
853
 
836
854
  # Collect prompts from DB (already scanned)
837
855
  rows = db.get_all_prompts()
@@ -928,6 +946,111 @@ def lint(
928
946
  raise typer.Exit(1)
929
947
 
930
948
 
949
+ @app.command(rich_help_panel="Analyze")
950
+ def check(
951
+ text: str = typer.Argument(..., help="Prompt text to check"),
952
+ model: str = typer.Option("", "--model", "-m", help="Target model (claude/gpt/gemini)"),
953
+ max_tokens: int = typer.Option(0, "--max-tokens", help="Token budget (0 = disabled)"),
954
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
955
+ copy: bool = typer.Option(False, "--copy", help="Copy rewritten prompt to clipboard"),
956
+ ) -> None:
957
+ """Full prompt diagnostic — score + lint + rewrite in one command.
958
+
959
+ Runs all quality checks and shows a unified report with score breakdown,
960
+ strengths, suggestions, lint issues, and auto-rewrite preview.
961
+
962
+ Examples:
963
+
964
+ reprompt check "fix the auth bug in login.ts"
965
+
966
+ reprompt check "refactor the middleware" --model claude
967
+
968
+ reprompt check "help me debug this crash" --json
969
+ """
970
+ from reprompt.core.check import check_prompt
971
+
972
+ result = check_prompt(text, model=model, max_tokens=max_tokens)
973
+
974
+ if json_output:
975
+ import json as json_mod
976
+
977
+ data = {
978
+ "total": result.total,
979
+ "tier": result.tier,
980
+ "clarity": result.clarity,
981
+ "context": result.context,
982
+ "position": result.position,
983
+ "structure": result.structure,
984
+ "repetition": result.repetition,
985
+ "word_count": result.word_count,
986
+ "token_count": result.token_count,
987
+ "confirmations": result.confirmations,
988
+ "suggestions": result.suggestions,
989
+ "lint_issues": result.lint_issues,
990
+ "rewritten": result.rewritten,
991
+ "rewrite_delta": result.rewrite_delta,
992
+ "rewrite_changes": result.rewrite_changes,
993
+ }
994
+ typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
995
+ else:
996
+ from reprompt.output.check_terminal import render_check
997
+
998
+ typer.echo(render_check(result))
999
+
1000
+ if copy:
1001
+ _copy_to_clip(result.rewritten, quiet=json_output)
1002
+
1003
+ from reprompt.core.suggestions import get_suggestion
1004
+
1005
+ hint = get_suggestion("check")
1006
+ if hint and not json_output:
1007
+ console.print(f" [dim]→ Try: {hint}[/dim]\n")
1008
+
1009
+
1010
+ @app.command(rich_help_panel="Analyze")
1011
+ def explain(
1012
+ text: str = typer.Argument(..., help="Prompt text to explain"),
1013
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
1014
+ ) -> None:
1015
+ """Explain what makes a prompt good or bad in plain English.
1016
+
1017
+ Analyzes the prompt and provides educational feedback: what's working,
1018
+ what's missing, and specific tips to improve. No LLM needed.
1019
+
1020
+ Examples:
1021
+
1022
+ reprompt explain "fix the auth bug"
1023
+
1024
+ reprompt explain "Fix the JWT expiration in src/auth.ts line 42" --json
1025
+ """
1026
+ from reprompt.core.explain import explain_prompt
1027
+
1028
+ result = explain_prompt(text)
1029
+
1030
+ if json_output:
1031
+ import json as json_mod
1032
+
1033
+ data = {
1034
+ "score": result.score,
1035
+ "tier": result.tier,
1036
+ "summary": result.summary,
1037
+ "strengths": result.strengths,
1038
+ "weaknesses": result.weaknesses,
1039
+ "tips": result.tips,
1040
+ }
1041
+ typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
1042
+ else:
1043
+ from reprompt.output.explain_terminal import render_explain
1044
+
1045
+ typer.echo(render_explain(result))
1046
+
1047
+ from reprompt.core.suggestions import get_suggestion
1048
+
1049
+ hint = get_suggestion("explain")
1050
+ if hint and not json_output:
1051
+ console.print(f" [dim]→ Try: {hint}[/dim]\n")
1052
+
1053
+
931
1054
  @app.command(rich_help_panel="Analyze")
932
1055
  def score(
933
1056
  text: str = typer.Argument(..., help="Prompt text to score"),
@@ -1087,6 +1210,7 @@ def compress(
1087
1210
  def rewrite(
1088
1211
  text: str = typer.Argument(..., help="Prompt text to improve"),
1089
1212
  json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
1213
+ diff: bool = typer.Option(False, "--diff", help="Show unified diff (red/green)"),
1090
1214
  copy: bool = typer.Option(False, "--copy", help="Copy rewritten text to clipboard"),
1091
1215
  ) -> None:
1092
1216
  """Rewrite a prompt to improve its score. Rule-based, no LLM needed.
@@ -1099,9 +1223,9 @@ def rewrite(
1099
1223
 
1100
1224
  reprompt rewrite "I was wondering if you could fix the authentication bug"
1101
1225
 
1102
- reprompt rewrite "please help me refactor this code to be better" --copy
1226
+ reprompt rewrite "fix the login" --diff
1103
1227
 
1104
- reprompt rewrite "fix the login" --json
1228
+ reprompt rewrite "please help me refactor this code to be better" --copy
1105
1229
  """
1106
1230
  from reprompt.core.rewrite import rewrite_prompt
1107
1231
 
@@ -1120,6 +1244,10 @@ def rewrite(
1120
1244
  "manual_suggestions": result.manual_suggestions,
1121
1245
  }
1122
1246
  typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
1247
+ elif diff:
1248
+ from reprompt.output.rewrite_terminal import render_rewrite_diff
1249
+
1250
+ typer.echo(render_rewrite_diff(result))
1123
1251
  else:
1124
1252
  from reprompt.output.rewrite_terminal import render_rewrite
1125
1253
 
@@ -1135,6 +1263,73 @@ def rewrite(
1135
1263
  console.print(f" [dim]→ Try: {hint}[/dim]\n")
1136
1264
 
1137
1265
 
1266
+ @app.command(rich_help_panel="Optimize")
1267
+ def build(
1268
+ task: str = typer.Argument(..., help="What the AI should do"),
1269
+ context: str = typer.Option("", "--context", "-c", help="Background information"),
1270
+ file: list[str] = typer.Option([], "--file", "-f", help="File references (repeatable)"),
1271
+ error: str = typer.Option("", "--error", "-e", help="Error message or stack trace"),
1272
+ constraint: list[str] = typer.Option([], "--constraint", help="Constraints (repeatable)"),
1273
+ example: str = typer.Option("", "--example", help="Example input/output"),
1274
+ output_format: str = typer.Option("", "--output-format", help="Expected response format"),
1275
+ role: str = typer.Option("", "--role", "-r", help="AI role/persona"),
1276
+ model: str = typer.Option("", "--model", "-m", help="Target model (claude/gpt/gemini)"),
1277
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
1278
+ copy: bool = typer.Option(False, "--copy", help="Copy built prompt to clipboard"),
1279
+ ) -> None:
1280
+ """Build a well-structured prompt from components.
1281
+
1282
+ Assembles a prompt that maximizes quality score by combining
1283
+ your task with context, files, errors, and constraints.
1284
+
1285
+ Examples:
1286
+
1287
+ reprompt build "fix the auth bug"
1288
+
1289
+ reprompt build "fix the crash" --file src/auth.ts --error "TypeError: ..."
1290
+
1291
+ reprompt build "refactor" -f src/app.py --constraint "keep tests" --model claude
1292
+ """
1293
+ from reprompt.core.build import build_prompt
1294
+
1295
+ result = build_prompt(
1296
+ task,
1297
+ context=context,
1298
+ files=file if file else None,
1299
+ error=error,
1300
+ constraints=constraint if constraint else None,
1301
+ examples=example,
1302
+ output_format=output_format,
1303
+ role=role,
1304
+ model=model,
1305
+ )
1306
+
1307
+ if json_output:
1308
+ import json as json_mod
1309
+
1310
+ data = {
1311
+ "prompt": result.prompt,
1312
+ "score": result.score,
1313
+ "tier": result.tier,
1314
+ "components_used": result.components_used,
1315
+ "suggestions": result.suggestions,
1316
+ }
1317
+ typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
1318
+ else:
1319
+ from reprompt.output.build_terminal import render_build
1320
+
1321
+ typer.echo(render_build(result))
1322
+
1323
+ if copy:
1324
+ _copy_to_clip(result.prompt, quiet=json_output)
1325
+
1326
+ from reprompt.core.suggestions import get_suggestion
1327
+
1328
+ hint = get_suggestion("build")
1329
+ if hint and not json_output:
1330
+ console.print(f" [dim]→ Try: {hint}[/dim]\n")
1331
+
1332
+
1138
1333
  @app.command(rich_help_panel="Optimize")
1139
1334
  def distill(
1140
1335
  session_id: str = typer.Argument(None, help="Session ID to distill"),
@@ -2042,6 +2237,13 @@ def init(
2042
2237
  # Useful for CI: reprompt lint --score-threshold reads this value
2043
2238
  # score-threshold = 50
2044
2239
 
2240
+ # Target model for model-specific rules (claude, gpt, gemini)
2241
+ # Enables rules like "prefer XML tags" (Claude) or "avoid XML tags" (GPT)
2242
+ # model = "claude"
2243
+
2244
+ # Token budget — warn when prompts exceed this limit (0 = disabled)
2245
+ # max-tokens = 4096
2246
+
2045
2247
  [lint.rules]
2046
2248
  # min-length: error if prompt < N chars (0 = disabled)
2047
2249
  min-length = 20
@@ -0,0 +1,246 @@
1
+ """Prompt builder — assemble a well-structured prompt from components.
2
+
3
+ Takes a task description and optional context, files, errors, constraints,
4
+ and examples, then assembles a prompt that maximizes the scoring dimensions.
5
+ Scoring-aware: structures the output to hit clarity, context, and position.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+
12
+
13
+ @dataclass
14
+ class BuildResult:
15
+ """Result of building a prompt from components."""
16
+
17
+ prompt: str
18
+ score: float = 0.0
19
+ tier: str = ""
20
+ components_used: list[str] = field(default_factory=list)
21
+ suggestions: list[str] = field(default_factory=list)
22
+
23
+
24
+ def build_prompt(
25
+ task: str,
26
+ *,
27
+ context: str = "",
28
+ files: list[str] | None = None,
29
+ error: str = "",
30
+ constraints: list[str] | None = None,
31
+ examples: str = "",
32
+ output_format: str = "",
33
+ role: str = "",
34
+ model: str = "",
35
+ ) -> BuildResult:
36
+ """Build a well-structured prompt from components.
37
+
38
+ Returns the assembled prompt with its score and suggestions
39
+ for components the user could still add.
40
+ """
41
+ parts: list[str] = []
42
+ components: list[str] = []
43
+
44
+ # Role (if provided) — goes first
45
+ if role:
46
+ parts.append(f"You are {role}.")
47
+ components.append("role")
48
+
49
+ # Task — always present, imperative form, at the front (position bias)
50
+ task_text = _ensure_imperative(task.strip())
51
+ if task_text and task_text[-1] not in ".!?":
52
+ task_text += "."
53
+ parts.append(task_text)
54
+ components.append("task")
55
+
56
+ # File references — high context value
57
+ if files:
58
+ if len(files) == 1:
59
+ parts.append(f"File: {files[0]}")
60
+ else:
61
+ file_list = ", ".join(files)
62
+ parts.append(f"Files: {file_list}")
63
+ components.append("files")
64
+
65
+ # Error context — critical for debug prompts
66
+ if error:
67
+ parts.append(f"Error: {error}")
68
+ components.append("error")
69
+
70
+ # Context — background information
71
+ if context:
72
+ parts.append(f"Context: {context}")
73
+ components.append("context")
74
+
75
+ # Examples
76
+ if examples:
77
+ parts.append(f"Example:\n{examples}")
78
+ components.append("examples")
79
+
80
+ # Constraints
81
+ if constraints:
82
+ if len(constraints) == 1:
83
+ parts.append(f"Constraint: {constraints[0]}")
84
+ else:
85
+ constraint_lines = "\n".join(f"- {c}" for c in constraints)
86
+ parts.append(f"Constraints:\n{constraint_lines}")
87
+ components.append("constraints")
88
+
89
+ # Output format
90
+ if output_format:
91
+ parts.append(f"Output format: {output_format}")
92
+ components.append("output_format")
93
+
94
+ # Model-specific formatting
95
+ prompt = _format_for_model(parts, model)
96
+
97
+ # Score the assembled prompt
98
+ from reprompt.core.extractors import extract_features
99
+ from reprompt.core.scorer import score_prompt
100
+
101
+ dna = extract_features(prompt, source="build", session_id="")
102
+ score_result = score_prompt(dna)
103
+
104
+ # Determine tier
105
+ tier = _get_tier(score_result.total)
106
+
107
+ # Generate suggestions for missing components
108
+ suggestions = _missing_suggestions(components)
109
+
110
+ return BuildResult(
111
+ prompt=prompt,
112
+ score=score_result.total,
113
+ tier=tier,
114
+ components_used=components,
115
+ suggestions=suggestions,
116
+ )
117
+
118
+
119
+ def _ensure_imperative(task: str) -> str:
120
+ """Strip filler prefixes to get an imperative task statement."""
121
+ import re
122
+
123
+ # Remove common polite/filler prefixes
124
+ cleaned = re.sub(
125
+ r"^(?:please\s+|can you\s+|could you\s+|i need you to\s+|"
126
+ r"i want you to\s+|i would like you to\s+|help me\s+|"
127
+ r"i was wondering if you could\s+)",
128
+ "",
129
+ task,
130
+ flags=re.IGNORECASE,
131
+ )
132
+ # Capitalize first letter
133
+ if cleaned:
134
+ cleaned = cleaned[0].upper() + cleaned[1:]
135
+ return cleaned
136
+
137
+
138
+ def _format_for_model(parts: list[str], model: str) -> str:
139
+ """Format prompt parts with model-appropriate structure."""
140
+ if model == "claude":
141
+ return _format_xml(parts)
142
+ elif model == "gpt":
143
+ return _format_markdown(parts)
144
+ else:
145
+ # Default: clean plain text with double newlines
146
+ return "\n\n".join(parts)
147
+
148
+
149
+ def _format_xml(parts: list[str]) -> str:
150
+ """Format with XML tags (preferred by Claude)."""
151
+ if len(parts) <= 2:
152
+ # Short prompts don't need XML
153
+ return "\n\n".join(parts)
154
+
155
+ sections: list[str] = []
156
+ for part in parts:
157
+ lower = part.lower()
158
+ if lower.startswith("you are "):
159
+ sections.append(part)
160
+ elif lower.startswith("context:"):
161
+ content = part[len("Context:") :].strip()
162
+ sections.append(f"<context>\n{content}\n</context>")
163
+ elif lower.startswith("error:"):
164
+ content = part[len("Error:") :].strip()
165
+ sections.append(f"<context>\n{content}\n</context>")
166
+ elif lower.startswith("constraint"):
167
+ content = part.split(":", 1)[1].strip() if ":" in part else part
168
+ sections.append(f"<constraints>\n{content}\n</constraints>")
169
+ elif lower.startswith("example"):
170
+ content = part.split(":", 1)[1].strip() if ":" in part else part
171
+ content = part.split("\n", 1)[1].strip() if "\n" in part else content
172
+ sections.append(f"<examples>\n{content}\n</examples>")
173
+ elif lower.startswith("output format:"):
174
+ content = part[len("Output format:") :].strip()
175
+ sections.append(f"<output>\n{content}\n</output>")
176
+ else:
177
+ sections.append(part)
178
+
179
+ return "\n\n".join(sections)
180
+
181
+
182
+ def _format_markdown(parts: list[str]) -> str:
183
+ """Format with markdown headers (preferred by GPT)."""
184
+ if len(parts) <= 2:
185
+ return "\n\n".join(parts)
186
+
187
+ sections: list[str] = []
188
+ for part in parts:
189
+ lower = part.lower()
190
+ if lower.startswith("you are "):
191
+ sections.append(part)
192
+ elif lower.startswith("context:"):
193
+ content = part[len("Context:") :].strip()
194
+ sections.append(f"## Context\n{content}")
195
+ elif lower.startswith("error:"):
196
+ content = part[len("Error:") :].strip()
197
+ sections.append(f"## Error\n{content}")
198
+ elif lower.startswith("constraint"):
199
+ content = part.split(":", 1)[1].strip() if ":" in part else part
200
+ sections.append(f"## Constraints\n{content}")
201
+ elif lower.startswith("example"):
202
+ content = part.split(":", 1)[1].strip() if ":" in part else part
203
+ content = part.split("\n", 1)[1].strip() if "\n" in part else content
204
+ sections.append(f"## Examples\n{content}")
205
+ elif lower.startswith("output format:"):
206
+ content = part[len("Output format:") :].strip()
207
+ sections.append(f"## Output Format\n{content}")
208
+ else:
209
+ sections.append(part)
210
+
211
+ return "\n\n".join(sections)
212
+
213
+
214
+ def _get_tier(score: float) -> str:
215
+ """Map score to tier label."""
216
+ if score >= 85:
217
+ return "EXPERT"
218
+ if score >= 70:
219
+ return "STRONG"
220
+ if score >= 50:
221
+ return "GOOD"
222
+ if score >= 30:
223
+ return "BASIC"
224
+ return "DRAFT"
225
+
226
+
227
+ def _missing_suggestions(components: list[str]) -> list[str]:
228
+ """Suggest components the user could add to improve the prompt."""
229
+ suggestions: list[str] = []
230
+
231
+ if "files" not in components:
232
+ suggestions.append("Add --file to reference specific files (+6 pts)")
233
+ if "error" not in components:
234
+ suggestions.append("Add --error with the actual error message (+6 pts)")
235
+ if "constraints" not in components:
236
+ suggestions.append("Add --constraint to set boundaries (+5 pts)")
237
+ if "context" not in components:
238
+ suggestions.append("Add --context for background information (+4 pts)")
239
+ if "examples" not in components:
240
+ suggestions.append("Add --example with expected input/output (+3 pts)")
241
+ if "role" not in components:
242
+ suggestions.append("Add --role to set the AI's perspective (+3 pts)")
243
+ if "output_format" not in components:
244
+ suggestions.append("Add --output-format to specify response structure (+2 pts)")
245
+
246
+ return suggestions