reprompt-cli 1.2.0__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reprompt_cli-1.4.0/.testmondata +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/CLAUDE.md +21 -5
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/PKG-INFO +1 -1
- reprompt_cli-1.4.0/docs/roadmap.md +95 -0
- reprompt_cli-1.4.0/docs/superpowers/specs/2026-03-23-distill-design.md +375 -0
- reprompt_cli-1.4.0/docs/superpowers/specs/2026-03-23-v131-suggestions-source-design.md +179 -0
- reprompt_cli-1.4.0/docs/superpowers/specs/2026-03-24-v14-context-recovery-design.md +342 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/pyproject.toml +1 -1
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/__init__.py +1 -1
- reprompt_cli-1.4.0/src/reprompt/adapters/base.py +43 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/chatgpt.py +61 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/claude_code.py +101 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/cli.py +522 -173
- reprompt_cli-1.4.0/src/reprompt/core/conversation.py +61 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/digest.py +11 -10
- reprompt_cli-1.4.0/src/reprompt/core/distill.py +338 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/insights.py +92 -1
- reprompt_cli-1.4.0/src/reprompt/core/suggestions.py +22 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/trends.py +4 -2
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/compress_terminal.py +1 -0
- reprompt_cli-1.4.0/src/reprompt/output/distill_terminal.py +94 -0
- reprompt_cli-1.4.0/src/reprompt/output/export.py +270 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/terminal.py +49 -3
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/storage/db.py +26 -7
- reprompt_cli-1.4.0/tests/fixtures/export/default_export.md +29 -0
- reprompt_cli-1.4.0/tests/fixtures/export/full_export.md +36 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_compress.py +3 -1
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_compress_cli.py +1 -0
- reprompt_cli-1.4.0/tests/test_conversation.py +118 -0
- reprompt_cli-1.4.0/tests/test_deprecated_commands.py +60 -0
- reprompt_cli-1.4.0/tests/test_distill.py +414 -0
- reprompt_cli-1.4.0/tests/test_distill_cli.py +151 -0
- reprompt_cli-1.4.0/tests/test_distill_weights.py +96 -0
- reprompt_cli-1.4.0/tests/test_export.py +332 -0
- reprompt_cli-1.4.0/tests/test_export_cli.py +122 -0
- reprompt_cli-1.4.0/tests/test_export_snapshot.py +182 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_import_cli.py +7 -0
- reprompt_cli-1.4.0/tests/test_insights_expanded.py +140 -0
- reprompt_cli-1.4.0/tests/test_parse_conversation_base.py +63 -0
- reprompt_cli-1.4.0/tests/test_parse_conversation_chatgpt.py +150 -0
- reprompt_cli-1.4.0/tests/test_parse_conversation_claude.py +160 -0
- reprompt_cli-1.4.0/tests/test_source_filter.py +176 -0
- reprompt_cli-1.4.0/tests/test_suggestions.py +55 -0
- reprompt_cli-1.4.0/tests/test_template_cli.py +180 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/uv.lock +1 -1
- reprompt_cli-1.2.0/.testmondata +0 -0
- reprompt_cli-1.2.0/.testmondata-shm +0 -0
- reprompt_cli-1.2.0/.testmondata-wal +0 -0
- reprompt_cli-1.2.0/docs/roadmap.md +0 -74
- reprompt_cli-1.2.0/src/reprompt/adapters/base.py +0 -25
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.editorconfig +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/dependabot.yml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/workflows/ci.yml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.github/workflows/publish.yml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.gitignore +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/.pre-commit-config.yaml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/CHANGELOG.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/CODE_OF_CONDUCT.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/CONTRIBUTING.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/LICENSE +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/README.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/SECURITY.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/action.yml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/docs/launch-post.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/docs/superpowers/specs/2026-03-11-html-dashboard-design.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/docs/superpowers/specs/2026-03-11-merge-view-design.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/docs/superpowers/specs/2026-03-11-prompt-templates-design.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/docs/superpowers/specs/2026-03-22-prompt-compress-design.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/module.yaml +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/scripts/generate_demo_data.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/scripts/launch/hn_monitor.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/scripts/launch/reddit_helper.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/aider.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/claude_chat.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/cline.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/cursor.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/filters.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/gemini.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/adapters/openclaw.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/bridge/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/bridge/handler.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/bridge/host.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/bridge/manifest.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/bridge/protocol.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/commands/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/commands/telemetry.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/commands/wrapped.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/config.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/analyzer.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/compress.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/dedup.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/effectiveness.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/extractors.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/extractors_zh.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/lang_detect.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/library.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/lint.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/merge_view.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/models.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/persona.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/pipeline.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/privacy.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/prompt_dna.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/recommend.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/scorer.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/segmenter.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/session_meta.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/style.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/templates.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/timeutil.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/core/wrapped.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/demo.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/embeddings/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/embeddings/base.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/embeddings/local_embed.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/embeddings/ollama.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/embeddings/openai_embed.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/embeddings/tfidf.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/mcp.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/mcp_main.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/chartjs.min.js +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/html_report.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/json_out.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/markdown.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/wrapped_html.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/output/wrapped_terminal.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/py.typed +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/sharing/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/sharing/client.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/sharing/clipboard.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/storage/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/telemetry/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/telemetry/collector.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/telemetry/consent.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/telemetry/events.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/telemetry/prompt.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/telemetry/queue.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/src/reprompt/telemetry/sender.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/__init__.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/conftest.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/fixtures/aider_chat_history.md +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/fixtures/chatgpt_conversations.json +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/fixtures/claude_chat_export.json +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/fixtures/claude_session.jsonl +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/fixtures/cline_task/api_conversation_history.json +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/fixtures/gemini_session.json +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/fixtures/openclaw_session.jsonl +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_adapter_aider.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_adapter_chatgpt.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_adapter_claude.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_adapter_claude_chat.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_adapter_cline.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_adapter_gemini.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_adapter_openclaw.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_analyzer.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_bridge_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_bridge_handler.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_bridge_integration.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_bridge_manifest.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_bridge_protocol.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_cli_library_effectiveness.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_clipboard.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_compress_dna.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_compress_html.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_compress_insights.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_config.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_coverage_boost.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_cursor_adapter.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_db.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_db_digest.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_db_effectiveness.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_db_trends.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_dedup.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_demo.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_digest.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_digest_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_e2e.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_effectiveness.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_embeddings_local.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_embeddings_ollama.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_embeddings_openai.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_empty_state.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_extractors.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_extractors_routing.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_extractors_zh.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_extractors_zh_e2e.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_html_report.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_import_e2e.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_insights.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_insights_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_install_hook.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_lang_detect.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_library.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_lint.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_lint_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_markdown.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_mcp.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_merge_view.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_models.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_output.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_persona.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_pipeline.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_privacy.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_privacy_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_privacy_e2e.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_privacy_output.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_prompt_dna.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_public_api.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_recommend.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_schema_version.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_score_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_scorer.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_segmenter.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_share_e2e.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_sharing_client.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_style.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_collector.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_consent.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_e2e.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_events.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_prompt.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_queue.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_telemetry_sender.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_templates.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_timeutil.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_trends.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_trends_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_use_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_wrapped.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_wrapped_cli.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_wrapped_e2e.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_wrapped_html.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_wrapped_output.py +0 -0
- {reprompt_cli-1.2.0 → reprompt_cli-1.4.0}/tests/test_wrapped_share.py +0 -0
|
Binary file
|
|
@@ -18,7 +18,7 @@ uv run python -m build # build wheel
|
|
|
18
18
|
|
|
19
19
|
```
|
|
20
20
|
src/reprompt/
|
|
21
|
-
├── cli.py # Typer CLI (scan, import, report, search, library, recommend, demo, status, purge, install-hook, install-extension, extension-status, score, compare, insights, digest, style, use, privacy, compress) + plugin loading
|
|
21
|
+
├── cli.py # Typer CLI (scan, import, report, search, library, recommend, demo, status, purge, install-hook, install-extension, extension-status, score, compare, insights, digest, style, template [save|list|use], privacy, compress, distill) + plugin loading
|
|
22
22
|
├── config.py # pydantic-settings, env vars (REPROMPT_ prefix) + TOML config
|
|
23
23
|
├── demo.py # Built-in demo data generator (no network required)
|
|
24
24
|
├── core/
|
|
@@ -40,9 +40,12 @@ src/reprompt/
|
|
|
40
40
|
│ ├── persona.py # 6 prompt personas (Architect/Debugger/Explorer/Novelist/Sniper/Teacher)
|
|
41
41
|
│ ├── wrapped.py # WrappedReport dataclass + build_wrapped(db) aggregation
|
|
42
42
|
│ ├── privacy.py # Privacy metadata registry + exposure summary per adapter
|
|
43
|
-
│
|
|
43
|
+
│ ├── compress.py # 4-layer prompt compression (char norm + phrase simplify + filler delete + structure cleanup)
|
|
44
|
+
│ ├── suggestions.py # Command journey suggestions ("→ Try:" hints for 5 core commands)
|
|
45
|
+
│ ├── conversation.py # ConversationTurn, Conversation, DistillResult dataclasses
|
|
46
|
+
│ └── distill.py # 6-signal importance scoring + filtering + summary generation
|
|
44
47
|
├── adapters/
|
|
45
|
-
│ ├── base.py # BaseAdapter ABC
|
|
48
|
+
│ ├── base.py # BaseAdapter ABC + parse_conversation() default
|
|
46
49
|
│ ├── claude_code.py # Claude Code JSONL parser
|
|
47
50
|
│ ├── openclaw.py # OpenClaw JSON parser (supports ~/.openclaw/ + legacy ~/.opencode/)
|
|
48
51
|
│ ├── cursor.py # Cursor IDE .vscdb parser (cursorDiskKV + legacy ItemTable)
|
|
@@ -83,7 +86,8 @@ src/reprompt/
|
|
|
83
86
|
├── markdown.py # Markdown export
|
|
84
87
|
├── wrapped_terminal.py # Rich Prompt Wrapped report rendering
|
|
85
88
|
├── wrapped_html.py # Self-contained HTML share card (dark theme)
|
|
86
|
-
|
|
89
|
+
├── compress_terminal.py # Rich output for compress command
|
|
90
|
+
└── distill_terminal.py # Rich output for distill command
|
|
87
91
|
```
|
|
88
92
|
|
|
89
93
|
## Data Flow
|
|
@@ -120,7 +124,7 @@ reprompt-extension (private) ← Browser extension: Chrome/Firefox prompt capt
|
|
|
120
124
|
- Pattern upsert (not clear+re-insert) for stable IDs
|
|
121
125
|
- Prompts starting with `<` are filtered (system-injected XML)
|
|
122
126
|
- Config: env vars (REPROMPT_ prefix) > TOML (~/.config/reprompt/config.toml) > defaults
|
|
123
|
-
- Tests: pytest,
|
|
127
|
+
- Tests: pytest, 1295 tests, 95% coverage target
|
|
124
128
|
|
|
125
129
|
## Prompt Science Engine
|
|
126
130
|
|
|
@@ -144,3 +148,15 @@ Rule-based prompt optimization (added v1.2.0):
|
|
|
144
148
|
- `compressibility` field in PromptDNA, visible in insights + HTML dashboard
|
|
145
149
|
|
|
146
150
|
Sources: LLMLingua (Microsoft), CompactPrompt, TSC, stopwords-iso/zh, Prompt Report 2406.06608.
|
|
151
|
+
|
|
152
|
+
## Conversation Distillation Engine
|
|
153
|
+
|
|
154
|
+
Conversation-level analysis (added v1.3.0):
|
|
155
|
+
- `reprompt distill` — extract important turns from AI conversations
|
|
156
|
+
- 6-signal importance scoring: position, length, tool_trigger, error_recovery, semantic_shift, uniqueness
|
|
157
|
+
- Hybrid data source: raw session files (full conversation) + DB enrichment
|
|
158
|
+
- `parse_conversation()` on adapters returns both user and assistant turns
|
|
159
|
+
- Claude Code and ChatGPT adapters have full implementations; others fall back to user-only
|
|
160
|
+
- `--last N` for recent sessions, `--summary` for compressed output, `--json`, `--copy`
|
|
161
|
+
- `--threshold` to control importance cutoff (default 0.3)
|
|
162
|
+
- Pro plugin interface: `reprompt.distill_backends` entry point for LLM summarization (future)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reprompt-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Discover, analyze, and optimize your prompts from AI coding sessions
|
|
5
5
|
Project-URL: Homepage, https://github.com/reprompt-dev/reprompt
|
|
6
6
|
Project-URL: Repository, https://github.com/reprompt-dev/reprompt
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# reprompt Roadmap
|
|
2
|
+
|
|
3
|
+
> Last updated: 2026-03-24 · Current version: v1.4.0
|
|
4
|
+
|
|
5
|
+
## Vision
|
|
6
|
+
|
|
7
|
+
reprompt is the **prompt intelligence** tool for AI sessions — distill your conversations, compress your prompts, score them against research, and track your progress. Zero-config, privacy-first, CLI-first.
|
|
8
|
+
|
|
9
|
+
**Category definition:** reprompt analyzes *human inputs* (how you prompt), not *LLM outputs* (how models respond). Every other tool in the eval/observability space — Promptfoo, Braintrust, DeepEval, Langfuse — answers "did my AI system answer correctly?" reprompt answers "am I asking well?" This is an unoccupied category.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Current State (v1.4.0) — Production Stable
|
|
14
|
+
|
|
15
|
+
### Adapters (8)
|
|
16
|
+
Claude Code · OpenClaw · Cursor IDE · Aider · Gemini CLI · Cline · ChatGPT · Claude.ai
|
|
17
|
+
|
|
18
|
+
### Commands (23 visible, 5 deprecated)
|
|
19
|
+
`scan` · `import` · `report` · `library` · `trends` · `recommend` · `template [save|list|use]` · `lint` · `search` · `demo` · `status` · `purge` · `install-hook` · `install-extension` · `extension-status` · `score` · `compare` · `insights` · `digest` · `style` · `wrapped` · `telemetry` · `mcp-serve` · `compress` · `distill` · `privacy`
|
|
20
|
+
|
|
21
|
+
### Integrations
|
|
22
|
+
- MCP server (`reprompt mcp-serve`) for IDE integration
|
|
23
|
+
- GitHub Action (`action.yml`) for CI prompt quality checks
|
|
24
|
+
- HTML dashboard (`reprompt report --html`)
|
|
25
|
+
- Browser extension (Chrome/Firefox) via Native Messaging bridge
|
|
26
|
+
- JSON output on all commands for pipeline integration
|
|
27
|
+
|
|
28
|
+
### Key Features by Version
|
|
29
|
+
|
|
30
|
+
| Version | Feature | Description |
|
|
31
|
+
|---------|---------|-------------|
|
|
32
|
+
| v1.0.0 | Core platform | Scoring, dedup, report, trends, digest, style, effectiveness, templates, MCP, HTML dashboard |
|
|
33
|
+
| v1.1.0 | Privacy exposure | `reprompt privacy` — where your prompts went, training risk analysis |
|
|
34
|
+
| v1.2.0 | Prompt compression | `reprompt compress` — 4-layer rule-based compression (43 zh + 51 en rules) |
|
|
35
|
+
| v1.3.0 | Conversation distillation | `reprompt distill` — 6-signal importance scoring for conversation turns |
|
|
36
|
+
| v1.3.1 | UX polish | Actionable suggestions on 5 commands, `--source` filter on all data commands |
|
|
37
|
+
| v1.4.0 | Context recovery + consolidation | `distill --export` context document, signal transparency, command consolidation (27→23) |
|
|
38
|
+
|
|
39
|
+
### Quality
|
|
40
|
+
- 1295 tests, ≥90% coverage
|
|
41
|
+
- Strict mypy, ruff lint/format
|
|
42
|
+
- CI: coverage gate + pre-publish test step
|
|
43
|
+
- Stable public API (`score_prompt`, `compare_prompts`, `extract_features`)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## v1.4 — Context Recovery + Command Consolidation
|
|
48
|
+
|
|
49
|
+
| Priority | Item | Rationale |
|
|
50
|
+
|----------|------|-----------|
|
|
51
|
+
| P1 | `distill --export` context recovery | **DONE** — community signal: resume sessions after compaction/timeout |
|
|
52
|
+
| P2 | Command consolidation: `save`/`templates`/`use` → `template [save\|list\|use]` | **DONE** — 3 commands doing 1 thing = cognitive overload |
|
|
53
|
+
| P2 | Command consolidation: `effectiveness`/`merge-view` → `insights` sub-insights | **DONE** — concepts unclear to users |
|
|
54
|
+
| P3 | `style` shows change trends | "specificity +12% this week" drives revisits |
|
|
55
|
+
| P4 | `distill --show-weights` / `--weights` signal transparency | Community request for weight visibility |
|
|
56
|
+
| P5 | `compare --best-worst` auto-pick | Auto-pick best/worst from DB |
|
|
57
|
+
| P5 | `--copy` as standard option on remaining commands | Consistency |
|
|
58
|
+
|
|
59
|
+
**Status: 27 → 23 visible commands. P1+P2 shipped. Context recovery + consolidation done.**
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## v1.5+ — Future Work
|
|
64
|
+
|
|
65
|
+
| Feature | Description |
|
|
66
|
+
|---------|-------------|
|
|
67
|
+
| Sensitive content detection | Privacy narrative; PII in prompts |
|
|
68
|
+
| Agent workflow analysis | Multi-step agent session patterns |
|
|
69
|
+
| `.reprompt.yml` configurable lint | Team/Pro direction |
|
|
70
|
+
| `reprompt suggest` (Ollama rewrite) | LLM-powered prompt improvement |
|
|
71
|
+
| Homebrew formula | `brew install reprompt` |
|
|
72
|
+
| More adapters | Perplexity, Mistral, Grok, Gemini Takeout |
|
|
73
|
+
| Windows Native Messaging | Extension support on Windows |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Architecture Principles
|
|
78
|
+
|
|
79
|
+
1. **Zero-config first** — Every feature works without LLM by default
|
|
80
|
+
2. **Privacy by design** — All data stays local; extension has zero server
|
|
81
|
+
3. **Adapter pattern** — New AI tools supported by adding ~50 lines
|
|
82
|
+
4. **Input not output** — We analyze human prompts (inputs); LLM eval tools analyze model responses (outputs)
|
|
83
|
+
5. **CLI first, GUI second** — Terminal is primary, HTML dashboard is secondary
|
|
84
|
+
6. **Composable** — Every command supports JSON output for piping
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## How to Contribute
|
|
89
|
+
|
|
90
|
+
- **New adapter** (~50 lines) — see `src/reprompt/adapters/base.py`
|
|
91
|
+
- **New lint rules** — see `src/reprompt/core/lint.py`
|
|
92
|
+
- **Better categorization** — improve keyword rules in `core/library.py`
|
|
93
|
+
- **Browser extension** — see `reprompt-extension` repo
|
|
94
|
+
|
|
95
|
+
See [CONTRIBUTING.md](../CONTRIBUTING.md) for details.
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# `reprompt distill` — Conversation Distillation Design
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Extract the most important turns from an AI coding conversation, filtering noise and surfacing key decisions. Rule-based (Layer 1, open-source), with a plugin interface for future LLM-powered summarization (Layer 2, Pro).
|
|
6
|
+
|
|
7
|
+
## Problem Statement
|
|
8
|
+
|
|
9
|
+
AI power users run 20–50+ turn conversations daily. When context windows fill up, they need to know: "What actually mattered in this session?" No existing tool provides conversation-level distillation locally, without sending data to a server.
|
|
10
|
+
|
|
11
|
+
**User stories:**
|
|
12
|
+
- "I had a 40-turn Claude Code session. What were the key decisions?"
|
|
13
|
+
- "Copy the important parts of my last conversation so I can paste them into a new session."
|
|
14
|
+
- "Show me which turns triggered the most work."
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Raw session file → parse_conversation() → list[ConversationTurn]
|
|
20
|
+
↓
|
|
21
|
+
DB enrichment (scores, dedup status)
|
|
22
|
+
↓
|
|
23
|
+
Turn importance scoring (6 signals)
|
|
24
|
+
↓
|
|
25
|
+
Filtering (threshold ≥ 0.3)
|
|
26
|
+
↓
|
|
27
|
+
Turn pairing (user + assistant pairs)
|
|
28
|
+
↓
|
|
29
|
+
Output (filtered / summary / json / clipboard)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Data Source: Hybrid Approach
|
|
33
|
+
|
|
34
|
+
- **Raw session files** provide full conversation (user + assistant turns, tool_use, timestamps).
|
|
35
|
+
- **DB tables** provide enrichment (prompt scores from `prompt_features`, dedup status from `prompts`, session metadata from `session_meta`).
|
|
36
|
+
- Session files are located via the `processed_sessions` table which maps `file_path → source`.
|
|
37
|
+
|
|
38
|
+
This avoids storing conversation data twice while getting the full picture.
|
|
39
|
+
|
|
40
|
+
## Data Model
|
|
41
|
+
|
|
42
|
+
### ConversationTurn
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
@dataclass
|
|
46
|
+
class ConversationTurn:
|
|
47
|
+
role: str # "user" | "assistant"
|
|
48
|
+
text: str # The actual content
|
|
49
|
+
timestamp: str # ISO timestamp
|
|
50
|
+
turn_index: int # 0-based position in conversation
|
|
51
|
+
|
|
52
|
+
# Assistant-specific (0/False for user turns)
|
|
53
|
+
tool_calls: int = 0 # Number of tool_use blocks in this turn
|
|
54
|
+
has_error: bool = False # Whether turn contains error/failure
|
|
55
|
+
tool_use_paths: list[str] = field(default_factory=list) # File paths from tool_use blocks
|
|
56
|
+
|
|
57
|
+
# Enrichment (populated by distill engine, not adapter)
|
|
58
|
+
score: float | None = None # Display-only, from prompt_features (see Enrichment)
|
|
59
|
+
is_duplicate: bool = False # Cross-referenced with dedup
|
|
60
|
+
importance: float = 0.0 # Computed by distill scoring (6 signals only)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Note on `score` field:** This is **display-only enrichment data** — it does NOT feed into the 6-signal importance calculation. It is populated opportunistically from `prompt_features` (see Enrichment section) and shown in JSON output for reference. The `importance` field is the sole ranking signal.
|
|
64
|
+
|
|
65
|
+
### Conversation
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
@dataclass
|
|
69
|
+
class Conversation:
|
|
70
|
+
session_id: str
|
|
71
|
+
source: str
|
|
72
|
+
project: str | None
|
|
73
|
+
turns: list[ConversationTurn]
|
|
74
|
+
start_time: str | None = None
|
|
75
|
+
end_time: str | None = None
|
|
76
|
+
duration_seconds: int | None = None # Computed from timestamps (see Duration)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### DistillResult
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
@dataclass
|
|
83
|
+
class DistillResult:
|
|
84
|
+
conversation: Conversation # Original full conversation
|
|
85
|
+
filtered_turns: list[ConversationTurn] # Turns above threshold
|
|
86
|
+
threshold: float # Importance threshold used
|
|
87
|
+
summary: str | None = None # Generated if --summary
|
|
88
|
+
files_changed: list[str] = field(default_factory=list) # Extracted from tool_use
|
|
89
|
+
stats: DistillStats = field(default_factory=DistillStats)
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class DistillStats:
|
|
93
|
+
total_turns: int = 0
|
|
94
|
+
kept_turns: int = 0
|
|
95
|
+
retention_ratio: float = 0.0 # kept/total (0.26 = kept 26% of turns)
|
|
96
|
+
total_duration_seconds: int = 0
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Turn Importance Scoring
|
|
100
|
+
|
|
101
|
+
Six weighted signals, all rule-based (no LLM):
|
|
102
|
+
|
|
103
|
+
| Signal | Weight | Computation | Rationale |
|
|
104
|
+
|--------|--------|-------------|-----------|
|
|
105
|
+
| **Position** | 0.15 | First turn = 1.0, last turn = 0.8, others = 0.3 + 0.2 * recency | First turn sets context, last is conclusion |
|
|
106
|
+
| **Length** | 0.15 | `min(char_count / median_length, 1.0)` | Longer turns tend to be more substantive |
|
|
107
|
+
| **Tool trigger** | 0.20 | `min(tool_calls / 5, 1.0)` on the assistant turn following this user turn | Turns causing lots of tool use = key decisions |
|
|
108
|
+
| **Error recovery** | 0.15 | 1.0 if previous assistant turn has `has_error=True` | User correcting course = high-value context |
|
|
109
|
+
| **Semantic shift** | 0.20 | TF-IDF cosine distance from previous user turn; 0.5 (neutral) if first turn | Topic changes mark decision boundaries |
|
|
110
|
+
| **Uniqueness** | 0.15 | 1.0 - (similarity to most similar earlier turn via TF-IDF) | Novel instructions > repetitive ones |
|
|
111
|
+
|
|
112
|
+
**Scoring rules:**
|
|
113
|
+
- User turns: scored by all 6 signals → `importance = weighted sum`
|
|
114
|
+
- Assistant turns: derived score = average of adjacent user turns' importance
|
|
115
|
+
- Minimum threshold: 0.3 (configurable via `--threshold`)
|
|
116
|
+
|
|
117
|
+
### Tool Trigger Pairing
|
|
118
|
+
|
|
119
|
+
The tool_trigger signal requires pairing user turns with their assistant responses. For user turn at index `i`, the tool_calls come from the assistant turn at index `i+1` (if it exists and role == "assistant").
|
|
120
|
+
|
|
121
|
+
### Semantic Shift Computation
|
|
122
|
+
|
|
123
|
+
Uses scikit-learn `TfidfVectorizer` (already a dependency) fitted on all user turn texts in the conversation. Cosine distance between consecutive user turns. First user turn gets semantic_shift = 0.5 (neutral).
|
|
124
|
+
|
|
125
|
+
## DB Enrichment
|
|
126
|
+
|
|
127
|
+
Enrichment is **opportunistic** — it enhances output when data exists, but distill works fully without it.
|
|
128
|
+
|
|
129
|
+
**Score enrichment:** For each user turn, compute `hashlib.sha256(text.strip().encode()).hexdigest()` (same algorithm as `Prompt.__post_init__`). Look up the hash in `prompt_features`. If found, populate `ConversationTurn.score` with `overall_score`. If not found (user never ran `reprompt score` on this text), leave as `None`. This field is display-only and does NOT affect the 6-signal importance calculation.
|
|
130
|
+
|
|
131
|
+
**Dedup enrichment:** Same hash lookup in `prompts` table. If `duplicate_of IS NOT NULL`, set `is_duplicate = True`. This is informational — the uniqueness signal in scoring uses TF-IDF similarity within the conversation, not the DB dedup status.
|
|
132
|
+
|
|
133
|
+
### Duration Computation
|
|
134
|
+
|
|
135
|
+
`Conversation.duration_seconds` is derived from the first and last timestamps in `turns`:
|
|
136
|
+
```python
|
|
137
|
+
from datetime import datetime
|
|
138
|
+
start = datetime.fromisoformat(turns[0].timestamp.replace("Z", "+00:00"))
|
|
139
|
+
end = datetime.fromisoformat(turns[-1].timestamp.replace("Z", "+00:00"))
|
|
140
|
+
duration_seconds = int((end - start).total_seconds())
|
|
141
|
+
```
|
|
142
|
+
This follows the same `fromisoformat` + Z-replacement pattern used in `claude_code.py:parse_session_meta()`.
|
|
143
|
+
|
|
144
|
+
## Turn Pairing in Output
|
|
145
|
+
|
|
146
|
+
Filtered output shows **user-assistant pairs**, not isolated turns. When a user turn passes the threshold, its corresponding assistant response is always included (preserves conversation coherence). Assistant-only turns below threshold are dropped only if their adjacent user turns are also below threshold.
|
|
147
|
+
|
|
148
|
+
## Adapter Integration
|
|
149
|
+
|
|
150
|
+
### Base class (additive, non-breaking)
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# adapters/base.py
|
|
154
|
+
class BaseAdapter(ABC):
|
|
155
|
+
@abstractmethod
|
|
156
|
+
def parse_session(self, path: Path) -> list[Prompt]: ...
|
|
157
|
+
|
|
158
|
+
def parse_conversation(self, path: Path) -> list[ConversationTurn]:
|
|
159
|
+
"""Parse full conversation with both roles.
|
|
160
|
+
Default: user-only turns from parse_session()."""
|
|
161
|
+
prompts = self.parse_session(path)
|
|
162
|
+
return [
|
|
163
|
+
ConversationTurn(
|
|
164
|
+
role="user", text=p.text, timestamp=p.timestamp, turn_index=i
|
|
165
|
+
)
|
|
166
|
+
for i, p in enumerate(prompts)
|
|
167
|
+
]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### v1.3.0 adapters with full parse_conversation()
|
|
171
|
+
|
|
172
|
+
| Adapter | Approach |
|
|
173
|
+
|---------|----------|
|
|
174
|
+
| **claude-code** | Iterate JSONL entries. `type=user` + `role=user` → user turn. `type=assistant` → assistant turn. Count `tool_use` content blocks. Detect errors from `is_error` field or error-related content. |
|
|
175
|
+
| **chatgpt-export** | Walk `mapping` tree in order for a single conversation (selected by `conv_id`). `author.role=user` → user turn. `author.role=assistant` → assistant turn. No tool_calls data (set to 0). Accepts optional `conv_id` parameter; if None, parses first conversation in file. |
|
|
176
|
+
|
|
177
|
+
### Future adapters (v1.3.1+)
|
|
178
|
+
|
|
179
|
+
claude-chat, aider, gemini, openclaw, cline — implement `parse_conversation()` as data becomes available. They fall back to user-only via base class until then.
|
|
180
|
+
|
|
181
|
+
## Session Resolution
|
|
182
|
+
|
|
183
|
+
How `distill` finds which session file to parse:
|
|
184
|
+
|
|
185
|
+
| Input | Resolution |
|
|
186
|
+
|-------|-----------|
|
|
187
|
+
| `--last N` | Query `processed_sessions` ordered by `processed_at DESC`, take first N. Return list of `(file_path, source)`. |
|
|
188
|
+
| `<session_id>` | Query `prompts` for matching `session_id`, get `source`. Reconstruct file path: `adapter.default_session_path / session_id + ext` (e.g. `.jsonl` for claude-code). Verify file exists. If not, fall back to `processed_sessions WHERE file_path LIKE '%' || session_id || '%'`. |
|
|
189
|
+
| `--source X` | Filter `processed_sessions` by source, then apply `--last` logic. |
|
|
190
|
+
|
|
191
|
+
**Edge case — file deleted:** If session file no longer exists on disk, distill falls back to DB-only mode: query `prompts WHERE session_id = ?` for user turns (no assistant turns, no tool_use data). Warn user: "Session file not found, showing user turns only."
|
|
192
|
+
|
|
193
|
+
### ChatGPT Session Resolution
|
|
194
|
+
|
|
195
|
+
ChatGPT exports contain multiple conversations in one `conversations.json` file. The `session_id` for ChatGPT prompts is a hash-based string (e.g. `chatgpt-20260323T100000-ab1c2d3e`). When `distill` receives a ChatGPT session_id:
|
|
196
|
+
|
|
197
|
+
1. Look up `source = "chatgpt-export"` from `prompts` table
|
|
198
|
+
2. Find the `conversations.json` file path from `processed_sessions`
|
|
199
|
+
3. Parse the file, iterate conversation objects, match by `conv_id` (computed same way as in `chatgpt.py:_make_session_id()`)
|
|
200
|
+
4. Call `parse_conversation()` on that single conversation object
|
|
201
|
+
|
|
202
|
+
The ChatGPT `parse_conversation()` accepts an optional `conv_id` parameter to select a specific conversation from the file.
|
|
203
|
+
|
|
204
|
+
## Output Modes
|
|
205
|
+
|
|
206
|
+
### Tier 1 — Filtered conversation (default)
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
╭─ Distill: session abc123 (claude-code) ─╮
|
|
210
|
+
│ Project: reprompt | 45min | 47 → 12 turns │
|
|
211
|
+
╰──────────────────────────────────────────╯
|
|
212
|
+
|
|
213
|
+
★★★ [T1] User:
|
|
214
|
+
根据我们的设计文档继续我们的任务
|
|
215
|
+
Assistant: I'll read the spec and continue with Task 5...
|
|
216
|
+
|
|
217
|
+
★★☆ [T8] User:
|
|
218
|
+
不对,用approach B,parse_session不要改
|
|
219
|
+
Assistant: You're right, creating separate parse_conversation()...
|
|
220
|
+
|
|
221
|
+
★★★ [T15] User:
|
|
222
|
+
测试通过了,commit吧
|
|
223
|
+
Assistant: Committed: feat: add conversation parser
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Stars: ★★★ = importance ≥ 0.7, ★★☆ = ≥ 0.5, ★☆☆ = ≥ 0.3
|
|
227
|
+
|
|
228
|
+
Assistant text truncated to first 80 chars in terminal mode (full in JSON).
|
|
229
|
+
|
|
230
|
+
### Tier 2 — Summary (`--summary`)
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
╭─ Session Summary: abc123 ─╮
|
|
234
|
+
|
|
235
|
+
This session implemented the conversation parser for the distill feature.
|
|
236
|
+
|
|
237
|
+
Key decisions:
|
|
238
|
+
• Created separate parse_conversation() instead of modifying parse_session()
|
|
239
|
+
• Used weighted scoring with 6 signals for turn importance
|
|
240
|
+
• Threshold set at 0.3 for filtering
|
|
241
|
+
|
|
242
|
+
Files changed: core/distill.py, adapters/base.py, tests/test_distill.py
|
|
243
|
+
|
|
244
|
+
47 turns → 12 key turns | 45min | claude-code
|
|
245
|
+
╰───────────────────────────╯
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Summary is rule-based:
|
|
249
|
+
1. **Description**: First user turn text (compressed via `compress_text()`) + project name
|
|
250
|
+
2. **Key decisions**: Top 5 user turns by importance, each compressed
|
|
251
|
+
3. **Files changed**: Flattened from `tool_use_paths` across all assistant turns (deduplicated, sorted). Only Edit/Write paths included (Read paths are noise).
|
|
252
|
+
4. **Stats**: Turn counts + duration + source
|
|
253
|
+
|
|
254
|
+
### Tier 3 — JSON (`--json`)
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"session_id": "abc123",
|
|
259
|
+
"source": "claude-code",
|
|
260
|
+
"project": "reprompt",
|
|
261
|
+
"duration_seconds": 2700,
|
|
262
|
+
"total_turns": 47,
|
|
263
|
+
"kept_turns": 12,
|
|
264
|
+
"retention_ratio": 0.26,
|
|
265
|
+
"threshold": 0.3,
|
|
266
|
+
"summary": "...",
|
|
267
|
+
"files_changed": ["core/distill.py", "adapters/base.py"],
|
|
268
|
+
"turns": [
|
|
269
|
+
{
|
|
270
|
+
"turn_index": 0,
|
|
271
|
+
"role": "user",
|
|
272
|
+
"text": "...",
|
|
273
|
+
"timestamp": "2026-03-23T10:00:00Z",
|
|
274
|
+
"importance": 0.85,
|
|
275
|
+
"tool_calls": 0
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## CLI Interface
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
reprompt distill # Default: most recent session
|
|
285
|
+
reprompt distill --last 3 # Last 3 sessions
|
|
286
|
+
reprompt distill --summary # Key decisions only
|
|
287
|
+
reprompt distill --copy # Filtered → clipboard
|
|
288
|
+
reprompt distill --json # Machine-readable
|
|
289
|
+
reprompt distill abc123 # By session ID
|
|
290
|
+
reprompt distill --source chatgpt-export # Filter by adapter
|
|
291
|
+
reprompt distill --threshold 0.5 # Stricter filtering
|
|
292
|
+
reprompt distill --last 3 --copy # Last 3, concatenated to clipboard
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Flag Details
|
|
296
|
+
|
|
297
|
+
| Flag | Type | Default | Description |
|
|
298
|
+
|------|------|---------|-------------|
|
|
299
|
+
| `--last` | `int` | 1 | Most recent N sessions. Always requires explicit value. |
|
|
300
|
+
| `--source` | `str` | None | Filter by adapter name |
|
|
301
|
+
| `--summary` | `bool` | False | Tier 2 compressed output |
|
|
302
|
+
| `--json` | `bool` | False | JSON output |
|
|
303
|
+
| `--copy` | `bool` | False | Copy to clipboard |
|
|
304
|
+
| `--threshold` | `float` | 0.3 | Importance cutoff (0.0–1.0) |
|
|
305
|
+
| `session_id` | `str` (positional, optional) | None | Specific session |
|
|
306
|
+
|
|
307
|
+
`session_id` and `--last` are mutually exclusive. If neither is provided, default behavior is `--last 1` (most recent session).
|
|
308
|
+
|
|
309
|
+
**Multi-session behavior (`--last N`):** Each session is distilled independently. Terminal output separates sessions with a horizontal rule. `--copy` concatenates all sessions (separated by `---`). `--json` outputs a JSON array of DistillResult objects.
|
|
310
|
+
|
|
311
|
+
## File Structure
|
|
312
|
+
|
|
313
|
+
| File | Responsibility |
|
|
314
|
+
|------|----------------|
|
|
315
|
+
| `core/conversation.py` | `ConversationTurn`, `Conversation`, `DistillResult`, `DistillStats` dataclasses |
|
|
316
|
+
| `core/distill.py` | `distill_conversation(conv, threshold) → DistillResult` — scoring engine, filtering, summary |
|
|
317
|
+
| `adapters/base.py` | Add `parse_conversation()` default method |
|
|
318
|
+
| `adapters/claude_code.py` | Override `parse_conversation()` with full JSONL parsing |
|
|
319
|
+
| `adapters/chatgpt.py` | Override `parse_conversation()` with tree-walk parsing |
|
|
320
|
+
| `output/distill_terminal.py` | `render_distill(result) → str` Rich terminal output |
|
|
321
|
+
| `cli.py` | `distill` command registration |
|
|
322
|
+
|
|
323
|
+
## DB Changes
|
|
324
|
+
|
|
325
|
+
**None.** Distill reads from existing tables:
|
|
326
|
+
- `processed_sessions` — file path + source lookup
|
|
327
|
+
- `prompt_features` — optional score enrichment
|
|
328
|
+
- `prompts` — dedup status, fallback if file missing
|
|
329
|
+
|
|
330
|
+
No new tables, no migrations.
|
|
331
|
+
|
|
332
|
+
## Pro Plugin Interface (Layer 2, future)
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
# Entry point group: "reprompt.distill_backends"
|
|
336
|
+
# Interface:
|
|
337
|
+
def distill_llm(conversation: Conversation) -> str:
|
|
338
|
+
"""Returns LLM-generated semantic summary.
|
|
339
|
+
Example: '20 turns about auth → decided JWT with refresh tokens'
|
|
340
|
+
"""
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
When `reprompt-pro` is installed and user passes `--mode llm`:
|
|
344
|
+
- Plugin registered via `entry_points`
|
|
345
|
+
- Falls back gracefully: "LLM distillation requires reprompt-pro. Install: pip install reprompt-pro"
|
|
346
|
+
|
|
347
|
+
## Testing Strategy
|
|
348
|
+
|
|
349
|
+
| Test file | Coverage |
|
|
350
|
+
|-----------|----------|
|
|
351
|
+
| `tests/test_conversation.py` | Dataclass construction, field defaults, validation |
|
|
352
|
+
| `tests/test_distill.py` | Scoring signals (each independently), filtering, summary generation, edge cases (empty conversation, single turn, all below threshold) |
|
|
353
|
+
| `tests/test_distill_cli.py` | CLI invocation, --last, --json, --summary, --copy, --threshold, session_id |
|
|
354
|
+
| `tests/test_parse_conversation_claude.py` | Claude Code parse_conversation() with sample JSONL |
|
|
355
|
+
| `tests/test_parse_conversation_chatgpt.py` | ChatGPT parse_conversation() with sample JSON, conv_id selection |
|
|
356
|
+
|
|
357
|
+
**Additional edge case tests (in test_distill.py and test_distill_cli.py):**
|
|
358
|
+
- Session file deleted → DB-only fallback (user turns only, warning message)
|
|
359
|
+
- `--last 3` multi-session → each session distilled independently, separated output
|
|
360
|
+
- `--copy` with `--last 3` → concatenated with `---` separator
|
|
361
|
+
- Empty conversation (0 turns) → graceful empty result
|
|
362
|
+
- Single-turn conversation → that turn always passes threshold
|
|
363
|
+
- All turns below threshold → empty filtered_turns with stats showing 0 kept
|
|
364
|
+
|
|
365
|
+
**Test data:** Synthetic JSONL/JSON fixtures embedded in test files (same pattern as existing adapter tests).
|
|
366
|
+
|
|
367
|
+
## Success Criteria
|
|
368
|
+
|
|
369
|
+
1. `reprompt distill --last` produces a useful filtered view of the most recent session
|
|
370
|
+
2. 47-turn conversation distilled to ~10-15 key turns (70-80% reduction)
|
|
371
|
+
3. Error-recovery turns and topic-shift turns are consistently ranked high
|
|
372
|
+
4. `--copy` puts clipboard-ready text that can be pasted into a new AI session
|
|
373
|
+
5. Works with both Claude Code and ChatGPT exports
|
|
374
|
+
6. Zero new dependencies (uses existing scikit-learn for TF-IDF)
|
|
375
|
+
7. All tests pass, no regressions in existing 1153 tests
|