reprompt-cli 2.0.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.
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/PKG-INFO +1 -1
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/pyproject.toml +1 -1
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/__init__.py +1 -1
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/cli.py +51 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/suggestions.py +1 -0
- reprompt_cli-2.0.1/src/reprompt/output/projects_terminal.py +101 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/storage/db.py +44 -0
- reprompt_cli-2.0.1/tests/test_projects.py +200 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_suggestions.py +1 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.editorconfig +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/dependabot.yml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/workflows/ci.yml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.github/workflows/publish.yml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.gitignore +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.pre-commit-config.yaml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.pre-commit-hooks.yaml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.testmondata-shm +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/.testmondata-wal +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/CHANGELOG.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/CODE_OF_CONDUCT.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/CONTRIBUTING.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/LICENSE +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/README.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/SECURITY.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/Screenshot 2026-03-24 at 09.45.03.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/action.yml +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/demo.gif +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-128.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-16.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-256.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-32.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-48.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-512.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon-96.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/brand-icon.svg +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-128.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-16.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-256.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-32.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-48.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-512.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon-96.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-favicon.svg +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-128.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-16.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-256.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-32.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-48.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-512.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon-96.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/cli-icon.svg +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon-128.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon-16.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon-256.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon-32.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon-48.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon-512.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon-96.png +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/favicon.svg +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/icons/generate.sh +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/superpowers/specs/2026-03-24-v14-command-consolidation-design.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/superpowers/specs/2026-03-25-v1.5-dashboard-design.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/scripts/generate_demo_data.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/aider.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/base.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/chatgpt.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/claude_chat.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/claude_code.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/cline.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/codex.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/cursor.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/filters.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/gemini.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/adapters/openclaw.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/handler.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/host.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/manifest.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/bridge/protocol.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/commands/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/commands/telemetry.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/commands/wrapped.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/config.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/agent.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/analyzer.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/compress.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/conversation.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/cost.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/dashboard.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/dedup.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/digest.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/distill.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/effectiveness.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/extractors.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/extractors_zh.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/insights.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/lang_detect.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/library.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/lint.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/merge_view.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/models.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/persona.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/pipeline.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/privacy.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/privacy_scan.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/prompt_dna.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/recommend.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/repetition.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/rewrite.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/scorer.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/segmenter.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/session_meta.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/session_quality.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/session_type.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/style.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/templates.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/timeutil.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/trends.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/core/wrapped.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/demo.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/base.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/local_embed.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/ollama.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/openai_embed.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/embeddings/tfidf.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/mcp.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/mcp_main.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/agent_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/chartjs.min.js +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/compress_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/dashboard_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/distill_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/export.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/html_report.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/json_out.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/markdown.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/repetition_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/rewrite_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/sessions_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/wrapped_html.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/output/wrapped_terminal.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/py.typed +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/sharing/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/sharing/client.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/sharing/clipboard.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/storage/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/collector.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/consent.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/events.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/prompt.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/queue.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/src/reprompt/telemetry/sender.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/__init__.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/conftest.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/aider_chat_history.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/chatgpt_conversations.json +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/claude_chat_export.json +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/claude_session.jsonl +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/cline_task/api_conversation_history.json +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/export/default_export.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/export/full_export.md +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/gemini_session.json +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/fixtures/openclaw_session.jsonl +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_adapter_aider.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_adapter_chatgpt.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_adapter_claude.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_adapter_claude_chat.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_adapter_cline.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_adapter_gemini.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_adapter_openclaw.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_agent.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_agent_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_analyzer.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_bridge_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_bridge_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_bridge_handler.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_bridge_integration.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_bridge_manifest.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_bridge_protocol.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_cli_deprecations.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_cli_library_effectiveness.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_clipboard.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_codex_adapter.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_compare_best_worst.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_compress.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_compress_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_compress_dna.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_compress_html.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_compress_insights.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_config.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_conversation.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_copy_flag.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_cost.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_coverage_boost.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_cursor_adapter.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_dashboard.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_db.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_db_digest.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_db_effectiveness.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_db_session_quality.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_db_trends.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_dedup.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_demo.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_deprecated_commands.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_digest.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_digest_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_distill.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_distill_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_distill_weights.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_effectiveness.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_embeddings_local.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_embeddings_ollama.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_embeddings_openai.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_empty_state.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_export.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_export_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_export_snapshot.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_extractors.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_extractors_routing.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_extractors_zh.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_extractors_zh_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_html_report.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_import_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_import_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_init_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_insights.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_insights_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_insights_expanded.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_install_hook.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_lang_detect.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_library.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_lint.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_lint_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_markdown.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_mcp.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_merge_view.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_models.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_output.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_parse_conversation_base.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_parse_conversation_chatgpt.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_parse_conversation_claude.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_persona.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_pipeline.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_privacy.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_privacy_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_privacy_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_privacy_output.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_privacy_scan.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_prompt_dna.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_public_api.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_recommend.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_repetition.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_repetition_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_repetition_output.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_rewrite.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_schema_version.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_score_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_scorer.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_segmenter.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_session_quality.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_session_type.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_sessions_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_sessions_output.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_share_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_sharing_client.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_source_filter.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_style.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_style_trends.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_collector.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_consent.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_events.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_prompt.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_queue.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_telemetry_sender.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_template_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_templates.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_timeutil.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_trends.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_trends_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_use_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_wrapped.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_wrapped_cli.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_wrapped_e2e.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_wrapped_html.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_wrapped_output.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/tests/test_wrapped_share.py +0 -0
- {reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reprompt-cli
|
|
3
|
-
Version: 2.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
|
|
@@ -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 == []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reprompt_cli-2.0.0 → reprompt_cli-2.0.1}/docs/superpowers/specs/2026-03-25-v1.5-dashboard-design.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|