pyqmd-mlx 0.7.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.
- pyqmd_mlx-0.7.0/.Justfile +50 -0
- pyqmd_mlx-0.7.0/.commitlintrc.mjs +47 -0
- pyqmd_mlx-0.7.0/.github/workflows/build.yml +61 -0
- pyqmd_mlx-0.7.0/.github/workflows/ci.yml +60 -0
- pyqmd_mlx-0.7.0/.github/workflows/pr-title.yml +24 -0
- pyqmd_mlx-0.7.0/.github/workflows/release.yml +87 -0
- pyqmd_mlx-0.7.0/.gitignore +60 -0
- pyqmd_mlx-0.7.0/.pre-commit-config.yaml +71 -0
- pyqmd_mlx-0.7.0/.python-version +1 -0
- pyqmd_mlx-0.7.0/.semrel/.components/changelog_header.md.j2 +9 -0
- pyqmd_mlx-0.7.0/.semrel/.components/changelog_init.md.j2 +29 -0
- pyqmd_mlx-0.7.0/.semrel/.components/changelog_update.md.j2 +71 -0
- pyqmd_mlx-0.7.0/.semrel/.components/changes.md.j2 +232 -0
- pyqmd_mlx-0.7.0/.semrel/.components/first_release.md.j2 +18 -0
- pyqmd_mlx-0.7.0/.semrel/.components/macros.md.j2 +251 -0
- pyqmd_mlx-0.7.0/.semrel/.components/unreleased_changes.md.j2 +7 -0
- pyqmd_mlx-0.7.0/.semrel/.components/versioned_changes.md.j2 +20 -0
- pyqmd_mlx-0.7.0/.semrel/.release_notes.md.j2 +62 -0
- pyqmd_mlx-0.7.0/.semrel/CHANGELOG.md.j2 +21 -0
- pyqmd_mlx-0.7.0/AGENTS.md +1 -0
- pyqmd_mlx-0.7.0/CHANGELOG.md +202 -0
- pyqmd_mlx-0.7.0/CLAUDE.md +229 -0
- pyqmd_mlx-0.7.0/COMMAND_STATUS.md +159 -0
- pyqmd_mlx-0.7.0/LICENSE +21 -0
- pyqmd_mlx-0.7.0/PKG-INFO +98 -0
- pyqmd_mlx-0.7.0/README.md +66 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-10-mlx-llm-layer-design.md +366 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-10-python-mlx-rewrite-roadmap.md +315 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-11-python-cli-design.md +207 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-11-python-mcp-server-design.md +202 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-11-python-storage-layer-design.md +283 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-12-python-metadata-filtering-design.md +439 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-12-python-node-parity-suite-design.md +373 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-13-python-update-command-design.md +556 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-15-python-folder-context-design.md +378 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-16-python-cleanup-command-design.md +217 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-17-collection-include-exclude-design.md +197 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-17-full-path-flag-design.md +226 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-17-multi-get-glob-support-design.md +248 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-17-parity-suite-mutating-commands-design.md +293 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-18-bench-command-design.md +269 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-18-skill-commands-design.md +197 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-19-ast-aware-chunking-design.md +268 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-20-github-ci-release-design.md +123 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-22-multi-agent-skills-design.md +119 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-24-cli-flow-output-text-parity-design.md +262 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-24-expand-model-gguf-weights-design.md +247 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-24-expand-model-hf-default-design.md +218 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-24-import-package-rename-design.md +135 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-24-mcp-http-parity-design.md +280 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-24-qrels-margin-calibration-design.md +206 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-25-bench-samples-design.md +234 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-25-expand-model-requantize-design.md +173 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-25-expansion-output-grammar-design.md +311 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-25-seeded-expansion-sampling-design.md +276 -0
- pyqmd_mlx-0.7.0/docs/specs/2026-09-28-pypi-publishing-design.md +282 -0
- pyqmd_mlx-0.7.0/parity/README.md +192 -0
- pyqmd_mlx-0.7.0/parity/__init__.py +0 -0
- pyqmd_mlx-0.7.0/parity/_mcp_client.py +70 -0
- pyqmd_mlx-0.7.0/parity/_node_cli.py +89 -0
- pyqmd_mlx-0.7.0/parity/_pyqmd_cli.py +39 -0
- pyqmd_mlx-0.7.0/parity/_quality_baseline.py +64 -0
- pyqmd_mlx-0.7.0/parity/_queries.py +32 -0
- pyqmd_mlx-0.7.0/parity/_snapshot_io.py +40 -0
- pyqmd_mlx-0.7.0/parity/_text_normalize.py +100 -0
- pyqmd_mlx-0.7.0/parity/_timing.py +61 -0
- pyqmd_mlx-0.7.0/parity/benchmark.py +288 -0
- pyqmd_mlx-0.7.0/parity/capture_node_snapshots.py +405 -0
- pyqmd_mlx-0.7.0/parity/conftest.py +47 -0
- pyqmd_mlx-0.7.0/parity/dataset_profile.py +84 -0
- pyqmd_mlx-0.7.0/parity/datasets/local/.gitkeep +0 -0
- pyqmd_mlx-0.7.0/parity/datasets/scifact.yaml +4 -0
- pyqmd_mlx-0.7.0/parity/fixtures/ast_chunking/sample.py +156 -0
- pyqmd_mlx-0.7.0/parity/fixtures/mutating/a.md +4 -0
- pyqmd_mlx-0.7.0/parity/fixtures/mutating/b.md +4 -0
- pyqmd_mlx-0.7.0/parity/fixtures/mutating/c.md +4 -0
- pyqmd_mlx-0.7.0/parity/fixtures/mutating/d.md +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/COMMIT.txt +2 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/collection_list_shows_active_profile.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/collection_show_active_profile.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/get_known_document.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/get_known_document_full_path.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/get_missing_document_errors_cleanly.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/ls_lists_active_profile_collection.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/multi_get_glob_pattern_matches.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/multi_get_two_documents.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/multi_get_two_documents_full_path.json +13 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/query_finds_sample_query.json +5 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/query_no_rerank.json +5 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/query_respects_min_score_1_0.json +5 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/query_top1_matches_verbatim_sentence.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/search_finds_sample_query.json +5 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/search_finds_sample_query_full_path.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/search_no_results.json +5 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/status_reports_index_health.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/vsearch_finds_sample_query.json +5 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli/vsearch_top1_matches_verbatim_sentence.json +4 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow/ast_chunking_embed.json +14 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow/collection_lifecycle.json +56 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow/context_lifecycle.json +49 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow/embed_lifecycle.json +27 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow/update_lifecycle.json +16 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow_raw/ast_chunking_embed.json +24 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow_raw/collection_lifecycle.json +88 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow_raw/context_lifecycle.json +52 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow_raw/embed_lifecycle.json +34 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_flow_raw/update_lifecycle.json +16 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/collection_list_shows_active_profile.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/collection_show_active_profile.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/get_known_document.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/get_known_document_full_path.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/get_missing_document_errors_cleanly.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/ls_lists_active_profile_collection.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/multi_get_glob_pattern_matches.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/multi_get_two_documents.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/multi_get_two_documents_full_path.json +12 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/query_finds_sample_query.json +11 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/query_no_rerank.json +12 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/query_respects_min_score_1_0.json +13 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/query_top1_matches_verbatim_sentence.json +11 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/search_finds_sample_query.json +11 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/search_finds_sample_query_full_path.json +12 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/search_no_results.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/status_reports_index_health.json +6 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/vsearch_finds_sample_query.json +11 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/cli_raw/vsearch_top1_matches_verbatim_sentence.json +11 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp/mcp_get_known_document.json +3 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp/mcp_multi_get_two_documents.json +3 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp/mcp_query_finds_sample_query.json +3 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp/mcp_query_nonsense_term_still_returns_hybrid_fallback.json +3 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp/mcp_query_top1_matches_verbatim_sentence.json +3 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp/mcp_status_reports_health.json +3 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp_raw/mcp_get_known_document.json +14 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp_raw/mcp_multi_get_two_documents.json +22 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp_raw/mcp_query_finds_sample_query.json +104 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp_raw/mcp_query_nonsense_term_still_returns_hybrid_fallback.json +104 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp_raw/mcp_query_top1_matches_verbatim_sentence.json +104 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/mcp_raw/mcp_status_reports_health.json +25 -0
- pyqmd_mlx-0.7.0/parity/node_ref/scifact/quality/node_query_baseline.json +166 -0
- pyqmd_mlx-0.7.0/parity/scenarios/__init__.py +0 -0
- pyqmd_mlx-0.7.0/parity/scenarios/_profile_fixtures.py +54 -0
- pyqmd_mlx-0.7.0/parity/scenarios/cli_flow_scenarios.py +492 -0
- pyqmd_mlx-0.7.0/parity/scenarios/cli_scenarios.py +308 -0
- pyqmd_mlx-0.7.0/parity/scenarios/mcp_scenarios.py +113 -0
- pyqmd_mlx-0.7.0/parity/test_quality.py +132 -0
- pyqmd_mlx-0.7.0/parity/test_structural.py +231 -0
- pyqmd_mlx-0.7.0/pyproject.toml +102 -0
- pyqmd_mlx-0.7.0/scripts/block-main-push.sh +28 -0
- pyqmd_mlx-0.7.0/scripts/compare_rerank_fixtures.py +207 -0
- pyqmd_mlx-0.7.0/scripts/convert_expand_gguf.py +251 -0
- pyqmd_mlx-0.7.0/scripts/prepare_scifact_corpus.py +73 -0
- pyqmd_mlx-0.7.0/scripts/replay_query.py +340 -0
- pyqmd_mlx-0.7.0/scripts/score_rerank_fixture.py +53 -0
- pyqmd_mlx-0.7.0/scripts/validate_store_query.py +90 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/__init__.py +0 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/bench/__init__.py +0 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/bench/_fixture.py +82 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/bench/_metrics.py +101 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/bench/_pathmatch.py +60 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/bench/_sampling.py +62 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/bench/runner.py +283 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/__init__.py +0 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_db.py +27 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_errors.py +48 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_format.py +95 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_fullpath.py +44 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_index_report.py +25 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_lineparse.py +30 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_metadata_filter.py +34 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_multiget.py +82 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_output_documents.py +146 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_output_search.py +247 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_progress.py +102 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_snippet.py +99 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_theme.py +27 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/_types.py +55 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/app.py +89 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/__init__.py +0 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/bench.py +170 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/cleanup.py +63 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/collection.py +220 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/context.py +115 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/documents.py +173 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/embed.py +83 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/mcp.py +67 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/pull.py +39 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/search.py +154 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/skill.py +171 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/skills.py +324 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/status.py +153 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/cli/commands/update.py +182 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/__init__.py +23 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/_cache.py +12 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/_constants.py +15 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/_expand_grammar.py +155 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/_hub.py +43 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/_prompts.py +60 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/embed.py +43 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/expand.py +195 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/llm/rerank.py +59 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/mcp/__init__.py +0 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/mcp/_errors.py +34 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/mcp/_formatting.py +52 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/mcp/_instructions.py +56 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/mcp/server.py +756 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/skills/pyqmd/SKILL.md +152 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/skills/pyqmd/references/mcp-setup.md +132 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/skills/pyqmd-bench/SKILL.md +71 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/skills/pyqmd-librarian/SKILL.md +52 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/skills/pyqmd-researcher/SKILL.md +43 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/__init__.py +3 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_ast.py +333 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_ast_queries.py +94 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_chunking.py +234 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_expansion.py +44 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_fts_query.py +109 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_indexing.py +186 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_intent.py +112 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_metadata.py +242 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_metadata_filter.py +362 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_rrf.py +53 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_schema.py +143 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_types.py +71 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/_yaml_alias_guard.py +33 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/store/store.py +1721 -0
- pyqmd_mlx-0.7.0/src/pyqmd_mlx/version.py +10 -0
- pyqmd_mlx-0.7.0/tests/.gitkeep +0 -0
- pyqmd_mlx-0.7.0/tests/__init__.py +0 -0
- pyqmd_mlx-0.7.0/tests/bench/__init__.py +0 -0
- pyqmd_mlx-0.7.0/tests/bench/test_fixture.py +98 -0
- pyqmd_mlx-0.7.0/tests/bench/test_metrics.py +136 -0
- pyqmd_mlx-0.7.0/tests/bench/test_pathmatch.py +69 -0
- pyqmd_mlx-0.7.0/tests/bench/test_runner.py +178 -0
- pyqmd_mlx-0.7.0/tests/bench/test_runner_samples.py +203 -0
- pyqmd_mlx-0.7.0/tests/bench/test_sampling.py +86 -0
- pyqmd_mlx-0.7.0/tests/cli/__init__.py +0 -0
- pyqmd_mlx-0.7.0/tests/cli/test_app.py +142 -0
- pyqmd_mlx-0.7.0/tests/cli/test_bench_command.py +269 -0
- pyqmd_mlx-0.7.0/tests/cli/test_cleanup_command.py +131 -0
- pyqmd_mlx-0.7.0/tests/cli/test_collection_command.py +544 -0
- pyqmd_mlx-0.7.0/tests/cli/test_context_command.py +229 -0
- pyqmd_mlx-0.7.0/tests/cli/test_db.py +48 -0
- pyqmd_mlx-0.7.0/tests/cli/test_e2e_real_models.py +54 -0
- pyqmd_mlx-0.7.0/tests/cli/test_embed_command.py +323 -0
- pyqmd_mlx-0.7.0/tests/cli/test_errors.py +46 -0
- pyqmd_mlx-0.7.0/tests/cli/test_format.py +104 -0
- pyqmd_mlx-0.7.0/tests/cli/test_fullpath.py +69 -0
- pyqmd_mlx-0.7.0/tests/cli/test_get_command.py +148 -0
- pyqmd_mlx-0.7.0/tests/cli/test_index_report.py +48 -0
- pyqmd_mlx-0.7.0/tests/cli/test_lineparse_shared.py +34 -0
- pyqmd_mlx-0.7.0/tests/cli/test_ls_command.py +139 -0
- pyqmd_mlx-0.7.0/tests/cli/test_mcp_command.py +158 -0
- pyqmd_mlx-0.7.0/tests/cli/test_metadata_filter_cli.py +29 -0
- pyqmd_mlx-0.7.0/tests/cli/test_multi_get_command.py +124 -0
- pyqmd_mlx-0.7.0/tests/cli/test_multiget_shared.py +120 -0
- pyqmd_mlx-0.7.0/tests/cli/test_output_documents.py +124 -0
- pyqmd_mlx-0.7.0/tests/cli/test_output_search.py +174 -0
- pyqmd_mlx-0.7.0/tests/cli/test_progress.py +121 -0
- pyqmd_mlx-0.7.0/tests/cli/test_pull_command.py +23 -0
- pyqmd_mlx-0.7.0/tests/cli/test_search_commands.py +536 -0
- pyqmd_mlx-0.7.0/tests/cli/test_skill_command.py +280 -0
- pyqmd_mlx-0.7.0/tests/cli/test_skills_command.py +281 -0
- pyqmd_mlx-0.7.0/tests/cli/test_snippet.py +61 -0
- pyqmd_mlx-0.7.0/tests/cli/test_status_command.py +276 -0
- pyqmd_mlx-0.7.0/tests/cli/test_update_command.py +437 -0
- pyqmd_mlx-0.7.0/tests/cli/test_version_flag.py +18 -0
- pyqmd_mlx-0.7.0/tests/conftest.py +54 -0
- pyqmd_mlx-0.7.0/tests/mcp/__init__.py +0 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_bootstrap.py +15 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_e2e_real_models.py +63 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_errors.py +72 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_formatting.py +91 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_http_origin_guard.py +396 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_instructions.py +45 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_locking.py +37 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_real_dispatch.py +194 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_server_get.py +100 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_server_http.py +198 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_server_multi_get.py +125 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_server_query.py +242 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_server_resource.py +30 -0
- pyqmd_mlx-0.7.0/tests/mcp/test_server_status.py +39 -0
- pyqmd_mlx-0.7.0/tests/parity/__init__.py +0 -0
- pyqmd_mlx-0.7.0/tests/parity/test_capture_node_snapshots.py +617 -0
- pyqmd_mlx-0.7.0/tests/parity/test_cli_dispatch_args.py +76 -0
- pyqmd_mlx-0.7.0/tests/parity/test_cli_flow_scenarios.py +202 -0
- pyqmd_mlx-0.7.0/tests/parity/test_cli_scenarios.py +143 -0
- pyqmd_mlx-0.7.0/tests/parity/test_conftest_fixture.py +6 -0
- pyqmd_mlx-0.7.0/tests/parity/test_dataset_profile.py +134 -0
- pyqmd_mlx-0.7.0/tests/parity/test_mcp_http_parity.py +175 -0
- pyqmd_mlx-0.7.0/tests/parity/test_mcp_scenarios.py +77 -0
- pyqmd_mlx-0.7.0/tests/parity/test_node_cli.py +109 -0
- pyqmd_mlx-0.7.0/tests/parity/test_quality_baseline.py +82 -0
- pyqmd_mlx-0.7.0/tests/parity/test_snapshot_io.py +19 -0
- pyqmd_mlx-0.7.0/tests/parity/test_text_normalize.py +99 -0
- pyqmd_mlx-0.7.0/tests/store/__init__.py +0 -0
- pyqmd_mlx-0.7.0/tests/store/test_ast.py +256 -0
- pyqmd_mlx-0.7.0/tests/store/test_bulk_operations.py +132 -0
- pyqmd_mlx-0.7.0/tests/store/test_chunking.py +222 -0
- pyqmd_mlx-0.7.0/tests/store/test_cleanup.py +164 -0
- pyqmd_mlx-0.7.0/tests/store/test_cli_capabilities.py +374 -0
- pyqmd_mlx-0.7.0/tests/store/test_collection_stats.py +50 -0
- pyqmd_mlx-0.7.0/tests/store/test_collections.py +208 -0
- pyqmd_mlx-0.7.0/tests/store/test_concurrency.py +127 -0
- pyqmd_mlx-0.7.0/tests/store/test_context.py +201 -0
- pyqmd_mlx-0.7.0/tests/store/test_documents.py +136 -0
- pyqmd_mlx-0.7.0/tests/store/test_expansion.py +77 -0
- pyqmd_mlx-0.7.0/tests/store/test_fts_query.py +57 -0
- pyqmd_mlx-0.7.0/tests/store/test_indexing.py +245 -0
- pyqmd_mlx-0.7.0/tests/store/test_indexing_scan.py +319 -0
- pyqmd_mlx-0.7.0/tests/store/test_integration.py +28 -0
- pyqmd_mlx-0.7.0/tests/store/test_intent.py +29 -0
- pyqmd_mlx-0.7.0/tests/store/test_llm_wrapping.py +114 -0
- pyqmd_mlx-0.7.0/tests/store/test_metadata_extraction.py +208 -0
- pyqmd_mlx-0.7.0/tests/store/test_metadata_filter_compile.py +200 -0
- pyqmd_mlx-0.7.0/tests/store/test_metadata_filter_parse.py +160 -0
- pyqmd_mlx-0.7.0/tests/store/test_metadata_persistence.py +167 -0
- pyqmd_mlx-0.7.0/tests/store/test_metadata_queries.py +105 -0
- pyqmd_mlx-0.7.0/tests/store/test_metadata_schema.py +101 -0
- pyqmd_mlx-0.7.0/tests/store/test_multi_collection.py +125 -0
- pyqmd_mlx-0.7.0/tests/store/test_orphan_cleanup.py +62 -0
- pyqmd_mlx-0.7.0/tests/store/test_query.py +386 -0
- pyqmd_mlx-0.7.0/tests/store/test_query_retrieval.py +106 -0
- pyqmd_mlx-0.7.0/tests/store/test_rrf.py +53 -0
- pyqmd_mlx-0.7.0/tests/store/test_schema.py +69 -0
- pyqmd_mlx-0.7.0/tests/store/test_search_fts.py +170 -0
- pyqmd_mlx-0.7.0/tests/store/test_search_vec.py +284 -0
- pyqmd_mlx-0.7.0/tests/store/test_types.py +48 -0
- pyqmd_mlx-0.7.0/tests/test_cache.py +31 -0
- pyqmd_mlx-0.7.0/tests/test_compare_rerank_fixtures.py +170 -0
- pyqmd_mlx-0.7.0/tests/test_convert_expand_gguf.py +97 -0
- pyqmd_mlx-0.7.0/tests/test_expand_grammar.py +160 -0
- pyqmd_mlx-0.7.0/tests/test_expand_model.py +188 -0
- pyqmd_mlx-0.7.0/tests/test_expand_sampling.py +106 -0
- pyqmd_mlx-0.7.0/tests/test_expand_seed.py +41 -0
- pyqmd_mlx-0.7.0/tests/test_hub.py +81 -0
- pyqmd_mlx-0.7.0/tests/test_integration.py +128 -0
- pyqmd_mlx-0.7.0/tests/test_packaging.py +41 -0
- pyqmd_mlx-0.7.0/tests/test_prepare_scifact_corpus.py +37 -0
- pyqmd_mlx-0.7.0/tests/test_prompts.py +66 -0
- pyqmd_mlx-0.7.0/tests/test_rerank.py +82 -0
- pyqmd_mlx-0.7.0/tests/test_score_rerank_fixture.py +31 -0
- pyqmd_mlx-0.7.0/uv.lock +2191 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
set shell := ["bash", "-uc"]
|
|
2
|
+
|
|
3
|
+
# Just runs the first target if no arguments are provided, so we set this to list all targets for safety.
|
|
4
|
+
_default:
|
|
5
|
+
@just --list
|
|
6
|
+
|
|
7
|
+
# Install/upgrade dev dependencies and activate pre-commit hooks.
|
|
8
|
+
[group('development')]
|
|
9
|
+
develop:
|
|
10
|
+
uv sync --dev
|
|
11
|
+
uv tool install pre-commit
|
|
12
|
+
pre-commit install
|
|
13
|
+
|
|
14
|
+
[group('quality')]
|
|
15
|
+
test *args:
|
|
16
|
+
uv run pytest {{args}} -v
|
|
17
|
+
|
|
18
|
+
[group('quality')]
|
|
19
|
+
test-fast:
|
|
20
|
+
uv run pytest -v -m "not slow"
|
|
21
|
+
|
|
22
|
+
[group('quality')]
|
|
23
|
+
test-slow:
|
|
24
|
+
uv run pytest -v -m slow
|
|
25
|
+
|
|
26
|
+
[group('quality')]
|
|
27
|
+
test-parity *args:
|
|
28
|
+
uv run pytest parity/ {{args}}
|
|
29
|
+
|
|
30
|
+
# Performance comparison against a Node qmd checkout at ../qmd (override with e.g. `just bench-scifact --qmd-repo-root /path/to/qmd`).
|
|
31
|
+
# Uses the built-in SciFact profile by default (override with `--dataset-config path/to/profile.yaml`,
|
|
32
|
+
# same as `test-parity`) -- its corpus lives in data/scifact/, gitignored and not shipped with the repo;
|
|
33
|
+
# generate it first with `uv run scripts/prepare_scifact_corpus.py`.
|
|
34
|
+
[group('quality')]
|
|
35
|
+
bench-scifact *args:
|
|
36
|
+
uv run python -m parity.benchmark --qmd-repo-root ../qmd {{args}}
|
|
37
|
+
|
|
38
|
+
[group('quality')]
|
|
39
|
+
lint:
|
|
40
|
+
uv run ruff check .
|
|
41
|
+
uv run ruff format --check .
|
|
42
|
+
|
|
43
|
+
# Install pyqmd as a real `pyqmd` command on PATH, editable (source changes apply without reinstalling).
|
|
44
|
+
[group('install')]
|
|
45
|
+
install:
|
|
46
|
+
uv tool install --editable .
|
|
47
|
+
|
|
48
|
+
[group('install')]
|
|
49
|
+
uninstall:
|
|
50
|
+
uv tool uninstall pyqmd-mlx
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
extends: ["@commitlint/config-conventional"],
|
|
3
|
+
rules: {
|
|
4
|
+
// 0 = off, 1 = warn, 2 = error | "always" / "never"
|
|
5
|
+
"scope-enum": [
|
|
6
|
+
2,
|
|
7
|
+
"always",
|
|
8
|
+
[
|
|
9
|
+
"store", // src/pyqmd_mlx/store/ -- schema, Store class, search/query
|
|
10
|
+
"cli", // src/pyqmd_mlx/cli/ -- Typer commands, output formatting
|
|
11
|
+
"mcp", // src/pyqmd_mlx/mcp/ -- MCP server (stdio/HTTP)
|
|
12
|
+
"llm", // src/pyqmd_mlx/llm/ -- MLX embed/rerank/query-expansion layer
|
|
13
|
+
"bench", // src/pyqmd_mlx/bench/ and the bench command
|
|
14
|
+
"skills", // src/pyqmd_mlx/skills/ and the skill/skills commands
|
|
15
|
+
"parity", // parity/ -- Node-vs-pyqmd golden-snapshot suite
|
|
16
|
+
"scripts", // scripts/ -- dev utility scripts
|
|
17
|
+
"config", // pyproject.toml, .pre-commit-config.yaml, Justfile, .gitignore
|
|
18
|
+
"docs", // README/CLAUDE.md/COMMAND_STATUS.md and any docs/ that aren't specs
|
|
19
|
+
"specs", // docs/specs/ -- published design/spec docs
|
|
20
|
+
"tests", // test-only changes
|
|
21
|
+
],
|
|
22
|
+
],
|
|
23
|
+
"scope-empty": [0, "never"], // off - some commits legitimately have no scope
|
|
24
|
+
"type-enum": [
|
|
25
|
+
2,
|
|
26
|
+
"always",
|
|
27
|
+
[
|
|
28
|
+
// conventional commits standard types
|
|
29
|
+
"feat",
|
|
30
|
+
"fix",
|
|
31
|
+
"docs",
|
|
32
|
+
"style",
|
|
33
|
+
"refactor",
|
|
34
|
+
"perf",
|
|
35
|
+
"test",
|
|
36
|
+
"build",
|
|
37
|
+
"ci",
|
|
38
|
+
"chore",
|
|
39
|
+
"revert",
|
|
40
|
+
// escape hatch — never bumps version, never appears in changelog
|
|
41
|
+
"doh",
|
|
42
|
+
// changes a reviewer asked for on an open PR — like doh, never released or logged
|
|
43
|
+
"review",
|
|
44
|
+
],
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Build
|
|
2
|
+
|
|
3
|
+
# Reusable: ci.yml runs it on every PR/push, release.yml on the new tag.
|
|
4
|
+
# No credentials: release.yml's publish job only downloads this artifact.
|
|
5
|
+
on:
|
|
6
|
+
workflow_call:
|
|
7
|
+
inputs:
|
|
8
|
+
ref:
|
|
9
|
+
description: Commit or tag to build
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
expected-version:
|
|
13
|
+
description: If set, the wheel's `pyqmd --version` must print exactly this
|
|
14
|
+
required: false
|
|
15
|
+
type: string
|
|
16
|
+
default: ""
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
# Apple Silicon, the only platform pyqmd supports. GitHub's macOS
|
|
24
|
+
# runners have no Metal access: this proves install + CLI startup,
|
|
25
|
+
# never that the MLX models run (those are `just test-slow`, locally).
|
|
26
|
+
runs-on: macos-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v7
|
|
29
|
+
with:
|
|
30
|
+
ref: ${{ inputs.ref }}
|
|
31
|
+
persist-credentials: false
|
|
32
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
33
|
+
with:
|
|
34
|
+
python-version: "3.12"
|
|
35
|
+
# Same reason as ci.yml's macOS test leg: the runner's preinstalled
|
|
36
|
+
# Python lacks sqlite3 loadable-extension support.
|
|
37
|
+
- name: Force managed Python
|
|
38
|
+
run: echo "UV_PYTHON_PREFERENCE=only-managed" >> "$GITHUB_ENV"
|
|
39
|
+
- run: uv build
|
|
40
|
+
- run: uvx twine check --strict dist/*
|
|
41
|
+
# Installs the wheel with the *latest* dependencies (no uv.lock), like
|
|
42
|
+
# a user's `uv tool install pyqmd-mlx` -- the one check that catches a
|
|
43
|
+
# new dependency release breaking pyqmd.
|
|
44
|
+
- name: Smoke-test the wheel
|
|
45
|
+
env:
|
|
46
|
+
EXPECTED_VERSION: ${{ inputs.expected-version }}
|
|
47
|
+
run: |
|
|
48
|
+
export UV_TOOL_DIR="$RUNNER_TEMP/uv-tools" UV_TOOL_BIN_DIR="$RUNNER_TEMP/uv-bin"
|
|
49
|
+
uv tool install dist/*.whl
|
|
50
|
+
"$UV_TOOL_BIN_DIR/pyqmd" --help > /dev/null
|
|
51
|
+
version=$("$UV_TOOL_BIN_DIR/pyqmd" --version)
|
|
52
|
+
echo "pyqmd --version: $version"
|
|
53
|
+
if [ -n "$EXPECTED_VERSION" ] && [ "$version" != "$EXPECTED_VERSION" ]; then
|
|
54
|
+
echo "::error::expected pyqmd --version $EXPECTED_VERSION, got $version"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
- uses: actions/upload-artifact@v7
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
- run: uv sync --dev
|
|
20
|
+
- run: uv run ruff check .
|
|
21
|
+
- run: uv run ruff format --check .
|
|
22
|
+
|
|
23
|
+
test:
|
|
24
|
+
needs: [lint]
|
|
25
|
+
strategy:
|
|
26
|
+
# Pinned, not floating: mlx ships manylinux_2_35+ wheels only, so
|
|
27
|
+
# ubuntu-24.04 (glibc 2.39) is the floor -- an older ubuntu image
|
|
28
|
+
# cannot even resolve uv.lock. macos-latest is ARM, covering the
|
|
29
|
+
# Apple Silicon import path.
|
|
30
|
+
matrix:
|
|
31
|
+
os: [ubuntu-24.04, macos-latest]
|
|
32
|
+
runs-on: ${{ matrix.os }}
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v7
|
|
35
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.12"
|
|
38
|
+
# macOS only: uv's discovery prefers the runner-preinstalled
|
|
39
|
+
# python-build-standalone (higher patch version wins), whose macOS
|
|
40
|
+
# builds lack sqlite3 loadable extension support -- every Store()
|
|
41
|
+
# construction then dies in load_sqlite_vec with AttributeError.
|
|
42
|
+
# only-managed forces the astral build setup-uv just installed
|
|
43
|
+
# (verified to have enable_load_extension). Ubuntu is untouched
|
|
44
|
+
# (green as-is). Done as a conditional step rather than job-level
|
|
45
|
+
# env so the ubuntu leg keeps default discovery byte-for-byte.
|
|
46
|
+
- name: Force managed Python on macOS
|
|
47
|
+
if: matrix.os == 'macos-latest'
|
|
48
|
+
run: echo "UV_PYTHON_PREFERENCE=only-managed" >> "$GITHUB_ENV"
|
|
49
|
+
- run: uv sync --dev
|
|
50
|
+
# No data/ or models/ on a fresh checkout (both gitignored): the
|
|
51
|
+
# scifact corpus (self-generatable but 33MB + slow) and the
|
|
52
|
+
# self-converted expansion weights (not fetchable anywhere) are
|
|
53
|
+
# absent, so requires_scifact_corpus / requires_expansion_weights
|
|
54
|
+
# tests skip with a clear reason (see tests/conftest.py).
|
|
55
|
+
- run: uv run pytest -m "not slow" -q
|
|
56
|
+
|
|
57
|
+
build:
|
|
58
|
+
uses: ./.github/workflows/build.yml
|
|
59
|
+
with:
|
|
60
|
+
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: PR title
|
|
2
|
+
|
|
3
|
+
# A squash merge of a multi-commit PR takes the PR title as its commit
|
|
4
|
+
# subject, and semantic-release reads that subject to decide the release.
|
|
5
|
+
# The commit-msg hook never sees it, so lint it here.
|
|
6
|
+
on:
|
|
7
|
+
pull_request:
|
|
8
|
+
types: [opened, edited, reopened, synchronize]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
pr-title:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
- uses: actions/setup-node@v7
|
|
19
|
+
with:
|
|
20
|
+
node-version: lts/*
|
|
21
|
+
- run: npm install --no-save --no-package-lock @commitlint/cli@21.2.3 @commitlint/config-conventional@21.2.3
|
|
22
|
+
- run: printf '%s\n' "$PR_TITLE" | npx --no-install commitlint --config .commitlintrc.mjs
|
|
23
|
+
env:
|
|
24
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: ["CI"]
|
|
6
|
+
types: [completed]
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
# Each job declares its own permissions.
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release:
|
|
14
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
concurrency: release
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write
|
|
19
|
+
outputs:
|
|
20
|
+
released: ${{ steps.result.outputs.released }}
|
|
21
|
+
tag: ${{ steps.result.outputs.tag }}
|
|
22
|
+
version: ${{ steps.result.outputs.version }}
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v7
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
fetch-tags: true
|
|
28
|
+
token: ${{ secrets.RELEASE_PAT }}
|
|
29
|
+
- id: before
|
|
30
|
+
run: echo "head=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
|
31
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
- run: uv tool install python-semantic-release==10.6.1
|
|
35
|
+
- run: semantic-release version --skip-build --no-commit --no-tag --no-changelog
|
|
36
|
+
env:
|
|
37
|
+
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
|
|
38
|
+
- run: |
|
|
39
|
+
uv lock --upgrade-package pyqmd-mlx
|
|
40
|
+
git add uv.lock
|
|
41
|
+
- run: semantic-release version
|
|
42
|
+
env:
|
|
43
|
+
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
|
|
44
|
+
# A release moves HEAD to PSR's release commit; no release leaves it.
|
|
45
|
+
- id: result
|
|
46
|
+
env:
|
|
47
|
+
BEFORE: ${{ steps.before.outputs.head }}
|
|
48
|
+
run: |
|
|
49
|
+
if [ "$(git rev-parse HEAD)" != "$BEFORE" ]; then
|
|
50
|
+
tag=$(git tag --points-at HEAD --list 'v*' | head -1)
|
|
51
|
+
{
|
|
52
|
+
echo "released=true"
|
|
53
|
+
echo "tag=$tag"
|
|
54
|
+
echo "version=${tag#v}"
|
|
55
|
+
} >> "$GITHUB_OUTPUT"
|
|
56
|
+
else
|
|
57
|
+
echo "released=false" >> "$GITHUB_OUTPUT"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
build:
|
|
61
|
+
needs: release
|
|
62
|
+
if: ${{ needs.release.outputs.released == 'true' }}
|
|
63
|
+
permissions:
|
|
64
|
+
contents: read
|
|
65
|
+
uses: ./.github/workflows/build.yml
|
|
66
|
+
with:
|
|
67
|
+
# The tag, not main: another merge may land after `release` checked out.
|
|
68
|
+
ref: ${{ needs.release.outputs.tag }}
|
|
69
|
+
expected-version: ${{ needs.release.outputs.version }}
|
|
70
|
+
|
|
71
|
+
publish:
|
|
72
|
+
needs: [release, build]
|
|
73
|
+
runs-on: ubuntu-latest # the PyPA action is a Docker action
|
|
74
|
+
# PyPI's trusted publisher matches workflow release.yml + environment pypi,
|
|
75
|
+
# so only this job can get a PyPI token. No checkout, no repo code.
|
|
76
|
+
environment:
|
|
77
|
+
name: pypi
|
|
78
|
+
url: https://pypi.org/project/pyqmd-mlx/${{ needs.release.outputs.version }}/
|
|
79
|
+
permissions:
|
|
80
|
+
id-token: write
|
|
81
|
+
contents: read
|
|
82
|
+
steps:
|
|
83
|
+
- uses: actions/download-artifact@v8
|
|
84
|
+
with:
|
|
85
|
+
name: dist
|
|
86
|
+
path: dist/
|
|
87
|
+
- uses: pypa/gh-action-pypi-publish@v1.14.2
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
|
|
8
|
+
# uv / venv
|
|
9
|
+
.venv/
|
|
10
|
+
|
|
11
|
+
# tooling caches
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
|
|
15
|
+
# env / secrets
|
|
16
|
+
.env
|
|
17
|
+
|
|
18
|
+
# editor / OS
|
|
19
|
+
.DS_Store
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
22
|
+
|
|
23
|
+
# generated data and models (large, re-fetchable)
|
|
24
|
+
data/
|
|
25
|
+
models/
|
|
26
|
+
|
|
27
|
+
# A user's own dataset profile + private data for the parity suite --
|
|
28
|
+
# never committed. .gitkeep above keeps the directory itself tracked.
|
|
29
|
+
parity/datasets/local/*
|
|
30
|
+
!parity/datasets/local/.gitkeep
|
|
31
|
+
|
|
32
|
+
# A user's own captured Node baseline lands in parity/node_ref/<profile>/
|
|
33
|
+
# regardless of where their profile YAML lives -- ignore every profile's
|
|
34
|
+
# captured data there except the built-in scifact one (the project's own
|
|
35
|
+
# tracked golden-reference data). Without this, README's "your data and
|
|
36
|
+
# captured baseline never get committed" claim was false for anything
|
|
37
|
+
# outside parity/datasets/local/ itself.
|
|
38
|
+
parity/node_ref/*
|
|
39
|
+
!parity/node_ref/scifact/
|
|
40
|
+
|
|
41
|
+
# Scratch Node collection-registry/index-DB state the capture script
|
|
42
|
+
# creates as isolation artifacts, not intended captured output --
|
|
43
|
+
# baked-in absolute paths make these non-portable across machines.
|
|
44
|
+
parity/node_ref/*/config/
|
|
45
|
+
parity/node_ref/*/_capture*.sqlite*
|
|
46
|
+
parity/node_ref/*/_structural_capture/
|
|
47
|
+
parity/node_ref/*/_quality_capture/
|
|
48
|
+
parity/node_ref/*/_mcp_capture/
|
|
49
|
+
parity/node_ref/*/_flow_*_capture/
|
|
50
|
+
|
|
51
|
+
# benchmark.py's generated report -- machine- and moment-dependent timing
|
|
52
|
+
# numbers, not something to track in git.
|
|
53
|
+
parity/BENCHMARK_RESULTS.md
|
|
54
|
+
|
|
55
|
+
# isolated git worktrees (per using-git-worktrees skill)
|
|
56
|
+
.worktrees/
|
|
57
|
+
|
|
58
|
+
# Private specs, plans, notes, leak-check tooling and history archive --
|
|
59
|
+
# see CLAUDE.md "Workflow". Public specs live in docs/specs/.
|
|
60
|
+
docs/superpowers/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Run 'just develop' to install pre-commit and activate these hooks.
|
|
3
|
+
default_install_hook_types: [pre-commit, commit-msg, pre-push]
|
|
4
|
+
default_stages: [pre-commit]
|
|
5
|
+
|
|
6
|
+
repos:
|
|
7
|
+
# --- General hygiene ---
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v5.0.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- id: end-of-file-fixer
|
|
13
|
+
- id: check-merge-conflict
|
|
14
|
+
- id: check-added-large-files
|
|
15
|
+
- id: detect-private-key
|
|
16
|
+
- id: mixed-line-ending
|
|
17
|
+
- id: check-yaml
|
|
18
|
+
- id: check-json
|
|
19
|
+
- id: check-toml
|
|
20
|
+
|
|
21
|
+
# --- Python lint/format ---
|
|
22
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
23
|
+
rev: v0.15.16
|
|
24
|
+
hooks:
|
|
25
|
+
- id: ruff-check
|
|
26
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
27
|
+
types_or: [python, pyi]
|
|
28
|
+
- id: ruff-format
|
|
29
|
+
|
|
30
|
+
# --- Commit message linting (Conventional Commits) ---
|
|
31
|
+
# Uses `npx --yes commitlint` directly (already available on PATH) rather than
|
|
32
|
+
# a pre-commit-managed hook repo. This mirrors the system/local hook pattern
|
|
33
|
+
# used for prettier below.
|
|
34
|
+
# Requires @commitlint/cli and @commitlint/config-conventional installed
|
|
35
|
+
# globally (npm i -g @commitlint/cli @commitlint/config-conventional) --
|
|
36
|
+
# language: system means pre-commit can't manage this dependency itself.
|
|
37
|
+
- repo: local
|
|
38
|
+
hooks:
|
|
39
|
+
- id: commitlint
|
|
40
|
+
name: commitlint
|
|
41
|
+
language: system
|
|
42
|
+
entry: npx --yes commitlint --edit --config .commitlintrc.mjs
|
|
43
|
+
stages: [commit-msg]
|
|
44
|
+
pass_filenames: false
|
|
45
|
+
always_run: true
|
|
46
|
+
|
|
47
|
+
# --- Prettier, for markdown/yaml/json only ---
|
|
48
|
+
# Uses whatever `prettier` is on PATH rather than a pre-commit-managed
|
|
49
|
+
# mirror repo.
|
|
50
|
+
- repo: local
|
|
51
|
+
hooks:
|
|
52
|
+
- id: prettier
|
|
53
|
+
name: prettier
|
|
54
|
+
language: system
|
|
55
|
+
entry: prettier --write --ignore-unknown
|
|
56
|
+
types_or: [markdown, yaml, json]
|
|
57
|
+
stages: [pre-commit]
|
|
58
|
+
|
|
59
|
+
# --- Block direct pushes to main (client-side backstop) ---
|
|
60
|
+
# Local safety net only -- bypassable with `git push --no-verify` and only
|
|
61
|
+
# active for whoever has run `just develop` in their own checkout. The
|
|
62
|
+
# actual enforcement point is GitHub branch protection on the upstream repo.
|
|
63
|
+
- repo: local
|
|
64
|
+
hooks:
|
|
65
|
+
- id: block-main-push
|
|
66
|
+
name: block direct pushes to main
|
|
67
|
+
language: system
|
|
68
|
+
entry: scripts/block-main-push.sh
|
|
69
|
+
pass_filenames: false
|
|
70
|
+
always_run: true
|
|
71
|
+
stages: [pre-push]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{#
|
|
2
|
+
This changelog template initializes a full changelog for the project,
|
|
3
|
+
it follows the following logic:
|
|
4
|
+
1. Header
|
|
5
|
+
2. Any Unreleased Details (uncommon)
|
|
6
|
+
3. all previous releases except the very first release
|
|
7
|
+
4. the first release
|
|
8
|
+
|
|
9
|
+
#}{#
|
|
10
|
+
# Header
|
|
11
|
+
#}{% include "changelog_header.md.j2"
|
|
12
|
+
-%}{#
|
|
13
|
+
# Any Unreleased Details (uncommon)
|
|
14
|
+
#}{% include "unreleased_changes.md.j2"
|
|
15
|
+
-%}{#
|
|
16
|
+
# Since this is initialization, we are generating all the previous
|
|
17
|
+
# release notes per version. The very first release notes is specialized
|
|
18
|
+
#}{% if releases | length > 0
|
|
19
|
+
%}{% for release in releases
|
|
20
|
+
%}{{ "\n"
|
|
21
|
+
}}{% if loop.last and ctx.mask_initial_release
|
|
22
|
+
%}{%- include "first_release.md.j2"
|
|
23
|
+
-%}{% else
|
|
24
|
+
%}{%- include "versioned_changes.md.j2"
|
|
25
|
+
-%}{% endif
|
|
26
|
+
%}{{ "\n"
|
|
27
|
+
}}{% endfor
|
|
28
|
+
%}{% endif
|
|
29
|
+
%}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{#
|
|
2
|
+
This Update changelog template uses the following logic:
|
|
3
|
+
|
|
4
|
+
1. Read previous changelog file (ex. project_root/CHANGELOG.md)
|
|
5
|
+
2. Split on insertion flag (ex. <!-- version list -->)
|
|
6
|
+
3. Print top half of previous changelog
|
|
7
|
+
3. New Changes (unreleased commits & newly released)
|
|
8
|
+
4. Print bottom half of previous changelog
|
|
9
|
+
|
|
10
|
+
Note: if a previous file was not found, it does not write anything at the bottom
|
|
11
|
+
but render does NOT fail
|
|
12
|
+
|
|
13
|
+
#}{% set prev_changelog_contents = prev_changelog_file | read_file | safe
|
|
14
|
+
%}{% set changelog_parts = prev_changelog_contents.split(insertion_flag, maxsplit=1)
|
|
15
|
+
%}{#
|
|
16
|
+
#}{% if changelog_parts | length < 2
|
|
17
|
+
%}{# # insertion flag was not found, check if the file was empty or did not exist
|
|
18
|
+
#}{% if prev_changelog_contents | length > 0
|
|
19
|
+
%}{# # File has content but no insertion flag, therefore, file will not be updated
|
|
20
|
+
#}{{ changelog_parts[0]
|
|
21
|
+
}}{% else
|
|
22
|
+
%}{# # File was empty or did not exist, therefore, it will be created from scratch
|
|
23
|
+
#}{% include "changelog_init.md.j2"
|
|
24
|
+
%}{% endif
|
|
25
|
+
%}{% else
|
|
26
|
+
%}{#
|
|
27
|
+
# Previous Changelog Header
|
|
28
|
+
# - Depending if there is header content, then it will separate the insertion flag
|
|
29
|
+
# with a newline from header content, otherwise it will just print the insertion flag
|
|
30
|
+
#}{% set prev_changelog_top = changelog_parts[0] | trim
|
|
31
|
+
%}{% if prev_changelog_top | length > 0
|
|
32
|
+
%}{{
|
|
33
|
+
"%s\n\n%s\n" | format(prev_changelog_top, insertion_flag | trim)
|
|
34
|
+
|
|
35
|
+
}}{% else
|
|
36
|
+
%}{{
|
|
37
|
+
"%s\n" | format(insertion_flag | trim)
|
|
38
|
+
|
|
39
|
+
}}{% endif
|
|
40
|
+
%}{#
|
|
41
|
+
# Any Unreleased Details (uncommon)
|
|
42
|
+
#}{% include "unreleased_changes.md.j2"
|
|
43
|
+
-%}{#
|
|
44
|
+
#}{% if releases | length > 0
|
|
45
|
+
%}{# # Latest Release Details
|
|
46
|
+
#}{% set release = releases[0]
|
|
47
|
+
%}{#
|
|
48
|
+
#}{% if releases | length == 1 and ctx.mask_initial_release
|
|
49
|
+
%}{# # First Release detected
|
|
50
|
+
#}{{ "\n"
|
|
51
|
+
}}{%- include "first_release.md.j2"
|
|
52
|
+
-%}{{ "\n"
|
|
53
|
+
}}{#
|
|
54
|
+
#}{% elif "# " ~ release.version.as_semver_tag() ~ " " not in changelog_parts[1]
|
|
55
|
+
%}{# # The release version is not already in the changelog so we add it
|
|
56
|
+
#}{{ "\n"
|
|
57
|
+
}}{%- include "versioned_changes.md.j2"
|
|
58
|
+
-%}{{ "\n"
|
|
59
|
+
}}{#
|
|
60
|
+
#}{% endif
|
|
61
|
+
%}{% endif
|
|
62
|
+
%}{#
|
|
63
|
+
# Previous Changelog Footer
|
|
64
|
+
# - skips printing footer if empty, which happens when the insertion_flag
|
|
65
|
+
# was at the end of the file (ignoring whitespace)
|
|
66
|
+
#}{% set previous_changelog_bottom = changelog_parts[1] | trim
|
|
67
|
+
%}{% if previous_changelog_bottom | length > 0
|
|
68
|
+
%}{{ "\n%s\n" | format(previous_changelog_bottom)
|
|
69
|
+
}}{% endif
|
|
70
|
+
%}{% endif
|
|
71
|
+
%}
|