reprompt-cli 2.1.0__tar.gz → 2.2.1__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 (316) hide show
  1. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/CHANGELOG.md +15 -0
  2. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/PKG-INFO +4 -2
  3. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/README.md +3 -1
  4. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/pyproject.toml +1 -1
  5. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/cli.py +203 -6
  6. reprompt_cli-2.2.1/src/reprompt/core/build.py +246 -0
  7. reprompt_cli-2.2.1/src/reprompt/core/check.py +115 -0
  8. reprompt_cli-2.2.1/src/reprompt/core/explain.py +208 -0
  9. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/suggestions.py +3 -0
  10. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/mcp.py +128 -1
  11. reprompt_cli-2.2.1/src/reprompt/output/build_terminal.py +52 -0
  12. reprompt_cli-2.2.1/src/reprompt/output/check_terminal.py +105 -0
  13. reprompt_cli-2.2.1/src/reprompt/output/explain_terminal.py +58 -0
  14. reprompt_cli-2.2.1/tests/test_build.py +270 -0
  15. reprompt_cli-2.2.1/tests/test_build_cli.py +126 -0
  16. reprompt_cli-2.2.1/tests/test_build_output.py +59 -0
  17. reprompt_cli-2.2.1/tests/test_check.py +101 -0
  18. reprompt_cli-2.2.1/tests/test_check_cli.py +80 -0
  19. reprompt_cli-2.2.1/tests/test_explain.py +100 -0
  20. reprompt_cli-2.2.1/tests/test_explain_cli.py +56 -0
  21. reprompt_cli-2.2.1/tests/test_file_input.py +107 -0
  22. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_mcp.py +69 -2
  23. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/uv.lock +1 -1
  24. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.editorconfig +0 -0
  25. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  26. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  27. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  28. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  29. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  30. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/dependabot.yml +0 -0
  31. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/workflows/ci.yml +0 -0
  32. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.github/workflows/publish.yml +0 -0
  33. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.gitignore +0 -0
  34. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.pre-commit-config.yaml +0 -0
  35. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.pre-commit-hooks.yaml +0 -0
  36. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.testmondata-shm +0 -0
  37. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/.testmondata-wal +0 -0
  38. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/CODE_OF_CONDUCT.md +0 -0
  39. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/CONTRIBUTING.md +0 -0
  40. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/LICENSE +0 -0
  41. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/SECURITY.md +0 -0
  42. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/Screenshot 2026-03-24 at 09.45.03.png +0 -0
  43. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/action.yml +0 -0
  44. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/demo.gif +0 -0
  45. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon-128.png +0 -0
  46. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon-16.png +0 -0
  47. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon-256.png +0 -0
  48. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon-32.png +0 -0
  49. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon-48.png +0 -0
  50. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon-512.png +0 -0
  51. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon-96.png +0 -0
  52. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/brand-icon.svg +0 -0
  53. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon-128.png +0 -0
  54. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon-16.png +0 -0
  55. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon-256.png +0 -0
  56. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon-32.png +0 -0
  57. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon-48.png +0 -0
  58. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon-512.png +0 -0
  59. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon-96.png +0 -0
  60. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-favicon.svg +0 -0
  61. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon-128.png +0 -0
  62. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon-16.png +0 -0
  63. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon-256.png +0 -0
  64. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon-32.png +0 -0
  65. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon-48.png +0 -0
  66. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon-512.png +0 -0
  67. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon-96.png +0 -0
  68. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/cli-icon.svg +0 -0
  69. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon-128.png +0 -0
  70. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon-16.png +0 -0
  71. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon-256.png +0 -0
  72. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon-32.png +0 -0
  73. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon-48.png +0 -0
  74. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon-512.png +0 -0
  75. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon-96.png +0 -0
  76. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/favicon.svg +0 -0
  77. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/icons/generate.sh +0 -0
  78. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/superpowers/specs/2026-03-24-v14-command-consolidation-design.md +0 -0
  79. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/docs/superpowers/specs/2026-03-25-v1.5-dashboard-design.md +0 -0
  80. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/scripts/generate_demo_data.py +0 -0
  81. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/__init__.py +0 -0
  82. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/__init__.py +0 -0
  83. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/aider.py +0 -0
  84. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/base.py +0 -0
  85. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/chatgpt.py +0 -0
  86. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/claude_chat.py +0 -0
  87. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/claude_code.py +0 -0
  88. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/cline.py +0 -0
  89. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/codex.py +0 -0
  90. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/cursor.py +0 -0
  91. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/filters.py +0 -0
  92. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/gemini.py +0 -0
  93. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/adapters/openclaw.py +0 -0
  94. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/bridge/__init__.py +0 -0
  95. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/bridge/handler.py +0 -0
  96. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/bridge/host.py +0 -0
  97. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/bridge/manifest.py +0 -0
  98. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/bridge/protocol.py +0 -0
  99. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/commands/__init__.py +0 -0
  100. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/commands/telemetry.py +0 -0
  101. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/commands/wrapped.py +0 -0
  102. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/config.py +0 -0
  103. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/__init__.py +0 -0
  104. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/agent.py +0 -0
  105. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/analyzer.py +0 -0
  106. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/compress.py +0 -0
  107. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/conversation.py +0 -0
  108. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/cost.py +0 -0
  109. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/dashboard.py +0 -0
  110. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/dedup.py +0 -0
  111. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/digest.py +0 -0
  112. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/distill.py +0 -0
  113. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/effectiveness.py +0 -0
  114. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/extractors.py +0 -0
  115. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/extractors_zh.py +0 -0
  116. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/insights.py +0 -0
  117. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/lang_detect.py +0 -0
  118. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/library.py +0 -0
  119. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/lint.py +0 -0
  120. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/merge_view.py +0 -0
  121. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/models.py +0 -0
  122. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/persona.py +0 -0
  123. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/pipeline.py +0 -0
  124. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/privacy.py +0 -0
  125. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/privacy_scan.py +0 -0
  126. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/prompt_dna.py +0 -0
  127. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/recommend.py +0 -0
  128. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/repetition.py +0 -0
  129. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/rewrite.py +0 -0
  130. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/scorer.py +0 -0
  131. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/segmenter.py +0 -0
  132. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/session_meta.py +0 -0
  133. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/session_quality.py +0 -0
  134. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/session_type.py +0 -0
  135. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/style.py +0 -0
  136. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/templates.py +0 -0
  137. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/timeutil.py +0 -0
  138. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/trends.py +0 -0
  139. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/core/wrapped.py +0 -0
  140. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/demo.py +0 -0
  141. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/embeddings/__init__.py +0 -0
  142. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/embeddings/base.py +0 -0
  143. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/embeddings/local_embed.py +0 -0
  144. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/embeddings/ollama.py +0 -0
  145. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/embeddings/openai_embed.py +0 -0
  146. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/embeddings/tfidf.py +0 -0
  147. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/mcp_main.py +0 -0
  148. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/__init__.py +0 -0
  149. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/agent_terminal.py +0 -0
  150. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/chartjs.min.js +0 -0
  151. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/compress_terminal.py +0 -0
  152. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/dashboard_terminal.py +0 -0
  153. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/distill_terminal.py +0 -0
  154. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/export.py +0 -0
  155. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/html_report.py +0 -0
  156. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/json_out.py +0 -0
  157. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/markdown.py +0 -0
  158. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/projects_terminal.py +0 -0
  159. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/repetition_terminal.py +0 -0
  160. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/rewrite_terminal.py +0 -0
  161. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/sessions_terminal.py +0 -0
  162. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/terminal.py +0 -0
  163. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/wrapped_html.py +0 -0
  164. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/output/wrapped_terminal.py +0 -0
  165. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/py.typed +0 -0
  166. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/sharing/__init__.py +0 -0
  167. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/sharing/client.py +0 -0
  168. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/sharing/clipboard.py +0 -0
  169. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/storage/__init__.py +0 -0
  170. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/storage/db.py +0 -0
  171. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/telemetry/__init__.py +0 -0
  172. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/telemetry/collector.py +0 -0
  173. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/telemetry/consent.py +0 -0
  174. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/telemetry/events.py +0 -0
  175. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/telemetry/prompt.py +0 -0
  176. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/telemetry/queue.py +0 -0
  177. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/src/reprompt/telemetry/sender.py +0 -0
  178. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/__init__.py +0 -0
  179. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/conftest.py +0 -0
  180. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/aider_chat_history.md +0 -0
  181. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/chatgpt_conversations.json +0 -0
  182. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/claude_chat_export.json +0 -0
  183. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/claude_session.jsonl +0 -0
  184. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/cline_task/api_conversation_history.json +0 -0
  185. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/export/default_export.md +0 -0
  186. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/export/full_export.md +0 -0
  187. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/gemini_session.json +0 -0
  188. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/fixtures/openclaw_session.jsonl +0 -0
  189. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_adapter_aider.py +0 -0
  190. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_adapter_chatgpt.py +0 -0
  191. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_adapter_claude.py +0 -0
  192. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_adapter_claude_chat.py +0 -0
  193. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_adapter_cline.py +0 -0
  194. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_adapter_gemini.py +0 -0
  195. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_adapter_openclaw.py +0 -0
  196. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_agent.py +0 -0
  197. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_agent_cli.py +0 -0
  198. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_analyzer.py +0 -0
  199. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_bridge_cli.py +0 -0
  200. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_bridge_e2e.py +0 -0
  201. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_bridge_handler.py +0 -0
  202. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_bridge_integration.py +0 -0
  203. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_bridge_manifest.py +0 -0
  204. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_bridge_protocol.py +0 -0
  205. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_cli.py +0 -0
  206. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_cli_deprecations.py +0 -0
  207. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_cli_library_effectiveness.py +0 -0
  208. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_clipboard.py +0 -0
  209. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_codex_adapter.py +0 -0
  210. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_compare_best_worst.py +0 -0
  211. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_compress.py +0 -0
  212. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_compress_cli.py +0 -0
  213. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_compress_dna.py +0 -0
  214. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_compress_html.py +0 -0
  215. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_compress_insights.py +0 -0
  216. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_config.py +0 -0
  217. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_conversation.py +0 -0
  218. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_copy_flag.py +0 -0
  219. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_cost.py +0 -0
  220. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_coverage_boost.py +0 -0
  221. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_cursor_adapter.py +0 -0
  222. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_dashboard.py +0 -0
  223. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_db.py +0 -0
  224. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_db_digest.py +0 -0
  225. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_db_effectiveness.py +0 -0
  226. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_db_session_quality.py +0 -0
  227. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_db_trends.py +0 -0
  228. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_dedup.py +0 -0
  229. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_demo.py +0 -0
  230. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_deprecated_commands.py +0 -0
  231. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_digest.py +0 -0
  232. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_digest_cli.py +0 -0
  233. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_distill.py +0 -0
  234. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_distill_cli.py +0 -0
  235. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_distill_weights.py +0 -0
  236. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_e2e.py +0 -0
  237. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_effectiveness.py +0 -0
  238. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_embeddings_local.py +0 -0
  239. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_embeddings_ollama.py +0 -0
  240. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_embeddings_openai.py +0 -0
  241. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_empty_state.py +0 -0
  242. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_export.py +0 -0
  243. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_export_cli.py +0 -0
  244. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_export_snapshot.py +0 -0
  245. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_extractors.py +0 -0
  246. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_extractors_routing.py +0 -0
  247. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_extractors_zh.py +0 -0
  248. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_extractors_zh_e2e.py +0 -0
  249. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_html_report.py +0 -0
  250. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_import_cli.py +0 -0
  251. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_import_e2e.py +0 -0
  252. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_init_cli.py +0 -0
  253. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_insights.py +0 -0
  254. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_insights_cli.py +0 -0
  255. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_insights_expanded.py +0 -0
  256. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_install_hook.py +0 -0
  257. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_lang_detect.py +0 -0
  258. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_library.py +0 -0
  259. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_lint.py +0 -0
  260. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_lint_cli.py +0 -0
  261. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_markdown.py +0 -0
  262. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_merge_view.py +0 -0
  263. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_models.py +0 -0
  264. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_output.py +0 -0
  265. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_parse_conversation_base.py +0 -0
  266. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_parse_conversation_chatgpt.py +0 -0
  267. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_parse_conversation_claude.py +0 -0
  268. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_persona.py +0 -0
  269. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_pipeline.py +0 -0
  270. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_privacy.py +0 -0
  271. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_privacy_cli.py +0 -0
  272. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_privacy_e2e.py +0 -0
  273. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_privacy_output.py +0 -0
  274. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_privacy_scan.py +0 -0
  275. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_projects.py +0 -0
  276. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_prompt_dna.py +0 -0
  277. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_public_api.py +0 -0
  278. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_recommend.py +0 -0
  279. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_repetition.py +0 -0
  280. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_repetition_cli.py +0 -0
  281. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_repetition_output.py +0 -0
  282. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_rewrite.py +0 -0
  283. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_schema_version.py +0 -0
  284. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_score_cli.py +0 -0
  285. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_scorer.py +0 -0
  286. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_segmenter.py +0 -0
  287. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_session_quality.py +0 -0
  288. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_session_type.py +0 -0
  289. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_sessions_cli.py +0 -0
  290. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_sessions_output.py +0 -0
  291. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_share_e2e.py +0 -0
  292. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_sharing_client.py +0 -0
  293. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_source_filter.py +0 -0
  294. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_style.py +0 -0
  295. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_style_trends.py +0 -0
  296. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_suggestions.py +0 -0
  297. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_cli.py +0 -0
  298. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_collector.py +0 -0
  299. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_consent.py +0 -0
  300. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_e2e.py +0 -0
  301. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_events.py +0 -0
  302. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_prompt.py +0 -0
  303. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_queue.py +0 -0
  304. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_telemetry_sender.py +0 -0
  305. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_template_cli.py +0 -0
  306. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_templates.py +0 -0
  307. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_timeutil.py +0 -0
  308. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_trends.py +0 -0
  309. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_trends_cli.py +0 -0
  310. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_use_cli.py +0 -0
  311. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_wrapped.py +0 -0
  312. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_wrapped_cli.py +0 -0
  313. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_wrapped_e2e.py +0 -0
  314. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_wrapped_html.py +0 -0
  315. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_wrapped_output.py +0 -0
  316. {reprompt_cli-2.1.0 → reprompt_cli-2.2.1}/tests/test_wrapped_share.py +0 -0
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.2.1] - 2026-04-01
6
+
7
+ ### Added
8
+ - **MCP server expanded** — 6→9 tools: `check_prompt_quality` (unified diagnostic), `build_prompt_from_parts` (prompt constructor), `explain_prompt_quality` (educational analysis). IDE integration now covers all prompt quality features.
9
+ - **File input** — all prompt commands (`check`, `score`, `explain`, `rewrite`, `compress`) accept `--file` to read from file and `-` for stdin. Enables pipeline integration and multi-line prompts.
10
+ - Tests: 1846 → 1864
11
+
12
+ ## [2.2.0] - 2026-04-01
13
+
14
+ ### Added
15
+ - **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.
16
+ - **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.
17
+ - **Prompt explainer** — `reprompt explain "prompt"` explains what makes a prompt good or bad in plain English. Educational feedback with research-backed insights per dimension.
18
+ - Tests: 1741 → 1846
19
+
5
20
  ## [2.1.0] - 2026-04-01
