reprompt-cli 1.10.0__tar.gz → 2.0.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 (302) hide show
  1. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/PKG-INFO +29 -15
  2. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/README.md +28 -14
  3. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/pyproject.toml +1 -1
  4. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/__init__.py +1 -1
  5. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/cli.py +51 -0
  6. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/suggestions.py +1 -0
  7. reprompt_cli-2.0.1/src/reprompt/output/projects_terminal.py +101 -0
  8. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/storage/db.py +44 -0
  9. reprompt_cli-2.0.1/tests/test_projects.py +200 -0
  10. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_suggestions.py +1 -0
  11. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/uv.lock +1 -1
  12. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.editorconfig +0 -0
  13. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  15. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  16. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  17. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  18. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/dependabot.yml +0 -0
  19. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/workflows/ci.yml +0 -0
  20. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.github/workflows/publish.yml +0 -0
  21. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.gitignore +0 -0
  22. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.pre-commit-config.yaml +0 -0
  23. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.pre-commit-hooks.yaml +0 -0
  24. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.testmondata-shm +0 -0
  25. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/.testmondata-wal +0 -0
  26. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/CHANGELOG.md +0 -0
  27. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/CODE_OF_CONDUCT.md +0 -0
  28. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/CONTRIBUTING.md +0 -0
  29. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/LICENSE +0 -0
  30. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/SECURITY.md +0 -0
  31. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/Screenshot 2026-03-24 at 09.45.03.png +0 -0
  32. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/action.yml +0 -0
  33. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/demo.gif +0 -0
  34. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-128.png +0 -0
  35. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-16.png +0 -0
  36. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-256.png +0 -0
  37. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-32.png +0 -0
  38. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-48.png +0 -0
  39. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-512.png +0 -0
  40. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-96.png +0 -0
  41. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon.svg +0 -0
  42. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-128.png +0 -0
  43. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-16.png +0 -0
  44. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-256.png +0 -0
  45. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-32.png +0 -0
  46. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-48.png +0 -0
  47. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-512.png +0 -0
  48. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-96.png +0 -0
  49. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon.svg +0 -0
  50. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-128.png +0 -0
  51. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-16.png +0 -0
  52. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-256.png +0 -0
  53. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-32.png +0 -0
  54. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-48.png +0 -0
  55. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-512.png +0 -0
  56. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-96.png +0 -0
  57. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon.svg +0 -0
  58. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon-128.png +0 -0
  59. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon-16.png +0 -0
  60. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon-256.png +0 -0
  61. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon-32.png +0 -0
  62. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon-48.png +0 -0
  63. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon-512.png +0 -0
  64. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon-96.png +0 -0
  65. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/favicon.svg +0 -0
  66. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/icons/generate.sh +0 -0
  67. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/superpowers/specs/2026-03-24-v14-command-consolidation-design.md +0 -0
  68. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/docs/superpowers/specs/2026-03-25-v1.5-dashboard-design.md +0 -0
  69. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/scripts/generate_demo_data.py +0 -0
  70. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/__init__.py +0 -0
  71. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/aider.py +0 -0
  72. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/base.py +0 -0
  73. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/chatgpt.py +0 -0
  74. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/claude_chat.py +0 -0
  75. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/claude_code.py +0 -0
  76. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/cline.py +0 -0
  77. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/codex.py +0 -0
  78. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/cursor.py +0 -0
  79. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/filters.py +0 -0
  80. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/gemini.py +0 -0
  81. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/openclaw.py +0 -0
  82. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/__init__.py +0 -0
  83. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/handler.py +0 -0
  84. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/host.py +0 -0
  85. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/manifest.py +0 -0
  86. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/protocol.py +0 -0
  87. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/commands/__init__.py +0 -0
  88. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/commands/telemetry.py +0 -0
  89. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/commands/wrapped.py +0 -0
  90. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/config.py +0 -0
  91. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/__init__.py +0 -0
  92. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/agent.py +0 -0
  93. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/analyzer.py +0 -0
  94. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/compress.py +0 -0
  95. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/conversation.py +0 -0
  96. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/cost.py +0 -0
  97. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/dashboard.py +0 -0
  98. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/dedup.py +0 -0
  99. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/digest.py +0 -0
  100. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/distill.py +0 -0
  101. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/effectiveness.py +0 -0
  102. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/extractors.py +0 -0
  103. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/extractors_zh.py +0 -0
  104. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/insights.py +0 -0
  105. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/lang_detect.py +0 -0
  106. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/library.py +0 -0
  107. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/lint.py +0 -0
  108. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/merge_view.py +0 -0
  109. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/models.py +0 -0
  110. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/persona.py +0 -0
  111. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/pipeline.py +0 -0
  112. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/privacy.py +0 -0
  113. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/privacy_scan.py +0 -0
  114. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/prompt_dna.py +0 -0
  115. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/recommend.py +0 -0
  116. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/repetition.py +0 -0
  117. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/rewrite.py +0 -0
  118. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/scorer.py +0 -0
  119. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/segmenter.py +0 -0
  120. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/session_meta.py +0 -0
  121. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/session_quality.py +0 -0
  122. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/session_type.py +0 -0
  123. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/style.py +0 -0
  124. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/templates.py +0 -0
  125. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/timeutil.py +0 -0
  126. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/trends.py +0 -0
  127. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/core/wrapped.py +0 -0
  128. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/demo.py +0 -0
  129. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/__init__.py +0 -0
  130. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/base.py +0 -0
  131. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/local_embed.py +0 -0
  132. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/ollama.py +0 -0
  133. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/openai_embed.py +0 -0
  134. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/tfidf.py +0 -0
  135. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/mcp.py +0 -0
  136. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/mcp_main.py +0 -0
  137. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/__init__.py +0 -0
  138. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/agent_terminal.py +0 -0
  139. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/chartjs.min.js +0 -0
  140. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/compress_terminal.py +0 -0
  141. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/dashboard_terminal.py +0 -0
  142. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/distill_terminal.py +0 -0
  143. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/export.py +0 -0
  144. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/html_report.py +0 -0
  145. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/json_out.py +0 -0
  146. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/markdown.py +0 -0
  147. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/repetition_terminal.py +0 -0
  148. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/rewrite_terminal.py +0 -0
  149. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/sessions_terminal.py +0 -0
  150. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/terminal.py +0 -0
  151. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/wrapped_html.py +0 -0
  152. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/output/wrapped_terminal.py +0 -0
  153. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/py.typed +0 -0
  154. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/sharing/__init__.py +0 -0
  155. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/sharing/client.py +0 -0
  156. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/sharing/clipboard.py +0 -0
  157. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/storage/__init__.py +0 -0
  158. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/__init__.py +0 -0
  159. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/collector.py +0 -0
  160. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/consent.py +0 -0
  161. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/events.py +0 -0
  162. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/prompt.py +0 -0
  163. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/queue.py +0 -0
  164. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/sender.py +0 -0
  165. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/__init__.py +0 -0
  166. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/conftest.py +0 -0
  167. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/aider_chat_history.md +0 -0
  168. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/chatgpt_conversations.json +0 -0
  169. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/claude_chat_export.json +0 -0
  170. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/claude_session.jsonl +0 -0
  171. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/cline_task/api_conversation_history.json +0 -0
  172. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/export/default_export.md +0 -0
  173. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/export/full_export.md +0 -0
  174. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/gemini_session.json +0 -0
  175. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/fixtures/openclaw_session.jsonl +0 -0
  176. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_adapter_aider.py +0 -0
  177. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_adapter_chatgpt.py +0 -0
  178. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_adapter_claude.py +0 -0
  179. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_adapter_claude_chat.py +0 -0
  180. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_adapter_cline.py +0 -0
  181. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_adapter_gemini.py +0 -0
  182. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_adapter_openclaw.py +0 -0
  183. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_agent.py +0 -0
  184. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_agent_cli.py +0 -0
  185. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_analyzer.py +0 -0
  186. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_bridge_cli.py +0 -0
  187. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_bridge_e2e.py +0 -0
  188. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_bridge_handler.py +0 -0
  189. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_bridge_integration.py +0 -0
  190. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_bridge_manifest.py +0 -0
  191. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_bridge_protocol.py +0 -0
  192. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_cli.py +0 -0
  193. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_cli_deprecations.py +0 -0
  194. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_cli_library_effectiveness.py +0 -0
  195. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_clipboard.py +0 -0
  196. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_codex_adapter.py +0 -0
  197. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_compare_best_worst.py +0 -0
  198. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_compress.py +0 -0
  199. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_compress_cli.py +0 -0
  200. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_compress_dna.py +0 -0
  201. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_compress_html.py +0 -0
  202. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_compress_insights.py +0 -0
  203. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_config.py +0 -0
  204. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_conversation.py +0 -0
  205. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_copy_flag.py +0 -0
  206. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_cost.py +0 -0
  207. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_coverage_boost.py +0 -0
  208. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_cursor_adapter.py +0 -0
  209. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_dashboard.py +0 -0
  210. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_db.py +0 -0
  211. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_db_digest.py +0 -0
  212. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_db_effectiveness.py +0 -0
  213. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_db_session_quality.py +0 -0
  214. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_db_trends.py +0 -0
  215. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_dedup.py +0 -0
  216. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_demo.py +0 -0
  217. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_deprecated_commands.py +0 -0
  218. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_digest.py +0 -0
  219. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_digest_cli.py +0 -0
  220. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_distill.py +0 -0
  221. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_distill_cli.py +0 -0
  222. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_distill_weights.py +0 -0
  223. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_e2e.py +0 -0
  224. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_effectiveness.py +0 -0
  225. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_embeddings_local.py +0 -0
  226. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_embeddings_ollama.py +0 -0
  227. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_embeddings_openai.py +0 -0
  228. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_empty_state.py +0 -0
  229. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_export.py +0 -0
  230. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_export_cli.py +0 -0
  231. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_export_snapshot.py +0 -0
  232. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_extractors.py +0 -0
  233. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_extractors_routing.py +0 -0
  234. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_extractors_zh.py +0 -0
  235. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_extractors_zh_e2e.py +0 -0
  236. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_html_report.py +0 -0
  237. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_import_cli.py +0 -0
  238. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_import_e2e.py +0 -0
  239. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_init_cli.py +0 -0
  240. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_insights.py +0 -0
  241. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_insights_cli.py +0 -0
  242. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_insights_expanded.py +0 -0
  243. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_install_hook.py +0 -0
  244. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_lang_detect.py +0 -0
  245. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_library.py +0 -0
  246. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_lint.py +0 -0
  247. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_lint_cli.py +0 -0
  248. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_markdown.py +0 -0
  249. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_mcp.py +0 -0
  250. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_merge_view.py +0 -0
  251. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_models.py +0 -0
  252. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_output.py +0 -0
  253. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_parse_conversation_base.py +0 -0
  254. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_parse_conversation_chatgpt.py +0 -0
  255. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_parse_conversation_claude.py +0 -0
  256. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_persona.py +0 -0
  257. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_pipeline.py +0 -0
  258. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_privacy.py +0 -0
  259. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_privacy_cli.py +0 -0
  260. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_privacy_e2e.py +0 -0
  261. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_privacy_output.py +0 -0
  262. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_privacy_scan.py +0 -0
  263. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_prompt_dna.py +0 -0
  264. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_public_api.py +0 -0
  265. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_recommend.py +0 -0
  266. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_repetition.py +0 -0
  267. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_repetition_cli.py +0 -0
  268. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_repetition_output.py +0 -0
  269. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_rewrite.py +0 -0
  270. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_schema_version.py +0 -0
  271. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_score_cli.py +0 -0
  272. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_scorer.py +0 -0
  273. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_segmenter.py +0 -0
  274. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_session_quality.py +0 -0
  275. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_session_type.py +0 -0
  276. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_sessions_cli.py +0 -0
  277. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_sessions_output.py +0 -0
  278. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_share_e2e.py +0 -0
  279. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_sharing_client.py +0 -0
  280. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_source_filter.py +0 -0
  281. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_style.py +0 -0
  282. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_style_trends.py +0 -0
  283. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_cli.py +0 -0
  284. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_collector.py +0 -0
  285. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_consent.py +0 -0
  286. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_e2e.py +0 -0
  287. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_events.py +0 -0
  288. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_prompt.py +0 -0
  289. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_queue.py +0 -0
  290. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_telemetry_sender.py +0 -0
  291. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_template_cli.py +0 -0
  292. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_templates.py +0 -0
  293. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_timeutil.py +0 -0
  294. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_trends.py +0 -0
  295. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_trends_cli.py +0 -0
  296. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_use_cli.py +0 -0
  297. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_wrapped.py +0 -0
  298. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_wrapped_cli.py +0 -0
  299. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_wrapped_e2e.py +0 -0
  300. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_wrapped_html.py +0 -0
  301. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_wrapped_output.py +0 -0
  302. {reprompt_cli-1.10.0 → reprompt_cli-2.0.1}/tests/test_wrapped_share.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reprompt-cli
