reprompt-cli 1.9.0__tar.gz → 1.10.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.9.0 → reprompt_cli-1.10.0}/PKG-INFO +23 -2
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/README.md +22 -1
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/pyproject.toml +1 -1
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/__init__.py +1 -1
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/cli.py +123 -6
- reprompt_cli-1.10.0/src/reprompt/core/lint.py +242 -0
- reprompt_cli-1.10.0/src/reprompt/core/rewrite.py +221 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/suggestions.py +2 -0
- reprompt_cli-1.10.0/src/reprompt/output/rewrite_terminal.py +69 -0
- reprompt_cli-1.10.0/tests/test_init_cli.py +80 -0
- reprompt_cli-1.10.0/tests/test_lint.py +237 -0
- reprompt_cli-1.10.0/tests/test_rewrite.py +157 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_suggestions.py +4 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/uv.lock +1 -1
- reprompt_cli-1.9.0/src/reprompt/core/lint.py +0 -112
- reprompt_cli-1.9.0/tests/test_lint.py +0 -81
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.editorconfig +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/dependabot.yml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/workflows/ci.yml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.github/workflows/publish.yml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.gitignore +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.pre-commit-config.yaml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.pre-commit-hooks.yaml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.testmondata-shm +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/.testmondata-wal +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/CHANGELOG.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/CODE_OF_CONDUCT.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/CONTRIBUTING.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/LICENSE +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/SECURITY.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/Screenshot 2026-03-24 at 09.45.03.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/action.yml +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/demo.gif +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon-128.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon-16.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon-256.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon-32.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon-48.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon-512.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon-96.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/brand-icon.svg +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon-128.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon-16.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon-256.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon-32.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon-48.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon-512.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon-96.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-favicon.svg +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon-128.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon-16.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon-256.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon-32.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon-48.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon-512.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon-96.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/cli-icon.svg +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon-128.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon-16.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon-256.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon-32.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon-48.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon-512.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon-96.png +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/favicon.svg +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/icons/generate.sh +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/superpowers/specs/2026-03-24-v14-command-consolidation-design.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/docs/superpowers/specs/2026-03-25-v1.5-dashboard-design.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/scripts/generate_demo_data.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/aider.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/base.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/chatgpt.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/claude_chat.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/claude_code.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/cline.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/codex.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/cursor.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/filters.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/gemini.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/adapters/openclaw.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/bridge/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/bridge/handler.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/bridge/host.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/bridge/manifest.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/bridge/protocol.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/commands/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/commands/telemetry.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/commands/wrapped.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/config.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/agent.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/analyzer.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/compress.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/conversation.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/cost.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/dashboard.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/dedup.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/digest.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/distill.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/effectiveness.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/extractors.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/extractors_zh.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/insights.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/lang_detect.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/library.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/merge_view.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/models.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/persona.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/pipeline.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/privacy.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/privacy_scan.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/prompt_dna.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/recommend.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/repetition.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/scorer.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/segmenter.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/session_meta.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/session_quality.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/session_type.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/style.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/templates.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/timeutil.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/trends.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/core/wrapped.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/demo.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/embeddings/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/embeddings/base.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/embeddings/local_embed.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/embeddings/ollama.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/embeddings/openai_embed.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/embeddings/tfidf.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/mcp.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/mcp_main.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/agent_terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/chartjs.min.js +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/compress_terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/dashboard_terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/distill_terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/export.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/html_report.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/json_out.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/markdown.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/repetition_terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/sessions_terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/wrapped_html.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/output/wrapped_terminal.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/py.typed +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/sharing/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/sharing/client.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/sharing/clipboard.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/storage/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/storage/db.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/telemetry/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/telemetry/collector.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/telemetry/consent.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/telemetry/events.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/telemetry/prompt.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/telemetry/queue.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/src/reprompt/telemetry/sender.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/__init__.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/conftest.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/aider_chat_history.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/chatgpt_conversations.json +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/claude_chat_export.json +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/claude_session.jsonl +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/cline_task/api_conversation_history.json +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/export/default_export.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/export/full_export.md +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/gemini_session.json +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/fixtures/openclaw_session.jsonl +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_adapter_aider.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_adapter_chatgpt.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_adapter_claude.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_adapter_claude_chat.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_adapter_cline.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_adapter_gemini.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_adapter_openclaw.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_agent.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_agent_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_analyzer.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_bridge_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_bridge_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_bridge_handler.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_bridge_integration.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_bridge_manifest.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_bridge_protocol.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_cli_deprecations.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_cli_library_effectiveness.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_clipboard.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_codex_adapter.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_compare_best_worst.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_compress.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_compress_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_compress_dna.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_compress_html.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_compress_insights.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_config.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_conversation.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_copy_flag.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_cost.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_coverage_boost.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_cursor_adapter.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_dashboard.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_db.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_db_digest.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_db_effectiveness.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_db_session_quality.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_db_trends.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_dedup.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_demo.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_deprecated_commands.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_digest.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_digest_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_distill.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_distill_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_distill_weights.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_effectiveness.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_embeddings_local.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_embeddings_ollama.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_embeddings_openai.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_empty_state.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_export.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_export_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_export_snapshot.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_extractors.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_extractors_routing.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_extractors_zh.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_extractors_zh_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_html_report.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_import_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_import_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_insights.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_insights_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_insights_expanded.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_install_hook.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_lang_detect.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_library.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_lint_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_markdown.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_mcp.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_merge_view.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_models.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_output.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_parse_conversation_base.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_parse_conversation_chatgpt.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_parse_conversation_claude.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_persona.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_pipeline.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_privacy.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_privacy_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_privacy_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_privacy_output.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_privacy_scan.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_prompt_dna.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_public_api.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_recommend.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_repetition.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_repetition_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_repetition_output.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_schema_version.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_score_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_scorer.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_segmenter.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_session_quality.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_session_type.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_sessions_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_sessions_output.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_share_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_sharing_client.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_source_filter.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_style.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_style_trends.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_collector.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_consent.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_events.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_prompt.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_queue.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_telemetry_sender.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_template_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_templates.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_timeutil.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_trends.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_trends_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_use_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_wrapped.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_wrapped_cli.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_wrapped_e2e.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_wrapped_html.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_wrapped_output.py +0 -0
- {reprompt_cli-1.9.0 → reprompt_cli-1.10.0}/tests/test_wrapped_share.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reprompt-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.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
|
|
@@ -96,6 +96,8 @@ $ reprompt compress "I was wondering if you could please help me refactor this c
|
|
|
96
96
|
| `reprompt insights` | Personal patterns vs research-optimal benchmarks |
|
|
97
97
|
| `reprompt style` | Prompting fingerprint with `--trends` for evolution tracking |
|
|
98
98
|
| `reprompt agent` | Agent workflow analysis -- error loops, tool patterns, session efficiency |
|
|
99
|
+
| `reprompt sessions` | Session quality scores with frustration signal detection |
|
|
100
|
+
| `reprompt repetition` | Cross-session repetition detection -- spot recurring prompts |
|
|
99
101
|
|
|
100
102
|
### Optimize
|
|
101
103
|
|
|
@@ -104,7 +106,8 @@ $ reprompt compress "I was wondering if you could please help me refactor this c
|
|
|
104
106
|
| `reprompt compress "prompt"` | 4-layer prompt compression (40-60% token savings typical) |
|
|
105
107
|
| `reprompt distill` | Extract important turns from conversations with 6-signal scoring |
|
|
106
108
|
| `reprompt distill --export` | Recover context when a session runs out -- paste into new session |
|
|
107
|
-
| `reprompt lint` |
|
|
109
|
+
| `reprompt lint` | Configurable prompt quality linter with CI/GitHub Action support |
|
|
110
|
+
| `reprompt init` | Generate `.reprompt.toml` config for your project |
|
|
108
111
|
|
|
109
112
|
### Manage
|
|
110
113
|
|
|
@@ -223,6 +226,24 @@ reprompt lint --strict # exit 1 on warnings
|
|
|
223
226
|
reprompt lint --json # machine-readable output
|
|
224
227
|
```
|
|
225
228
|
|
|
229
|
+
#### Project configuration
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
reprompt init # generates .reprompt.toml with all rules documented
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```toml
|
|
236
|
+
# .reprompt.toml (or [tool.reprompt.lint] in pyproject.toml)
|
|
237
|
+
[lint]
|
|
238
|
+
score-threshold = 50 # fail if avg score < 50
|
|
239
|
+
|
|
240
|
+
[lint.rules]
|
|
241
|
+
min-length = 20 # error if prompt < 20 chars (0 = off)
|
|
242
|
+
short-prompt = 40 # warning if < 40 chars (0 = off)
|
|
243
|
+
vague-prompt = true # error on "fix it" etc (false = off)
|
|
244
|
+
debug-needs-reference = true
|
|
245
|
+
```
|
|
246
|
+
|
|
226
247
|
## Privacy
|
|
227
248
|
|
|
228
249
|
- All analysis runs locally. No prompts leave your machine.
|
|
@@ -51,6 +51,8 @@ $ reprompt compress "I was wondering if you could please help me refactor this c
|
|
|
51
51
|
| `reprompt insights` | Personal patterns vs research-optimal benchmarks |
|
|
52
52
|
| `reprompt style` | Prompting fingerprint with `--trends` for evolution tracking |
|
|
53
53
|
| `reprompt agent` | Agent workflow analysis -- error loops, tool patterns, session efficiency |
|
|
54
|
+
| `reprompt sessions` | Session quality scores with frustration signal detection |
|
|
55
|
+
| `reprompt repetition` | Cross-session repetition detection -- spot recurring prompts |
|
|
54
56
|
|
|
55
57
|
### Optimize
|
|
56
58
|
|
|
@@ -59,7 +61,8 @@ $ reprompt compress "I was wondering if you could please help me refactor this c
|
|
|
59
61
|
| `reprompt compress "prompt"` | 4-layer prompt compression (40-60% token savings typical) |
|
|
60
62
|
| `reprompt distill` | Extract important turns from conversations with 6-signal scoring |
|
|
61
63
|
| `reprompt distill --export` | Recover context when a session runs out -- paste into new session |
|
|
62
|
-
| `reprompt lint` |
|
|
64
|
+
| `reprompt lint` | Configurable prompt quality linter with CI/GitHub Action support |
|
|
65
|
+
| `reprompt init` | Generate `.reprompt.toml` config for your project |
|
|
63
66
|
|
|
64
67
|
### Manage
|
|
65
68
|
|
|
@@ -178,6 +181,24 @@ reprompt lint --strict # exit 1 on warnings
|
|
|
178
181
|
reprompt lint --json # machine-readable output
|
|
179
182
|
```
|
|
180
183
|
|
|
184
|
+
#### Project configuration
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
reprompt init # generates .reprompt.toml with all rules documented
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```toml
|
|
191
|
+
# .reprompt.toml (or [tool.reprompt.lint] in pyproject.toml)
|
|
192
|
+
[lint]
|
|
193
|
+
score-threshold = 50 # fail if avg score < 50
|
|
194
|
+
|
|
195
|
+
[lint.rules]
|
|
196
|
+
min-length = 20 # error if prompt < 20 chars (0 = off)
|
|
197
|
+
short-prompt = 40 # warning if < 40 chars (0 = off)
|
|
198
|
+
vague-prompt = true # error on "fix it" etc (false = off)
|
|
199
|
+
debug-needs-reference = true
|
|
200
|
+
```
|
|
201
|
+
|
|
181
202
|
## Privacy
|
|
182
203
|
|
|
183
204
|
- All analysis runs locally. No prompts leave your machine.
|
|
@@ -820,13 +820,19 @@ def lint(
|
|
|
820
820
|
import json as json_mod
|
|
821
821
|
|
|
822
822
|
from reprompt.config import Settings
|
|
823
|
-
from reprompt.core.lint import format_lint_results, lint_prompts
|
|
823
|
+
from reprompt.core.lint import format_lint_results, lint_prompts, load_lint_config
|
|
824
824
|
from reprompt.core.pipeline import get_adapters
|
|
825
825
|
from reprompt.storage.db import PromptDB
|
|
826
826
|
|
|
827
827
|
settings = Settings()
|
|
828
828
|
db = PromptDB(settings.db_path)
|
|
829
829
|
|
|
830
|
+
# Load lint config from .reprompt.toml / pyproject.toml
|
|
831
|
+
lint_config = load_lint_config()
|
|
832
|
+
|
|
833
|
+
# CLI flags override config file
|
|
834
|
+
effective_threshold = score_threshold if score_threshold > 0 else lint_config.score_threshold
|
|
835
|
+
|
|
830
836
|
# Collect prompts from DB (already scanned)
|
|
831
837
|
rows = db.get_all_prompts()
|
|
832
838
|
texts = [r["text"] for r in rows]
|
|
@@ -858,11 +864,11 @@ def lint(
|
|
|
858
864
|
console.print("No prompts found. Run [bold]reprompt scan[/bold] first.")
|
|
859
865
|
raise typer.Exit(0)
|
|
860
866
|
|
|
861
|
-
violations = lint_prompts(texts)
|
|
867
|
+
violations = lint_prompts(texts, config=lint_config)
|
|
862
868
|
|
|
863
869
|
# Score threshold mode (CI integration)
|
|
864
870
|
score_data = None
|
|
865
|
-
if
|
|
871
|
+
if effective_threshold > 0:
|
|
866
872
|
from reprompt.core.extractors import extract_features
|
|
867
873
|
from reprompt.core.scorer import score_prompt
|
|
868
874
|
|
|
@@ -875,8 +881,8 @@ def lint(
|
|
|
875
881
|
"avg_score": round(avg_score, 1),
|
|
876
882
|
"min_score": min(scores) if scores else 0,
|
|
877
883
|
"max_score": max(scores) if scores else 0,
|
|
878
|
-
"threshold":
|
|
879
|
-
"pass": avg_score >=
|
|
884
|
+
"threshold": effective_threshold,
|
|
885
|
+
"pass": avg_score >= effective_threshold,
|
|
880
886
|
}
|
|
881
887
|
|
|
882
888
|
if json_output:
|
|
@@ -905,7 +911,7 @@ def lint(
|
|
|
905
911
|
console.print(
|
|
906
912
|
f"\n Score: avg {score_data['avg_score']}/100"
|
|
907
913
|
f" (min {score_data['min_score']}, max {score_data['max_score']})"
|
|
908
|
-
f" — threshold {
|
|
914
|
+
f" — threshold {effective_threshold} → {status}"
|
|
909
915
|
)
|
|
910
916
|
|
|
911
917
|
if copy:
|
|
@@ -1059,6 +1065,58 @@ def compress(
|
|
|
1059
1065
|
_copy_to_clip(result.compressed, quiet=json_output)
|
|
1060
1066
|
|
|
1061
1067
|
|
|
1068
|
+
@app.command(rich_help_panel="Optimize")
|
|
1069
|
+
def rewrite(
|
|
1070
|
+
text: str = typer.Argument(..., help="Prompt text to improve"),
|
|
1071
|
+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
1072
|
+
copy: bool = typer.Option(False, "--copy", help="Copy rewritten text to clipboard"),
|
|
1073
|
+
) -> None:
|
|
1074
|
+
"""Rewrite a prompt to improve its score. Rule-based, no LLM needed.
|
|
1075
|
+
|
|
1076
|
+
Applies 4 layers: filler removal, instruction front-loading,
|
|
1077
|
+
key requirement echo, and hedging cleanup. Also suggests manual
|
|
1078
|
+
improvements you can make.
|
|
1079
|
+
|
|
1080
|
+
Examples:
|
|
1081
|
+
|
|
1082
|
+
reprompt rewrite "I was wondering if you could fix the authentication bug"
|
|
1083
|
+
|
|
1084
|
+
reprompt rewrite "please help me refactor this code to be better" --copy
|
|
1085
|
+
|
|
1086
|
+
reprompt rewrite "fix the login" --json
|
|
1087
|
+
"""
|
|
1088
|
+
from reprompt.core.rewrite import rewrite_prompt
|
|
1089
|
+
|
|
1090
|
+
result = rewrite_prompt(text)
|
|
1091
|
+
|
|
1092
|
+
if json_output:
|
|
1093
|
+
import json as json_mod
|
|
1094
|
+
|
|
1095
|
+
data = {
|
|
1096
|
+
"original": result.original,
|
|
1097
|
+
"rewritten": result.rewritten,
|
|
1098
|
+
"score_before": result.score_before,
|
|
1099
|
+
"score_after": result.score_after,
|
|
1100
|
+
"score_delta": result.score_delta,
|
|
1101
|
+
"changes": result.changes,
|
|
1102
|
+
"manual_suggestions": result.manual_suggestions,
|
|
1103
|
+
}
|
|
1104
|
+
typer.echo(json_mod.dumps(data, indent=2, ensure_ascii=False))
|
|
1105
|
+
else:
|
|
1106
|
+
from reprompt.output.rewrite_terminal import render_rewrite
|
|
1107
|
+
|
|
1108
|
+
typer.echo(render_rewrite(result))
|
|
1109
|
+
|
|
1110
|
+
if copy:
|
|
1111
|
+
_copy_to_clip(result.rewritten, quiet=json_output)
|
|
1112
|
+
|
|
1113
|
+
from reprompt.core.suggestions import get_suggestion
|
|
1114
|
+
|
|
1115
|
+
hint = get_suggestion("rewrite")
|
|
1116
|
+
if hint and not json_output:
|
|
1117
|
+
console.print(f" [dim]→ Try: {hint}[/dim]\n")
|
|
1118
|
+
|
|
1119
|
+
|
|
1062
1120
|
@app.command(rich_help_panel="Optimize")
|
|
1063
1121
|
def distill(
|
|
1064
1122
|
session_id: str = typer.Argument(None, help="Session ID to distill"),
|
|
@@ -1929,6 +1987,65 @@ def extension_status() -> None:
|
|
|
1929
1987
|
console.print(" Last sync: never")
|
|
1930
1988
|
|
|
1931
1989
|
|
|
1990
|
+
@app.command(rich_help_panel="Setup")
|
|
1991
|
+
def init(
|
|
1992
|
+
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing config"),
|
|
1993
|
+
) -> None:
|
|
1994
|
+
"""Generate a .reprompt.toml config file in the current directory.
|
|
1995
|
+
|
|
1996
|
+
Creates a starter config with all lint rules documented and commented.
|
|
1997
|
+
Use this to customize reprompt for your project or CI pipeline.
|
|
1998
|
+
|
|
1999
|
+
Examples:
|
|
2000
|
+
|
|
2001
|
+
reprompt init # create .reprompt.toml
|
|
2002
|
+
|
|
2003
|
+
reprompt init --force # overwrite existing config
|
|
2004
|
+
"""
|
|
2005
|
+
config_path = Path.cwd() / ".reprompt.toml"
|
|
2006
|
+
|
|
2007
|
+
if config_path.exists() and not force:
|
|
2008
|
+
console.print(
|
|
2009
|
+
f"[yellow]{config_path.name} already exists.[/yellow]"
|
|
2010
|
+
" Use [bold]--force[/bold] to overwrite."
|
|
2011
|
+
)
|
|
2012
|
+
raise typer.Exit(1)
|
|
2013
|
+
|
|
2014
|
+
config_content = """\
|
|
2015
|
+
# reprompt configuration
|
|
2016
|
+
# Docs: https://github.com/reprompt-dev/reprompt
|
|
2017
|
+
#
|
|
2018
|
+
# This file configures `reprompt lint` rules and CI thresholds.
|
|
2019
|
+
# Place in your project root — reprompt walks up from CWD to find it.
|
|
2020
|
+
# Alternatively, add [tool.reprompt.lint] to pyproject.toml.
|
|
2021
|
+
|
|
2022
|
+
[lint]
|
|
2023
|
+
# Fail `reprompt lint` if average prompt score < threshold (0 = disabled)
|
|
2024
|
+
# Useful for CI: reprompt lint --score-threshold reads this value
|
|
2025
|
+
# score-threshold = 50
|
|
2026
|
+
|
|
2027
|
+
[lint.rules]
|
|
2028
|
+
# min-length: error if prompt < N chars (0 = disabled)
|
|
2029
|
+
min-length = 20
|
|
2030
|
+
|
|
2031
|
+
# short-prompt: warning if prompt < N chars (0 = disabled)
|
|
2032
|
+
short-prompt = 40
|
|
2033
|
+
|
|
2034
|
+
# vague-prompt: error on vague prompts like "fix it" (false = disabled)
|
|
2035
|
+
vague-prompt = true
|
|
2036
|
+
|
|
2037
|
+
# debug-needs-reference: warning if debug prompt lacks file reference (false = disabled)
|
|
2038
|
+
debug-needs-reference = true
|
|
2039
|
+
|
|
2040
|
+
# file-extensions: extensions recognized as file references
|
|
2041
|
+
# file-extensions = [".py", ".ts", ".js", ".go", ".rs", ".java", ".rb", ".cpp", ".c"]
|
|
2042
|
+
"""
|
|
2043
|
+
|
|
2044
|
+
config_path.write_text(config_content)
|
|
2045
|
+
console.print(f"[green]Created[/green] {config_path.name}")
|
|
2046
|
+
console.print(" Edit rules, then run [bold]reprompt lint[/bold] to verify.")
|
|
2047
|
+
|
|
2048
|
+
|
|
1932
2049
|
@app.command(rich_help_panel="Analyze")
|
|
1933
2050
|
def sessions(
|
|
1934
2051
|
last: int = typer.Option(10, "--last", help="Show N most recent sessions"),
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Prompt quality linting rules.
|
|
2
|
+
|
|
3
|
+
Checks prompts against configurable quality rules and returns violations.
|
|
4
|
+
Designed for CI integration — each rule produces a severity + message.
|
|
5
|
+
|
|
6
|
+
Configuration loaded from (highest priority wins):
|
|
7
|
+
1. CLI flags (--score-threshold, --strict)
|
|
8
|
+
2. .reprompt.toml in CWD or parents
|
|
9
|
+
3. [tool.reprompt.lint] in pyproject.toml
|
|
10
|
+
4. Built-in defaults
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import sys
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
if sys.version_info >= (3, 11):
|
|
20
|
+
import tomllib
|
|
21
|
+
else:
|
|
22
|
+
try:
|
|
23
|
+
import tomllib
|
|
24
|
+
except ModuleNotFoundError: # Python 3.10
|
|
25
|
+
import tomli as tomllib # type: ignore[no-redefine]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class LintViolation:
|
|
30
|
+
"""A single lint rule violation."""
|
|
31
|
+
|
|
32
|
+
rule: str
|
|
33
|
+
severity: str # "error" | "warning"
|
|
34
|
+
message: str
|
|
35
|
+
prompt_text: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class LintConfig:
|
|
40
|
+
"""Configuration for lint rules. Loaded from .reprompt.toml or pyproject.toml."""
|
|
41
|
+
|
|
42
|
+
# Rule thresholds (0 = disabled)
|
|
43
|
+
min_length: int = 20
|
|
44
|
+
short_prompt: int = 40
|
|
45
|
+
|
|
46
|
+
# Boolean rules (False = disabled)
|
|
47
|
+
vague_prompt: bool = True
|
|
48
|
+
debug_needs_reference: bool = True
|
|
49
|
+
|
|
50
|
+
# File extensions for debug-needs-reference
|
|
51
|
+
file_extensions: list[str] = field(
|
|
52
|
+
default_factory=lambda: [".py", ".ts", ".js", ".go", ".rs", ".java", ".rb", ".cpp", ".c"]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# CI score threshold (0 = disabled, set via --score-threshold or config)
|
|
56
|
+
score_threshold: int = 0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# --- Default config ---
|
|
60
|
+
DEFAULT_CONFIG = LintConfig()
|
|
61
|
+
|
|
62
|
+
VAGUE_STARTERS = frozenset(
|
|
63
|
+
{"fix it", "fix this", "do it", "help me", "change it", "update it", "make it work"}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def load_lint_config(start_dir: Path | None = None) -> LintConfig:
|
|
68
|
+
"""Load lint config from .reprompt.toml or pyproject.toml, walking up from start_dir.
|
|
69
|
+
|
|
70
|
+
Returns DEFAULT_CONFIG if no config file found.
|
|
71
|
+
"""
|
|
72
|
+
if start_dir is None:
|
|
73
|
+
start_dir = Path.cwd()
|
|
74
|
+
|
|
75
|
+
# Walk up to find config files
|
|
76
|
+
current = start_dir.resolve()
|
|
77
|
+
for _ in range(20): # safety limit
|
|
78
|
+
# Check .reprompt.toml first (project-specific)
|
|
79
|
+
reprompt_toml = current / ".reprompt.toml"
|
|
80
|
+
if reprompt_toml.is_file():
|
|
81
|
+
return _parse_reprompt_toml(reprompt_toml)
|
|
82
|
+
|
|
83
|
+
# Check pyproject.toml
|
|
84
|
+
pyproject = current / "pyproject.toml"
|
|
85
|
+
if pyproject.is_file():
|
|
86
|
+
config = _parse_pyproject_toml(pyproject)
|
|
87
|
+
if config is not None:
|
|
88
|
+
return config
|
|
89
|
+
|
|
90
|
+
parent = current.parent
|
|
91
|
+
if parent == current:
|
|
92
|
+
break
|
|
93
|
+
current = parent
|
|
94
|
+
|
|
95
|
+
return LintConfig()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _parse_reprompt_toml(path: Path) -> LintConfig:
|
|
99
|
+
"""Parse a .reprompt.toml file."""
|
|
100
|
+
try:
|
|
101
|
+
with open(path, "rb") as f:
|
|
102
|
+
data = tomllib.load(f)
|
|
103
|
+
return _build_config(data.get("lint", {}))
|
|
104
|
+
except Exception:
|
|
105
|
+
return LintConfig()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _parse_pyproject_toml(path: Path) -> LintConfig | None:
|
|
109
|
+
"""Parse [tool.reprompt.lint] from pyproject.toml. Returns None if section missing."""
|
|
110
|
+
try:
|
|
111
|
+
with open(path, "rb") as f:
|
|
112
|
+
data = tomllib.load(f)
|
|
113
|
+
lint_section = data.get("tool", {}).get("reprompt", {}).get("lint")
|
|
114
|
+
if lint_section is None:
|
|
115
|
+
return None
|
|
116
|
+
return _build_config(lint_section)
|
|
117
|
+
except Exception:
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _build_config(lint_data: dict) -> LintConfig:
|
|
122
|
+
"""Build LintConfig from a parsed TOML dict."""
|
|
123
|
+
config = LintConfig()
|
|
124
|
+
|
|
125
|
+
# Top-level lint settings
|
|
126
|
+
if "score-threshold" in lint_data:
|
|
127
|
+
config.score_threshold = int(lint_data["score-threshold"])
|
|
128
|
+
|
|
129
|
+
# Rule settings
|
|
130
|
+
rules = lint_data.get("rules", {})
|
|
131
|
+
|
|
132
|
+
# min-length: int threshold or false
|
|
133
|
+
if "min-length" in rules:
|
|
134
|
+
val = rules["min-length"]
|
|
135
|
+
config.min_length = int(val) if val else 0
|
|
136
|
+
|
|
137
|
+
# short-prompt: int threshold or false
|
|
138
|
+
if "short-prompt" in rules:
|
|
139
|
+
val = rules["short-prompt"]
|
|
140
|
+
config.short_prompt = int(val) if val else 0
|
|
141
|
+
|
|
142
|
+
# vague-prompt: bool
|
|
143
|
+
if "vague-prompt" in rules:
|
|
144
|
+
config.vague_prompt = bool(rules["vague-prompt"])
|
|
145
|
+
|
|
146
|
+
# debug-needs-reference: bool
|
|
147
|
+
if "debug-needs-reference" in rules:
|
|
148
|
+
config.debug_needs_reference = bool(rules["debug-needs-reference"])
|
|
149
|
+
|
|
150
|
+
# file-extensions: list
|
|
151
|
+
if "file-extensions" in rules:
|
|
152
|
+
config.file_extensions = list(rules["file-extensions"])
|
|
153
|
+
|
|
154
|
+
return config
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def lint_prompt(text: str, config: LintConfig | None = None) -> list[LintViolation]:
|
|
158
|
+
"""Lint a single prompt against quality rules."""
|
|
159
|
+
if config is None:
|
|
160
|
+
config = DEFAULT_CONFIG
|
|
161
|
+
|
|
162
|
+
violations: list[LintViolation] = []
|
|
163
|
+
stripped = text.strip().lower()
|
|
164
|
+
|
|
165
|
+
# Rule 1: Too short
|
|
166
|
+
if config.min_length > 0 and len(stripped) < config.min_length:
|
|
167
|
+
violations.append(
|
|
168
|
+
LintViolation(
|
|
169
|
+
rule="min-length",
|
|
170
|
+
severity="error",
|
|
171
|
+
message=f"Prompt is too short ({len(stripped)} chars, minimum {config.min_length})",
|
|
172
|
+
prompt_text=text,
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
elif config.short_prompt > 0 and len(stripped) < config.short_prompt:
|
|
176
|
+
violations.append(
|
|
177
|
+
LintViolation(
|
|
178
|
+
rule="short-prompt",
|
|
179
|
+
severity="warning",
|
|
180
|
+
message=(f"Prompt is short ({len(stripped)} chars) — consider adding context"),
|
|
181
|
+
prompt_text=text,
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Rule 2: Vague starter
|
|
186
|
+
if config.vague_prompt and stripped in VAGUE_STARTERS:
|
|
187
|
+
violations.append(
|
|
188
|
+
LintViolation(
|
|
189
|
+
rule="vague-prompt",
|
|
190
|
+
severity="error",
|
|
191
|
+
message="Prompt is too vague — specify what to fix/change and where",
|
|
192
|
+
prompt_text=text,
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Rule 3: No file/function reference in debug prompts
|
|
197
|
+
if config.debug_needs_reference:
|
|
198
|
+
debug_words = {"fix", "debug", "bug", "error", "broken", "failing", "crash"}
|
|
199
|
+
has_debug_intent = any(w in stripped for w in debug_words)
|
|
200
|
+
indicators = list(config.file_extensions) + ["()", "line ", "file "]
|
|
201
|
+
has_reference = any(indicator in text for indicator in indicators)
|
|
202
|
+
min_len = config.min_length if config.min_length > 0 else 20
|
|
203
|
+
if has_debug_intent and not has_reference and len(stripped) >= min_len:
|
|
204
|
+
violations.append(
|
|
205
|
+
LintViolation(
|
|
206
|
+
rule="debug-needs-reference",
|
|
207
|
+
severity="warning",
|
|
208
|
+
message="Debug prompt lacks file/function reference — add specifics",
|
|
209
|
+
prompt_text=text,
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return violations
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def lint_prompts(texts: list[str], config: LintConfig | None = None) -> list[LintViolation]:
|
|
217
|
+
"""Lint multiple prompts and return all violations."""
|
|
218
|
+
violations: list[LintViolation] = []
|
|
219
|
+
for text in texts:
|
|
220
|
+
violations.extend(lint_prompt(text, config))
|
|
221
|
+
return violations
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def format_lint_results(violations: list[LintViolation], total_prompts: int) -> str:
|
|
225
|
+
"""Format lint results for terminal/CI output."""
|
|
226
|
+
if not violations:
|
|
227
|
+
return f"✓ {total_prompts} prompts checked, no issues found"
|
|
228
|
+
|
|
229
|
+
errors = [v for v in violations if v.severity == "error"]
|
|
230
|
+
warnings = [v for v in violations if v.severity == "warning"]
|
|
231
|
+
|
|
232
|
+
lines: list[str] = []
|
|
233
|
+
lines.append(f"Checked {total_prompts} prompts\n")
|
|
234
|
+
|
|
235
|
+
for v in violations:
|
|
236
|
+
prefix = "✗" if v.severity == "error" else "!"
|
|
237
|
+
display = v.prompt_text[:60] + "..." if len(v.prompt_text) > 60 else v.prompt_text
|
|
238
|
+
lines.append(f' {prefix} [{v.rule}] "{display}"')
|
|
239
|
+
lines.append(f" {v.message}")
|
|
240
|
+
|
|
241
|
+
lines.append(f"\n{len(errors)} error(s), {len(warnings)} warning(s)")
|
|
242
|
+
return "\n".join(lines)
|