6
21
 
7
22
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reprompt-cli
3
- Version: 2.1.0
3
+ Version: 2.2.1
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.1.0
233
+ rev: v2.2.1
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.1.0
188
+ rev: v2.2.1
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.1.0"
3
+ version = "2.2.1"
4
4
  description = "Discover, analyze, and optimize your prompts from AI coding sessions"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -15,6 +15,21 @@ if TYPE_CHECKING:
15
15
  from reprompt.storage.db import PromptDB
16
16
 
17
17
 
18
+ def _resolve_text(text: str, file: str) -> str:
19
+ """Resolve prompt text from argument or --file option."""
20
+ if file:
21
+ p = Path(file)
22
+ if not p.is_file():
23
+ typer.echo(f"Error: file not found: {file}", err=True)
24
+ raise typer.Exit(1)
25
+ return p.read_text(encoding="utf-8").strip()
26
+ if text == "-":
27
+ import sys
28
+
29
+ return sys.stdin.read().strip()
30
+ return text
31
+
32
+
18
33
  def _copy_to_clip(text: str, quiet: bool = False) -> None:
19
34
  """Copy text to clipboard with user feedback."""
20
35
  from reprompt.sharing.clipboard import copy_to_clipboard
@@ -946,9 +961,119 @@ def lint(
946
961
  raise typer.Exit(1)
947
962
 
948
963
 
964
+ @app.command(rich_help_panel="Analyze")
965
+ def check(
966
+ text: str = typer.Argument(..., help="Prompt text to check (use '-' for stdin)"),
967
+ model: str = typer.Option("", "--model", "-m", help="Target model (claude/gpt/gemini)"),
968
+ max_tokens: int = typer.Option(0, "--max-tokens", help="Token budget (0 = disabled)"),
969
+ file: str = typer.Option("", "--file", "-f", help="Read prompt from file"),
970
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
971
+ copy: bool = typer.Option(False, "--copy", help="Copy rewritten prompt to clipboard"),
972
+ ) -> None:
973
+ """Full prompt diagnostic — score + lint + rewrite in one command.
974
+
975
+ Runs all quality checks and shows a unified report with score breakdown,
976
+ strengths, suggestions, lint issues, and auto-rewrite preview.
977
+
978
+ Examples:
979
+
980
+ reprompt check "fix the auth bug in login.ts"
981
+
982
+ reprompt check "refactor the middleware" --model claude
983
+
984
+ reprompt check "help me debug this crash" --json
985
+ """
986
+ text = _resolve_text(text, file)
987
+ from reprompt.core.check import check_prompt
988
+
989
+ result = check_prompt(text, model=model, max_tokens=max_tokens)
990
+
991
+ if json_output:
992
+ import json as json_mod
993
+
994
+ data = {
995
+ "total": result.total,
996
+ "tier": result.tier,
997
+ "clarity": result.clarity,
998
+ "context": result.context,
999
+ "position": result.position,
1000
+ "structure": result.structure,
1001
+ "repetition": result.repetition,
1002
+ "word_count": result.word_count,
1003
+ "token_count": result.token_count,
1004
+ "confirmations": result.confirmations,
1005
+ "suggestions": result.suggestions,
1006
+ "lint_issues": result.lint_issues,
1007
+ "rewritten": result.rewritten,
1008
+ "rewrite_delta": result.rewrite_delta,
1009
+ "rewrite_changes": result.rewrite_changes,
1010
+ }
1011
+ typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
1012
+ else:
1013
+ from reprompt.output.check_terminal import render_check
1014
+
1015
+ typer.echo(render_check(result))
1016
+
1017
+ if copy:
1018
+ _copy_to_clip(result.rewritten, quiet=json_output)
1019
+
1020
+ from reprompt.core.suggestions import get_suggestion
1021
+
1022
+ hint = get_suggestion("check")
1023
+ if hint and not json_output:
1024
+ console.print(f" [dim]→ Try: {hint}[/dim]\n")
1025
+
1026
+
1027
+ @app.command(rich_help_panel="Analyze")
1028
+ def explain(
1029
+ text: str = typer.Argument(..., help="Prompt text to explain (use '-' for stdin)"),
1030
+ file: str = typer.Option("", "--file", "-f", help="Read prompt from file"),
1031
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
1032
+ ) -> None:
1033
+ """Explain what makes a prompt good or bad in plain English.
1034
+
1035
+ Analyzes the prompt and provides educational feedback: what's working,
1036
+ what's missing, and specific tips to improve. No LLM needed.
1037
+
1038
+ Examples:
1039
+
1040
+ reprompt explain "fix the auth bug"
1041
+
1042
+ reprompt explain --file prompt.txt --json
1043
+ """
1044
+ text = _resolve_text(text, file)
1045
+ from reprompt.core.explain import explain_prompt
1046
+
1047
+ result = explain_prompt(text)
1048
+
1049
+ if json_output:
1050
+ import json as json_mod
1051
+
1052
+ data = {
1053
+ "score": result.score,
1054
+ "tier": result.tier,
1055
+ "summary": result.summary,
1056
+ "strengths": result.strengths,
1057
+ "weaknesses": result.weaknesses,
1058
+ "tips": result.tips,
1059
+ }
1060
+ typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
1061
+ else:
1062
+ from reprompt.output.explain_terminal import render_explain
1063
+
1064
+ typer.echo(render_explain(result))
1065
+
1066
+ from reprompt.core.suggestions import get_suggestion
1067
+
1068
+ hint = get_suggestion("explain")
1069
+ if hint and not json_output:
1070
+ console.print(f" [dim]→ Try: {hint}[/dim]\n")
1071
+
1072
+
949
1073
  @app.command(rich_help_panel="Analyze")
950
1074
  def score(
951
- text: str = typer.Argument(..., help="Prompt text to score"),
1075
+ text: str = typer.Argument(..., help="Prompt text to score (use '-' for stdin)"),
1076
+ file: str = typer.Option("", "--file", "-f", help="Read prompt from file"),
952
1077
  json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
953
1078
  copy: bool = typer.Option(False, "--copy", help="Copy result to clipboard"),
954
1079
  ) -> None:
@@ -958,10 +1083,11 @@ def score(
958
1083
 
959
1084
  reprompt score "Fix the auth bug in login.ts where JWT expires"
960
1085
 
961
- reprompt score "Refactor auth module to use refresh tokens" --json
1086
+ reprompt score --file prompt.txt --json
962
1087
 
963
1088
  reprompt score "Fix bug" --copy
964
1089
  """
1090
+ text = _resolve_text(text, file)
965
1091
  from reprompt.core.cost import estimate_cost, format_cost, model_for_source
966
1092
  from reprompt.core.extractors import extract_features
967
1093
  from reprompt.core.scorer import score_prompt
@@ -1069,7 +1195,8 @@ def score(
1069
1195
 
1070
1196
  @app.command(rich_help_panel="Optimize")
1071
1197
  def compress(
1072
- text: str = typer.Argument(..., help="Prompt text to compress"),
1198
+ text: str = typer.Argument(..., help="Prompt text to compress (use '-' for stdin)"),
1199
+ file: str = typer.Option("", "--file", "-f", help="Read prompt from file"),
1073
1200
  json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
1074
1201
  copy: bool = typer.Option(False, "--copy", help="Copy compressed text to clipboard"),
1075
1202
  ) -> None:
@@ -1079,10 +1206,11 @@ def compress(
1079
1206
 
1080
1207
  reprompt compress "Can you please help me refactor this code?"
1081
1208
 
1082
- reprompt compress "I was wondering if you could fix the bug" --json
1209
+ reprompt compress --file prompt.txt --json
1083
1210
 
1084
1211
  reprompt compress "verbose prompt here" --copy
1085
1212
  """
1213
+ text = _resolve_text(text, file)
1086
1214
  from reprompt.core.compress import compress_text
1087
1215
 
1088
1216
  result = compress_text(text)
@@ -1103,7 +1231,8 @@ def compress(
1103
1231
 
1104
1232
  @app.command(rich_help_panel="Optimize")
1105
1233
  def rewrite(
1106
- text: str = typer.Argument(..., help="Prompt text to improve"),
1234
+ text: str = typer.Argument(..., help="Prompt text to improve (use '-' for stdin)"),
1235
+ file: str = typer.Option("", "--file", "-f", help="Read prompt from file"),
1107
1236
  json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
1108
1237
  diff: bool = typer.Option(False, "--diff", help="Show unified diff (red/green)"),
1109
1238
  copy: bool = typer.Option(False, "--copy", help="Copy rewritten text to clipboard"),
@@ -1118,10 +1247,11 @@ def rewrite(
1118
1247
 
1119
1248
  reprompt rewrite "I was wondering if you could fix the authentication bug"
1120
1249
 
1121
- reprompt rewrite "fix the login" --diff
1250
+ reprompt rewrite --file prompt.txt --diff
1122
1251
 
1123
1252
  reprompt rewrite "please help me refactor this code to be better" --copy
1124
1253
  """
1254
+ text = _resolve_text(text, file)
1125
1255
  from reprompt.core.rewrite import rewrite_prompt
1126
1256
 
1127
1257
  result = rewrite_prompt(text)
@@ -1158,6 +1288,73 @@ def rewrite(
1158
1288
  console.print(f" [dim]→ Try: {hint}[/dim]\n")
1159
1289
 
1160
1290
 
1291
+ @app.command(rich_help_panel="Optimize")
1292
+ def build(
1293
+ task: str = typer.Argument(..., help="What the AI should do"),
1294
+ context: str = typer.Option("", "--context", "-c", help="Background information"),
1295
+ file: list[str] = typer.Option([], "--file", "-f", help="File references (repeatable)"),
1296
+ error: str = typer.Option("", "--error", "-e", help="Error message or stack trace"),
1297
+ constraint: list[str] = typer.Option([], "--constraint", help="Constraints (repeatable)"),
1298
+ example: str = typer.Option("", "--example", help="Example input/output"),
1299
+ output_format: str = typer.Option("", "--output-format", help="Expected response format"),
1300
+ role: str = typer.Option("", "--role", "-r", help="AI role/persona"),
1301
+ model: str = typer.Option("", "--model", "-m", help="Target model (claude/gpt/gemini)"),
1302
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
1303
+ copy: bool = typer.Option(False, "--copy", help="Copy built prompt to clipboard"),
1304
+ ) -> None:
1305
+ """Build a well-structured prompt from components.
1306
+
1307
+ Assembles a prompt that maximizes quality score by combining
1308
+ your task with context, files, errors, and constraints.
1309
+
1310
+ Examples:
1311
+
1312
+ reprompt build "fix the auth bug"
1313
+
1314
+ reprompt build "fix the crash" --file src/auth.ts --error "TypeError: ..."
1315
+
1316
+ reprompt build "refactor" -f src/app.py --constraint "keep tests" --model claude
1317
+ """
1318
+ from reprompt.core.build import build_prompt
1319
+
1320
+ result = build_prompt(
1321
+ task,
1322
+ context=context,
1323
+ files=file if file else None,
1324
+ error=error,
1325
+ constraints=constraint if constraint else None,
1326
+ examples=example,
1327
+ output_format=output_format,
1328
+ role=role,
1329
+ model=model,
1330
+ )
1331
+
1332
+ if json_output:
1333
+ import json as json_mod
1334
+
1335
+ data = {
1336
+ "prompt": result.prompt,
1337
+ "score": result.score,
1338
+ "tier": result.tier,
1339
+ "components_used": result.components_used,
1340
+ "suggestions": result.suggestions,
1341
+ }
1342
+ typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
1343
+ else:
1344
+ from reprompt.output.build_terminal import render_build
1345
+
1346
+ typer.echo(render_build(result))
1347
+
1348
+ if copy:
1349
+ _copy_to_clip(result.prompt, quiet=json_output)
1350
+
1351
+ from reprompt.core.suggestions import get_suggestion
1352
+
1353
+ hint = get_suggestion("build")
1354
+ if hint and not json_output:
1355
+ console.print(f" [dim]→ Try: {hint}[/dim]\n")
1356
+
1357
+
1161
1358
  @app.command(rich_help_panel="Optimize")
1162
1359
  def distill(
1163
1360
  session_id: str = typer.Argument(None, help="Session ID to distill"),
@@ -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