3
- Version: 1.10.0
3
+ Version: 2.0.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
@@ -45,7 +45,7 @@ Description-Content-Type: text/markdown
45
45
 
46
46
  # `re:prompt`
47
47
 
48
- **Analyze what you type into AI tools** -- prompt scoring, agent error loops, leaked credential detection, conversation distillation.
48
+ **Score, rewrite, and optimize your AI prompts** -- the only CLI that improves your prompts automatically. No LLM needed.
49
49
 
50
50
  [![PyPI version](https://img.shields.io/pypi/v/reprompt-cli)](https://pypi.org/project/reprompt-cli/)
51
51
  [![Python 3.10+](https://img.shields.io/pypi/pyversions/reprompt-cli)](https://pypi.org/project/reprompt-cli/)
@@ -61,26 +61,39 @@ Description-Content-Type: text/markdown
61
61
 
62
62
  ```bash
63
63
  $ pip install reprompt-cli
64
- $ reprompt
65
- ╭─ Prompt Dashboard ─────────────────────────────────────────╮
66
- │ Prompts: 1,063 (295 unique) Sessions: 890 │
67
- │ Avg Score: 68/100 Top: debug (31%), impl (24%)│
68
- │ Sources: claude-code, cursor, chatgpt │
64
+
65
+ # Rewrite a weak prompt into a better one (no LLM, rule-based)
66
+ $ reprompt rewrite "I was wondering if you could maybe help me fix the auth bug"
67
+ 34 52 (+18)
68
+
69
+ ╭─ Rewritten ────────────────────────────────────────────────╮
70
+ │ Help me fix the auth bug. │
69
71
  ╰────────────────────────────────────────────────────────────╯
70
72
 
73
+ Changes
74
+ ✓ Removed filler (24% shorter)
75
+ ✓ Removed hedging language
76
+
77
+ You should also
78
+ → Add actual code snippets or error messages for context
79
+ → Reference specific files or functions by name
80
+ → Add constraints (e.g., "Do not modify existing tests")
81
+
82
+ # Score any prompt instantly (research-backed, 30+ features)
71
83
  $ reprompt score "Fix the auth bug in src/login.ts where JWT expires"
72
84
  Score: 40/100 (Fair)
73
- Structure: 0/25 | Context: 8/25 | Position: 20/20 | Repetition: 0/15 | Clarity: 12/15
74
85
  Tip: Include the error message -- debug prompts with errors are 3.7x more effective
75
86
 
76
- $ reprompt distill --last 3 --summary
77
- Session: feature-dev (42 turns, 18 important)
78
- Key moments: initial spec → auth module → test failures → JWT fix → passing
79
- Context: "Building auth system with JWT refresh tokens for Express API"
80
-
81
- $ reprompt compress "I was wondering if you could please help me refactor this code. Basically what I need is to split this function into smaller helpers and add error handling."
87
+ # Compress prompts to save tokens
88
+ $ reprompt compress "I was wondering if you could please help me refactor this code. Basically what I need is to split this function into smaller helpers."
82
89
  Before: 28 tokens → After: 14 tokens (50% saved)
83
- "Help me refactor this code. Split this function into smaller helpers and add error handling."
90
+
91
+ # Your personal dashboard
92
+ $ reprompt
93
+ ╭─ Prompt Dashboard ─────────────────────────────────────────╮
94
+ │ Prompts: 1,063 (295 unique) Sessions: 890 │
95
+ │ Avg Score: 68/100 Top: debug (31%), impl (24%)│
96
+ ╰────────────────────────────────────────────────────────────╯
84
97
  ```
85
98
 
86
99
  ## What it does
@@ -103,6 +116,7 @@ $ reprompt compress "I was wondering if you could please help me refactor this c
103
116
 
104
117
  | Command | Description |
105
118
  |---------|-------------|
119
+ | `reprompt rewrite "prompt"` | **Rewrite prompts to score higher** -- filler removal, restructuring, hedging cleanup |
106
120
  | `reprompt compress "prompt"` | 4-layer prompt compression (40-60% token savings typical) |
107
121
  | `reprompt distill` | Extract important turns from conversations with 6-signal scoring |
108
122
  | `reprompt distill --export` | Recover context when a session runs out -- paste into new session |
@@ -1,6 +1,6 @@
1
1
  # `re:prompt`
2
2
 
3
- **Analyze what you type into AI tools** -- prompt scoring, agent error loops, leaked credential detection, conversation distillation.
3
+ **Score, rewrite, and optimize your AI prompts** -- the only CLI that improves your prompts automatically. No LLM needed.
4
4
 
5
5
  [![PyPI version](https://img.shields.io/pypi/v/reprompt-cli)](https://pypi.org/project/reprompt-cli/)
6
6
  [![Python 3.10+](https://img.shields.io/pypi/pyversions/reprompt-cli)](https://pypi.org/project/reprompt-cli/)
@@ -16,26 +16,39 @@
16
16
 
17
17
  ```bash
18
18
  $ pip install reprompt-cli
19
- $ reprompt
20
- ╭─ Prompt Dashboard ─────────────────────────────────────────╮
21
- │ Prompts: 1,063 (295 unique) Sessions: 890 │
22
- │ Avg Score: 68/100 Top: debug (31%), impl (24%)│
23
- │ Sources: claude-code, cursor, chatgpt │
19
+
20
+ # Rewrite a weak prompt into a better one (no LLM, rule-based)
21
+ $ reprompt rewrite "I was wondering if you could maybe help me fix the auth bug"
22
+ 34 52 (+18)
23
+
24
+ ╭─ Rewritten ────────────────────────────────────────────────╮
25
+ │ Help me fix the auth bug. │
24
26
  ╰────────────────────────────────────────────────────────────╯
25
27
 
28
+ Changes
29
+ ✓ Removed filler (24% shorter)
30
+ ✓ Removed hedging language
31
+
32
+ You should also
33
+ → Add actual code snippets or error messages for context
34
+ → Reference specific files or functions by name
35
+ → Add constraints (e.g., "Do not modify existing tests")
36
+
37
+ # Score any prompt instantly (research-backed, 30+ features)
26
38
  $ reprompt score "Fix the auth bug in src/login.ts where JWT expires"
27
39
  Score: 40/100 (Fair)
28
- Structure: 0/25 | Context: 8/25 | Position: 20/20 | Repetition: 0/15 | Clarity: 12/15
29
40
  Tip: Include the error message -- debug prompts with errors are 3.7x more effective
30
41
 
31
- $ reprompt distill --last 3 --summary
32
- Session: feature-dev (42 turns, 18 important)
33
- Key moments: initial spec → auth module → test failures → JWT fix → passing
34
- Context: "Building auth system with JWT refresh tokens for Express API"
35
-
36
- $ reprompt compress "I was wondering if you could please help me refactor this code. Basically what I need is to split this function into smaller helpers and add error handling."
42
+ # Compress prompts to save tokens
43
+ $ reprompt compress "I was wondering if you could please help me refactor this code. Basically what I need is to split this function into smaller helpers."
37
44
  Before: 28 tokens → After: 14 tokens (50% saved)
38
- "Help me refactor this code. Split this function into smaller helpers and add error handling."
45
+
46
+ # Your personal dashboard
47
+ $ reprompt
48
+ ╭─ Prompt Dashboard ─────────────────────────────────────────╮
49
+ │ Prompts: 1,063 (295 unique) Sessions: 890 │
50
+ │ Avg Score: 68/100 Top: debug (31%), impl (24%)│
51
+ ╰────────────────────────────────────────────────────────────╯
39
52
  ```
40
53
 
41
54
  ## What it does
@@ -58,6 +71,7 @@ $ reprompt compress "I was wondering if you could please help me refactor this c
58
71
 
59
72
  | Command | Description |
60
73
  |---------|-------------|
74
+ | `reprompt rewrite "prompt"` | **Rewrite prompts to score higher** -- filler removal, restructuring, hedging cleanup |
61
75
  | `reprompt compress "prompt"` | 4-layer prompt compression (40-60% token savings typical) |
62
76
  | `reprompt distill` | Extract important turns from conversations with 6-signal scoring |
63
77
  | `reprompt distill --export` | Recover context when a session runs out -- paste into new session |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "reprompt-cli"
3
- version = "1.10.0"
3
+ version = "2.0.1"
4
4
  description = "Discover, analyze, and optimize your prompts from AI coding sessions"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -1,6 +1,6 @@
1
1
  """reprompt — Discover, analyze, and evolve your best prompts from AI coding sessions."""
2
2
 
3
- __version__ = "1.10.0"
3
+ __version__ = "2.0.1"
4
4
 
5
5
  __all__ = [
6
6
  "__version__",
@@ -2046,6 +2046,57 @@ debug-needs-reference = true
2046
2046
  console.print(" Edit rules, then run [bold]reprompt lint[/bold] to verify.")
2047
2047
 
2048
2048
 
2049
+ @app.command(rich_help_panel="Analyze")
2050
+ def projects(
2051
+ source: str = typer.Option(
2052
+ None, "--source", "-s", help="Filter by source (e.g. claude-code, cursor)"
2053
+ ),
2054
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
2055
+ copy: bool = typer.Option(False, "--copy", help="Copy result to clipboard"),
2056
+ ) -> None:
2057
+ """Compare prompt quality across projects.
2058
+
2059
+ Shows per-project breakdown: sessions, prompts, quality scores,
2060
+ efficiency, focus, and frustration signals.
2061
+
2062
+ Examples:
2063
+
2064
+ reprompt projects # all projects
2065
+
2066
+ reprompt projects --source claude-code # filter by source
2067
+
2068
+ reprompt projects --json # machine-readable
2069
+ """
2070
+ import json as json_mod
2071
+
2072
+ from reprompt.config import Settings
2073
+ from reprompt.output.projects_terminal import render_projects_table
2074
+ from reprompt.storage.db import PromptDB
2075
+
2076
+ settings = Settings()
2077
+ db = PromptDB(settings.db_path)
2078
+
2079
+ project_data = db.get_project_summary(source=source)
2080
+
2081
+ if json_output:
2082
+ print(json_mod.dumps(project_data, indent=2, default=str))
2083
+ else:
2084
+ output = render_projects_table(project_data)
2085
+ print(output)
2086
+
2087
+ if copy:
2088
+ if json_output:
2089
+ _copy_to_clip(json_mod.dumps(project_data, indent=2, default=str), quiet=True)
2090
+ else:
2091
+ _copy_to_clip(output)
2092
+
2093
+ from reprompt.core.suggestions import get_suggestion
2094
+
2095
+ hint = get_suggestion("projects")
2096
+ if hint and not json_output:
2097
+ console.print(f" [dim]→ Try: {hint}[/dim]\n")
2098
+
2099
+
2049
2100
  @app.command(rich_help_panel="Analyze")
2050
2101
  def sessions(
2051
2102
  last: int = typer.Option(10, "--last", help="Show N most recent sessions"),
@@ -24,6 +24,7 @@ SUGGESTIONS: dict[str, str] = {
24
24
  "template": "reprompt insights (see which patterns work best)",
25
25
  "lint": "reprompt init (generate .reprompt.toml) · reprompt rewrite (improve prompts)",
26
26
  "rewrite": "reprompt compress (reduce tokens) · reprompt score (verify improvement)",
27
+ "projects": "reprompt sessions --detail <id> (deep-dive) · reprompt insights (patterns)",
27
28
  }
28
29
 
29
30
 
@@ -0,0 +1,101 @@
1
+ """Rich terminal output for projects command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from io import StringIO
6
+ from typing import Any
7
+
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+
12
+
13
+ def _score_style(score: float | None) -> str:
14
+ if score is None:
15
+ return "dim"
16
+ if score >= 80:
17
+ return "bold green"
18
+ if score >= 60:
19
+ return "green"
20
+ if score >= 40:
21
+ return "yellow"
22
+ return "red"
23
+
24
+
25
+ def render_projects_table(projects: list[dict[str, Any]]) -> str:
26
+ """Render project quality summary as a Rich table."""
27
+ buf = StringIO()
28
+ console = Console(file=buf, width=110, record=True)
29
+
30
+ if not projects:
31
+ console.print("\n [dim]No project data. Run [bold]reprompt scan[/bold] first.[/dim]\n")
32
+ return buf.getvalue()
33
+
34
+ # Header stats
35
+ total_sessions = sum(p.get("session_count", 0) for p in projects)
36
+ total_prompts = sum(p.get("prompt_count", 0) or 0 for p in projects)
37
+ quality_scores = [p["avg_quality"] for p in projects if p.get("avg_quality") is not None]
38
+ avg_quality = round(sum(quality_scores) / len(quality_scores), 1) if quality_scores else 0
39
+
40
+ console.print(
41
+ Panel(
42
+ f" [bold]{len(projects)}[/bold] projects · "
43
+ f"[bold]{total_sessions}[/bold] sessions · "
44
+ f"[bold]{total_prompts}[/bold] prompts · "
45
+ f"avg quality [bold]{avg_quality}[/bold]/100",
46
+ title="[bold]Project Quality[/bold]",
47
+ border_style="blue",
48
+ )
49
+ )
50
+
51
+ # Table
52
+ table = Table(show_header=True, header_style="bold", padding=(0, 1), expand=True)
53
+ table.add_column("Project", style="bold", min_width=12)
54
+ table.add_column("Sessions", justify="right", width=8)
55
+ table.add_column("Prompts", justify="right", width=8)
56
+ table.add_column("Quality", justify="right", width=8)
57
+ table.add_column("Efficiency", justify="right", width=10)
58
+ table.add_column("Focus", justify="right", width=7)
59
+ table.add_column("Frustration", justify="right", width=11)
60
+ table.add_column("Source", width=14)
61
+
62
+ for p in projects:
63
+ quality = p.get("avg_quality")
64
+ efficiency = p.get("avg_efficiency")
65
+ focus = p.get("avg_focus")
66
+
67
+ abandon = p.get("abandonment_count", 0) or 0
68
+ escalate = p.get("escalation_count", 0) or 0
69
+ frustration_total = abandon + escalate
70
+ sessions = p.get("session_count", 0)
71
+ frust_pct = round(frustration_total / sessions * 100) if sessions > 0 else 0
72
+
73
+ q_str = f"[{_score_style(quality)}]{quality:.0f}[/]" if quality else "[dim]--[/dim]"
74
+ e_str = (
75
+ f"[{_score_style(efficiency)}]{efficiency:.0f}[/]" if efficiency else "[dim]--[/dim]"
76
+ )
77
+ f_str = f"[{_score_style(focus)}]{focus:.0f}[/]" if focus else "[dim]--[/dim]"
78
+
79
+ frust_style = "red" if frust_pct > 30 else "yellow" if frust_pct > 15 else "dim"
80
+ frust_str = f"[{frust_style}]{frust_pct}%[/]" if frustration_total > 0 else "[dim]--[/dim]"
81
+
82
+ sources = p.get("sources", "")
83
+ # Shorten source names
84
+ short_sources = (
85
+ sources.replace("claude-code", "claude").replace("-ext", "") if sources else ""
86
+ )
87
+
88
+ table.add_row(
89
+ p.get("project", "unknown"),
90
+ str(sessions),
91
+ str(p.get("prompt_count", 0) or 0),
92
+ q_str,
93
+ e_str,
94
+ f_str,
95
+ frust_str,
96
+ short_sources,
97
+ )
98
+
99
+ console.print(table)
100
+ console.print()
101
+ return buf.getvalue()
@@ -809,6 +809,50 @@ class PromptDB:
809
809
  finally:
810
810
  conn.close()
811
811
 
812
+ def get_project_summary(self, source: str | None = None) -> list[dict[str, Any]]:
813
+ """Return per-project quality summary aggregated from session_meta.
814
+
815
+ Returns list of dicts with: project, session_count, prompt_count,
816
+ avg_quality, avg_efficiency, avg_focus, avg_outcome, abandonment_count,
817
+ escalation_count, sources, earliest, latest.
818
+ """
819
+ conn = self._conn()
820
+ try:
821
+ base_query = """
822
+ SELECT
823
+ COALESCE(sm.project, 'unknown') as project,
824
+ COUNT(DISTINCT sm.session_id) as session_count,
825
+ SUM(sm.prompt_count) as prompt_count,
826
+ ROUND(AVG(CASE WHEN sm.quality_score IS NOT NULL
827
+ THEN sm.quality_score END), 1) as avg_quality,
828
+ ROUND(AVG(CASE WHEN sm.efficiency_score IS NOT NULL
829
+ THEN sm.efficiency_score END), 1) as avg_efficiency,
830
+ ROUND(AVG(CASE WHEN sm.focus_score IS NOT NULL
831
+ THEN sm.focus_score END), 1) as avg_focus,
832
+ ROUND(AVG(CASE WHEN sm.outcome_score IS NOT NULL
833
+ THEN sm.outcome_score END), 1) as avg_outcome,
834
+ SUM(COALESCE(sm.has_abandonment, 0)) as abandonment_count,
835
+ SUM(COALESCE(sm.has_escalation, 0)) as escalation_count,
836
+ GROUP_CONCAT(DISTINCT sm.source) as sources,
837
+ MIN(sm.start_time) as earliest,
838
+ MAX(sm.end_time) as latest
839
+ FROM session_meta sm
840
+ WHERE sm.project IS NOT NULL AND sm.project != ''
841
+ """
842
+ if source:
843
+ base_query += " AND sm.source = ?"
844
+ rows = conn.execute(
845
+ base_query + " GROUP BY sm.project ORDER BY session_count DESC",
846
+ (source,),
847
+ ).fetchall()
848
+ else:
849
+ rows = conn.execute(
850
+ base_query + " GROUP BY sm.project ORDER BY session_count DESC"
851
+ ).fetchall()
852
+ return [dict(r) for r in rows]
853
+ finally:
854
+ conn.close()
855
+
812
856
  def get_effectiveness_for_session(self, session_id: str) -> float | None:
813
857
  """Return effectiveness_score for a session, or None if not found."""
814
858
  conn = self._conn()
@@ -0,0 +1,200 @@
1
+ """Tests for reprompt projects command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from reprompt.output.projects_terminal import render_projects_table
8
+ from reprompt.storage.db import PromptDB
9
+
10
+
11
+ def _meta(db: PromptDB, session_id: str, project: str, source: str = "claude-code", **kw):
12
+ """Helper to insert session_meta with required fields."""
13
+ db.upsert_session_meta(
14
+ session_id=session_id,
15
+ source=source,
16
+ project=project,
17
+ start_time=kw.get("start_time", "2026-03-15T10:00:00Z"),
18
+ end_time=kw.get("end_time", "2026-03-15T11:00:00Z"),
19
+ duration_seconds=kw.get("duration_seconds", 3600),
20
+ prompt_count=kw.get("prompt_count", 10),
21
+ tool_call_count=kw.get("tool_call_count", 5),
22
+ error_count=kw.get("error_count", 0),
23
+ final_status=kw.get("final_status", "completed"),
24
+ avg_prompt_length=kw.get("avg_prompt_length", 50.0),
25
+ effectiveness_score=kw.get("effectiveness_score", 0.7),
26
+ )
27
+
28
+
29
+ class TestGetProjectSummary:
30
+ def test_empty_db(self, tmp_path: Path) -> None:
31
+ db = PromptDB(tmp_path / "test.db")
32
+ assert db.get_project_summary() == []
33
+
34
+ def test_single_project(self, tmp_path: Path) -> None:
35
+ db = PromptDB(tmp_path / "test.db")
36
+ _meta(db, "s1", "myproject", prompt_count=5)
37
+ result = db.get_project_summary()
38
+ assert len(result) == 1
39
+ assert result[0]["project"] == "myproject"
40
+ assert result[0]["session_count"] == 1
41
+ assert result[0]["prompt_count"] == 5
42
+
43
+ def test_multiple_projects(self, tmp_path: Path) -> None:
44
+ db = PromptDB(tmp_path / "test.db")
45
+ for i, proj in enumerate(["alpha", "beta", "gamma"]):
46
+ for j in range(i + 1):
47
+ _meta(db, f"{proj}-s{j}", proj)
48
+ result = db.get_project_summary()
49
+ assert len(result) == 3
50
+ assert result[0]["project"] == "gamma"
51
+ assert result[0]["session_count"] == 3
52
+
53
+ def test_source_filter(self, tmp_path: Path) -> None:
54
+ db = PromptDB(tmp_path / "test.db")
55
+ _meta(db, "s1", "proj1", source="claude-code")
56
+ _meta(db, "s2", "proj2", source="cursor")
57
+ result = db.get_project_summary(source="claude-code")
58
+ assert len(result) == 1
59
+ assert result[0]["project"] == "proj1"
60
+
61
+ def test_quality_scores(self, tmp_path: Path) -> None:
62
+ db = PromptDB(tmp_path / "test.db")
63
+ _meta(db, "s1", "myproject")
64
+ db.upsert_session_quality(
65
+ session_id="s1",
66
+ quality_score=75.0,
67
+ prompt_quality_score=70.0,
68
+ efficiency_score=80.0,
69
+ focus_score=85.0,
70
+ outcome_score=65.0,
71
+ has_abandonment=False,
72
+ has_escalation=False,
73
+ stall_turns=0,
74
+ session_type="feature-dev",
75
+ quality_insight="Good",
76
+ )
77
+ result = db.get_project_summary()
78
+ assert len(result) == 1
79
+ assert result[0]["avg_quality"] == 75.0
80
+ assert result[0]["avg_efficiency"] == 80.0
81
+
82
+ def test_frustration_counts(self, tmp_path: Path) -> None:
83
+ db = PromptDB(tmp_path / "test.db")
84
+ for i in range(3):
85
+ _meta(db, f"s{i}", "troubled", error_count=3)
86
+ db.upsert_session_quality(
87
+ session_id=f"s{i}",
88
+ quality_score=40.0,
89
+ prompt_quality_score=35.0,
90
+ efficiency_score=30.0,
91
+ focus_score=45.0,
92
+ outcome_score=40.0,
93
+ has_abandonment=i < 2,
94
+ has_escalation=i == 0,
95
+ stall_turns=2,
96
+ session_type="debug",
97
+ quality_insight="Frustrating",
98
+ )
99
+ result = db.get_project_summary()
100
+ assert result[0]["abandonment_count"] == 2
101
+ assert result[0]["escalation_count"] == 1
102
+
103
+ def test_excludes_empty_project(self, tmp_path: Path) -> None:
104
+ db = PromptDB(tmp_path / "test.db")
105
+ _meta(db, "s1", "")
106
+ _meta(db, "s2", "real-project")
107
+ result = db.get_project_summary()
108
+ assert len(result) == 1
109
+ assert result[0]["project"] == "real-project"
110
+
111
+
112
+ class TestRenderProjectsTable:
113
+ def test_empty_projects(self) -> None:
114
+ output = render_projects_table([])
115
+ assert "No project data" in output
116
+
117
+ def test_renders_table(self) -> None:
118
+ data = [
119
+ {
120
+ "project": "myproject",
121
+ "session_count": 10,
122
+ "prompt_count": 50,
123
+ "avg_quality": 72.0,
124
+ "avg_efficiency": 65.0,
125
+ "avg_focus": 80.0,
126
+ "avg_outcome": None,
127
+ "abandonment_count": 1,
128
+ "escalation_count": 0,
129
+ "sources": "claude-code",
130
+ "earliest": None,
131
+ "latest": None,
132
+ }
133
+ ]
134
+ output = render_projects_table(data)
135
+ assert "myproject" in output
136
+ assert "72" in output
137
+
138
+ def test_multiple_projects(self) -> None:
139
+ data = [
140
+ {
141
+ "project": "alpha",
142
+ "session_count": 5,
143
+ "prompt_count": 20,
144
+ "avg_quality": 80.0,
145
+ "avg_efficiency": 75.0,
146
+ "avg_focus": 85.0,
147
+ "avg_outcome": None,
148
+ "abandonment_count": 0,
149
+ "escalation_count": 0,
150
+ "sources": "claude-code",
151
+ "earliest": None,
152
+ "latest": None,
153
+ },
154
+ {
155
+ "project": "beta",
156
+ "session_count": 3,
157
+ "prompt_count": 15,
158
+ "avg_quality": 45.0,
159
+ "avg_efficiency": 40.0,
160
+ "avg_focus": 50.0,
161
+ "avg_outcome": None,
162
+ "abandonment_count": 2,
163
+ "escalation_count": 1,
164
+ "sources": "cursor",
165
+ "earliest": None,
166
+ "latest": None,
167
+ },
168
+ ]
169
+ output = render_projects_table(data)
170
+ assert "alpha" in output
171
+ assert "beta" in output
172
+ assert "2 projects" in output
173
+
174
+
175
+ class TestProjectsCLI:
176
+ def test_help(self) -> None:
177
+ from typer.testing import CliRunner
178
+
179
+ from reprompt.cli import app
180
+
181
+ runner = CliRunner()
182
+ result = runner.invoke(app, ["projects", "--help"])
183
+ assert result.exit_code == 0
184
+ assert "project" in result.output.lower()
185
+
186
+ def test_empty_json(self, tmp_path: Path) -> None:
187
+ import json
188
+ import os
189
+
190
+ from typer.testing import CliRunner
191
+
192
+ from reprompt.cli import app
193
+
194
+ os.environ["REPROMPT_DB_PATH"] = str(tmp_path / "test.db")
195
+ runner = CliRunner()
196
+ result = runner.invoke(app, ["projects", "--json"])
197
+ del os.environ["REPROMPT_DB_PATH"]
198
+ assert result.exit_code == 0
199
+ data = json.loads(result.output)
200
+ assert data == []
@@ -19,6 +19,7 @@ class TestGetSuggestion:
19
19
  "template",
20
20
  "lint",
21
21
  "rewrite",
22
+ "projects",
22
23
  }
23
24
  assert set(SUGGESTIONS.keys()) == expected
24
25
 
@@ -2210,7 +2210,7 @@ wheels = [
2210
2210
 
2211
2211
  [[package]]
2212
2212
  name = "reprompt-cli"
2213
- version = "1.10.0"
2213
+ version = "2.0.0"
2214
2214
  source = { editable = "." }
2215
2215
  dependencies = [
2216
2216
  { name = "pydantic-settings" },
File without changes
File without changes
File without changes
File without changes
File without changes