jfox-cli 1.4.0__tar.gz → 1.6.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.
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.claude/skills/release/SKILL.md +15 -4
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.claude/skills/release/release_helper.py +138 -6
- jfox_cli-1.6.0/.claude/skills/release-all/SKILL.md +111 -0
- jfox_cli-1.6.0/.claude/skills/release-all/release_all_helper.py +270 -0
- jfox_cli-1.6.0/.claude/skills/release-cc-plugin/SKILL.md +104 -0
- jfox_cli-1.6.0/.claude/skills/release-cc-plugin/release_cc_plugin_helper.py +239 -0
- jfox_cli-1.6.0/.claude/skills/release-kimi-plugin/SKILL.md +104 -0
- jfox_cli-1.6.0/.claude/skills/release-kimi-plugin/release_kimi_plugin_helper.py +215 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.claude-plugin/marketplace.json +2 -2
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/AGENTS.md +3 -1
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/CHANGELOG.md +31 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/CLAUDE.md +24 -4
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/PKG-INFO +29 -2
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/README.md +28 -1
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-12-release-cc-plugin-skill.md +681 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-12-show-json.md +393 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-17-fix-candidate-dual-h1.md +266 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-18-dedup-lifecycle-decouple.md +736 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-19-promote-skill-three-modes.md +354 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-25-bookshelf.md +1367 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-25-gem-synth-dedup-incremental-merge.md +1212 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-26-kb-backup-restore.md +1329 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-26-promote-skill-clear-reports-rewrite.md +173 -0
- jfox_cli-1.6.0/docs/superpowers/plans/2026-07-27-release-all.md +1219 -0
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-12-release-cc-plugin-skill-design.md +91 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-07-12-show-json-design.md +8 -3
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-18-dedup-lifecycle-decouple-design.md +119 -0
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-19-promote-skill-three-modes-design.md +94 -0
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-25-bookshelf-design.md +190 -0
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-25-gem-synth-dedup-incremental-merge-design.md +176 -0
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-26-kb-backup-restore-design.md +141 -0
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-26-promote-skill-clear-reports-rewrite-design.md +96 -0
- jfox_cli-1.6.0/docs/superpowers/specs/2026-07-27-release-all-orchestration-design.md +243 -0
- jfox_cli-1.6.0/jfox/__init__.py +15 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/loop.py +6 -0
- jfox_cli-1.6.0/jfox/backup/__init__.py +11 -0
- jfox_cli-1.6.0/jfox/backup/cli.py +245 -0
- jfox_cli-1.6.0/jfox/backup/loop.py +130 -0
- jfox_cli-1.6.0/jfox/backup/manager.py +504 -0
- jfox_cli-1.6.0/jfox/backup/schedule.py +52 -0
- jfox_cli-1.6.0/jfox/bookshelf/__init__.py +19 -0
- jfox_cli-1.6.0/jfox/bookshelf/cli.py +306 -0
- jfox_cli-1.6.0/jfox/bookshelf/meta.py +149 -0
- jfox_cli-1.6.0/jfox/bookshelf/store.py +320 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/cli.py +43 -7
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/daemon/server.py +51 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/cli.py +5 -2
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/dedup.py +55 -12
- jfox_cli-1.6.0/jfox/gem_synth/lifecycle.py +86 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/llm.py +58 -2
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/loop.py +6 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/store.py +20 -6
- jfox_cli-1.6.0/jfox/gem_synth/synthesizer.py +356 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/global_config.py +76 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/models.py +52 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/note.py +57 -51
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/.claude-plugin/plugin.json +1 -1
- jfox_cli-1.6.0/packages/cc-plugin/skills/bookshelf/SKILL.md +49 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/skills/manage/SKILL.md +3 -3
- jfox_cli-1.6.0/packages/cc-plugin/skills/promote/SKILL.md +229 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/kimi.plugin.json +1 -1
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-manage/SKILL.md +3 -3
- jfox_cli-1.6.0/packages/kimi-plugin/skills/jfox-promote/SKILL.md +323 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/using-jfox/SKILL.md +1 -1
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/pyproject.toml +1 -1
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/pi/jfox-common/SKILL.md +3 -3
- jfox_cli-1.6.0/tests/unit/bookshelf/__init__.py +0 -0
- jfox_cli-1.6.0/tests/unit/bookshelf/conftest.py +62 -0
- jfox_cli-1.6.0/tests/unit/bookshelf/test_cli.py +120 -0
- jfox_cli-1.6.0/tests/unit/bookshelf/test_meta.py +148 -0
- jfox_cli-1.6.0/tests/unit/bookshelf/test_store.py +408 -0
- jfox_cli-1.6.0/tests/unit/conftest.py +18 -0
- jfox_cli-1.6.0/tests/unit/test_backup.py +413 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_cli.py +29 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_config_dedup.py +14 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_dedup.py +12 -2
- jfox_cli-1.6.0/tests/unit/test_gem_synth_lifecycle.py +108 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_llm.py +86 -1
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_store.py +40 -0
- jfox_cli-1.6.0/tests/unit/test_gem_synth_synthesizer.py +256 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_note_dedup_sync.py +14 -0
- jfox_cli-1.6.0/tests/unit/test_note_lifecycle_hooks.py +81 -0
- jfox_cli-1.6.0/tests/unit/test_release_all_helper.py +293 -0
- jfox_cli-1.6.0/tests/unit/test_release_cc_plugin_helper.py +215 -0
- jfox_cli-1.6.0/tests/unit/test_release_helper_verify.py +211 -0
- jfox_cli-1.6.0/tests/unit/test_release_kimi_plugin_helper.py +172 -0
- jfox_cli-1.6.0/tests/unit/test_show.py +200 -0
- jfox_cli-1.6.0/tests/unit/test_synthesizer_dedup.py +589 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/uv.lock +1 -1
- jfox_cli-1.4.0/jfox/__init__.py +0 -5
- jfox_cli-1.4.0/jfox/gem_synth/synthesizer.py +0 -186
- jfox_cli-1.4.0/packages/cc-plugin/skills/promote/SKILL.md +0 -40
- jfox_cli-1.4.0/packages/kimi-plugin/skills/jfox-promote/SKILL.md +0 -154
- jfox_cli-1.4.0/tests/unit/test_gem_synth_synthesizer.py +0 -133
- jfox_cli-1.4.0/tests/unit/test_show.py +0 -73
- jfox_cli-1.4.0/tests/unit/test_synthesizer_dedup.py +0 -175
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.claude/skills/ci/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.githooks/pre-push +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.github/ISSUE_TEMPLATE/docs_improvement.yml +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.github/workflows/integration-test.yml +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.github/workflows/publish.yml +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.gitignore +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/.python-version +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/DEVELOPMENT_PLAN.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/SESSION.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/SESSION_SUMMARY.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/claude-code-plugin-guide.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/installation.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-11-bulk-import-bm25-fix.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-11-edit-command.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-11-unify-format-option.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-ci-coverage-optimization.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-edit-content-file.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-fix-index-rebuild-clear.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-fix-index-verify-id-mismatch.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-fix-jfox-health-skill-kb-param.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-index-kb-param.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-lazy-import-perf.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-12-skill-redesign.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-13-add-content-file.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-13-bulk-import-vectorstore-init.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-13-ingest-log-command.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-13-ingest-skill-sync.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-13-suppress-third-party-logging.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-13-sync-skills-with-cli.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-14-sync-docs-daemon-show.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-15-gpu-embedding.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-15-make-daemon-deps-required.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-15-session-summary-confirmation.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-16-fix-windows-daemon-console-window.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-18-daemon-timeout-and-index-rebuild.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-22-release-skill.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-26-fix-daemon-deprecation-warnings.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-27-intranet-model-download.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-28-tag-filtering.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-04-29-daemon-stop-fix.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-03-jfox-check.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-03-list-notes-skip-summary.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-03-log-level-fix.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-04-list-notes-index.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-06-atomic-write.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-09-session-note-type.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-10-jfox-plugin.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-19-fix-last-used-never.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-05-20-modelscope-migration.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-02-todo-note-type.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-16-kimi-code-auto-summary.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-17-index-rebuild-backlinks.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-17-using-jfox-skill.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-18-self-update-command.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-21-fragment-capture.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-21-gem-synthesis.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-06-25-gem-synth-throttle-ledger.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-07-08-auto-summary-schedule-window.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-07-08-candidate-promote.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-07-12-gem-synth-dedup.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/plans/2026-07-12-session-summary-streamline.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-03-bugfixes-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-12-skill-redesign-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-13-pr-auto-code-review-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-14-show-command-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-15-gpu-embedding-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-15-session-summary-confirmation-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-21-release-skill-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-26-fix-daemon-deprecation-warnings-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-27-intranet-model-download-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-28-tag-filtering-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-29-daemon-stop-fix-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-04-30-jfox-kb-env-var-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-03-jfox-check-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-03-list-notes-skip-summary-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-03-log-level-fix-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-04-list-notes-index-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-06-atomic-write-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-09-jfox-plugin-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-09-session-note-type-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-05-20-modelscope-migration-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-02-todo-note-type-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-16-kimi-code-auto-summary-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-17-index-rebuild-backlinks-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-17-using-jfox-skill-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-18-archive-soft-delete-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-18-self-update-command-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-21-fragment-capture-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-21-gem-synthesis-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-06-25-gem-synth-throttle-ledger-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-07-08-candidate-promote-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-07-12-gem-synth-dedup-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/superpowers/specs/2026-07-12-session-summary-streamline-design.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/docs/troubleshooting.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jessica-jones-static-cable.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/__main__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/extractor.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/kimi_source.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/ledger.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/runner.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/scanner.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/schedule.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/auto_summary/sources.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/bm25_index.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/config.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/daemon/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/daemon/__main__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/daemon/client.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/daemon/process.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/embedding_backend.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/formatters.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/fragment/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/fragment/cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/fragment/detector.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/fragment/internal_sources.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/fragment/service.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/fragment/store.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/anchors.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/grounding.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/paths.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/gem_synth/transcript.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/git_extractor.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/graph.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/indexer.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/kb_manager.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/model_downloader.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/note_index.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/performance.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/search_engine.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/template.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/template_cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/utils.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/jfox/vector_store.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/package.json +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/hooks/fragment-capture.sh +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/hooks/hooks.json +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/skills/ingest/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/skills/organize/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/skills/search/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/skills/session-summary/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/cc-plugin/skills/using-jfox/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/README.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-admin/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-auto-summary/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/packages/kimi-plugin/skills/jfox-template/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/pytest.ini +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/run_full_test.ps1 +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/scripts/download-model-intranet.sh +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/README.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/kimi-cli/jfox-common/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/kimi-cli/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/kimi-cli/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/kimi-cli/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/kimi-cli/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/pi/jfox-ci/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/pi/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/pi/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/pi/jfox-release/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/pi/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/skills-recommend/pi/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/COVERAGE_PLAN.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/MIGRATION.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/TESTS.md +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/conftest.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_auto_summary_kimi.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_backlinks.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_candidate_promote_flow.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_fragment_capture_flow.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_gem_synth_flow.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_index_rebuild_backlinks.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_model_download.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/integration/test_tag_filter_cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/performance/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/performance/test_performance.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_advanced_features.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_cli_format.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_config_set_unit.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_config_unit.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_core_workflow.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_embedding_device.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_hybrid_search.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_integration.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_kb_current.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/test_suggest_links.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_add_backfill_links.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_archive.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_atomic_write.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_config_sources.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_defaults.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_extractor.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_ledger.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_loop.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_runner.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_scanner.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_schedule.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_auto_summary_status.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_bm25_batch.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_check.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_content_file.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_daemon_cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_daemon_process.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_edit.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_embedding_local_load.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_format_unify.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_formatters.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_fragment_cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_fragment_config.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_fragment_detector.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_fragment_hook.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_fragment_internal_sources.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_fragment_service.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_fragment_store.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_anchors.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_backfill.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_config.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_grounding.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_loop.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_store_duplicate.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_gem_synth_transcript.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_git_extractor.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_global_config.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_index_kb_param.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_indexer_clear_before_rebuild.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_indexer_verify.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_kb_manager.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_kimi_extractor.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_kimi_plugin_skills.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_kimi_scanner.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_lazy_import.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_ledger_migration.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_list_notes_skip.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_logging_config.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_model_downloader.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_note_candidate_fields.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_note_candidate_filename.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_note_index.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_note_promote.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_note_type_candidate.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_rebuild_backlinks_impl.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_release_helper.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_runner_multi_source.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_scanner_list.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_session_file_source.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_session_note.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_session_source.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_system_prompt_sections.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_tag_filter.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_template.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_template_cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_update.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_use_kb_env_var.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_using_jfox_skill.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/unit/test_vector_store_clear.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/utils/__init__.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/utils/assertions.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/utils/jfox_cli.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/utils/note_generator.py +0 -0
- {jfox_cli-1.4.0 → jfox_cli-1.6.0}/tests/utils/temp_kb.py +0 -0
|
@@ -138,13 +138,24 @@ git pull origin main
|
|
|
138
138
|
|
|
139
139
|
### Step 9: 创建 GitHub Release
|
|
140
140
|
|
|
141
|
+
**先跑 verify 校验**(#333:核对 `last_tag..HEAD` 功能 commit 的 PR 号是否都进了 CHANGELOG 顶段,防 bump 后被外部 PR 抢先合入致漏项——v1.1.0/v1.5.0 踩过):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
uv run python .claude/skills/release/release_helper.py verify
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
退出码非 0 → 打印 missing 条目,**停止**,按下面流程修复后重跑(**不能直接重跑**——本地 main 须先拉取补丁,否则 verify 仍会失败):
|
|
148
|
+
|
|
149
|
+
1. 开 `docs(changelog)` PR,把 missing PR 号补进 CHANGELOG.md 顶段(首个 `## [...]` 版本段),合并到 main。
|
|
150
|
+
2. `git checkout main && git pull origin main`(拉取刚合并的 CHANGELOG 补丁)。
|
|
151
|
+
3. 重跑 `uv run python .claude/skills/release/release_helper.py verify`,退出码 0 才继续。
|
|
152
|
+
|
|
153
|
+
退出码 0 → 创建 Release。**release notes 必须取自当前 CHANGELOG.md 的顶段**(首个 `## [{new_version}]` 版本段的全部条目)——若中间经过 verify 补丁,顶段已含新增条目,**不要复用 Step 2/3 缓存的 `{changelog_preview}`**:
|
|
154
|
+
|
|
141
155
|
```bash
|
|
142
156
|
gh release create v{new_version} \
|
|
143
157
|
--title "v{new_version}" \
|
|
144
|
-
--notes "
|
|
145
|
-
{changelog_preview}
|
|
146
|
-
EOF
|
|
147
|
-
)"
|
|
158
|
+
--notes "<CHANGELOG.md 顶段(## [{new_version}] ... 到下一个 ## [ 之间)的全部条目>"
|
|
148
159
|
```
|
|
149
160
|
|
|
150
161
|
告知用户:
|
|
@@ -12,6 +12,7 @@ jfox release 辅助脚本
|
|
|
12
12
|
python release_helper.py 0.5.0 # 指定版本
|
|
13
13
|
python release_helper.py ... --dry-run # 只计算不修改文件
|
|
14
14
|
"""
|
|
15
|
+
|
|
15
16
|
import json
|
|
16
17
|
import re
|
|
17
18
|
import subprocess
|
|
@@ -145,8 +146,16 @@ def parse_commits(last_tag: str) -> list[dict]:
|
|
|
145
146
|
|
|
146
147
|
# -c core.quotepath=false 确保 git 输出中文不被转义
|
|
147
148
|
result = subprocess.run(
|
|
148
|
-
[
|
|
149
|
-
|
|
149
|
+
[
|
|
150
|
+
"git",
|
|
151
|
+
"-c",
|
|
152
|
+
"core.quotepath=false",
|
|
153
|
+
"-c",
|
|
154
|
+
"i18n.logoutputencoding=utf-8",
|
|
155
|
+
"log",
|
|
156
|
+
range_spec,
|
|
157
|
+
"--format=%s",
|
|
158
|
+
],
|
|
150
159
|
cwd=str(PROJECT_ROOT),
|
|
151
160
|
capture_output=True,
|
|
152
161
|
text=True,
|
|
@@ -204,7 +213,9 @@ def parse_commits(last_tag: str) -> list[dict]:
|
|
|
204
213
|
return entries
|
|
205
214
|
|
|
206
215
|
|
|
207
|
-
def generate_changelog(
|
|
216
|
+
def generate_changelog(
|
|
217
|
+
new_version: str, current_version: str, entries: list[dict], last_tag: str = ""
|
|
218
|
+
) -> str:
|
|
208
219
|
"""生成 CHANGELOG Markdown 内容"""
|
|
209
220
|
today = date.today().isoformat()
|
|
210
221
|
|
|
@@ -244,8 +255,7 @@ def generate_changelog(new_version: str, current_version: str, entries: list[dic
|
|
|
244
255
|
# 底部比较链接
|
|
245
256
|
tag_prev = last_tag.lstrip("v") if last_tag else current_version
|
|
246
257
|
lines.append(
|
|
247
|
-
f"[{new_version}]: https://github.com/zhuxixi/jfox/compare/"
|
|
248
|
-
f"v{tag_prev}...v{new_version}"
|
|
258
|
+
f"[{new_version}]: https://github.com/zhuxixi/jfox/compare/" f"v{tag_prev}...v{new_version}"
|
|
249
259
|
)
|
|
250
260
|
|
|
251
261
|
return "\n".join(lines)
|
|
@@ -289,9 +299,131 @@ def summarize_entries(entries: list[dict]) -> str:
|
|
|
289
299
|
return ", ".join(parts) if parts else "0 changes"
|
|
290
300
|
|
|
291
301
|
|
|
302
|
+
def _git(args: list[str], cwd: Path) -> subprocess.CompletedProcess:
|
|
303
|
+
"""git 调用封装(verify 用,cwd 可注入便于测试)。"""
|
|
304
|
+
return subprocess.run(
|
|
305
|
+
["git", "-c", "core.quotepath=false", "-c", "i18n.logoutputencoding=utf-8", *args],
|
|
306
|
+
cwd=str(cwd),
|
|
307
|
+
capture_output=True,
|
|
308
|
+
text=True,
|
|
309
|
+
encoding="utf-8",
|
|
310
|
+
errors="replace",
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _repo_has_v_tag(root: Path) -> bool:
|
|
315
|
+
"""仓库是否存在任意 v* tag(locale 无关地判定「有无 tag」)。
|
|
316
|
+
|
|
317
|
+
用 `git tag --list v*` 而非 stderr 文本——非英文 locale 下 git 的报错文案会被本地化,
|
|
318
|
+
靠 "No names found" 之类子串匹配会误判。tag --list 自身失败则 raise(真正 git 错误)。
|
|
319
|
+
"""
|
|
320
|
+
out = _git(["tag", "--list", "v*"], root)
|
|
321
|
+
if out.returncode != 0:
|
|
322
|
+
raise RuntimeError(f"git tag --list 失败: {out.stderr.strip()}")
|
|
323
|
+
return bool(out.stdout.strip())
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def functional_commits_since_last_tag(root: Path) -> list[str]:
|
|
327
|
+
"""last v* tag..HEAD 的功能类 commit subject(白名单:feat/fix/refactor/docs/perf)。
|
|
328
|
+
|
|
329
|
+
白名单比黑名单更抗格式偏差(非 conventional 提交 ctype='' 不入选);额外跳过
|
|
330
|
+
merge commit、含 "bump version" 的提交,以及 docs(changelog) 这类 CHANGELOG 维护
|
|
331
|
+
提交(其 PR 号本身就是补 CHANGELOG 的,不应反过来要求进 CHANGELOG,否则 verify 陷入
|
|
332
|
+
无限修复循环)。支持 conventional 破坏性标记(feat!:/feat(scope)!:)。
|
|
333
|
+
无 v* tag(首次发版)时返回 []——没有上一版基线,无从谈漂移。
|
|
334
|
+
git log 失败时 raise RuntimeError,由 verify() 捕获转 fail-closed。
|
|
335
|
+
"""
|
|
336
|
+
out = _git(["describe", "--tags", "--abbrev=0", "--match", "v*"], root)
|
|
337
|
+
if out.returncode != 0:
|
|
338
|
+
# 区分「无 v* tag」(合法,首次发版)与真正 git 错误。
|
|
339
|
+
# 用 tag --list(locale 无关)判定——非英文 locale 下 git stderr 会被本地化,不能靠文本匹配。
|
|
340
|
+
if not _repo_has_v_tag(root):
|
|
341
|
+
return []
|
|
342
|
+
raise RuntimeError(f"git describe 失败: {out.stderr.strip()}")
|
|
343
|
+
tag = out.stdout.strip()
|
|
344
|
+
out = _git(["log", f"{tag}..HEAD", "--format=%s"], root)
|
|
345
|
+
if out.returncode != 0:
|
|
346
|
+
raise RuntimeError(f"git log 失败({tag}..HEAD): {out.stderr.strip()}")
|
|
347
|
+
functional = {"feat", "fix", "refactor", "docs", "perf"}
|
|
348
|
+
result = []
|
|
349
|
+
for line in out.stdout.splitlines():
|
|
350
|
+
s = line.strip()
|
|
351
|
+
if not s or "bump version" in s.lower():
|
|
352
|
+
continue
|
|
353
|
+
if s.startswith("Merge "):
|
|
354
|
+
continue
|
|
355
|
+
# conventional: type[(scope)][!]: —— 捕获 type/scope,容忍破坏性标记 !
|
|
356
|
+
m = re.match(r"^(\w+)(?:\(([^)]*)\))?!?:", s)
|
|
357
|
+
if not m:
|
|
358
|
+
continue
|
|
359
|
+
ctype = m.group(1).lower()
|
|
360
|
+
scope = (m.group(2) or "").lower()
|
|
361
|
+
if ctype not in functional:
|
|
362
|
+
continue
|
|
363
|
+
if ctype == "docs" and "changelog" in scope:
|
|
364
|
+
continue # docs(changelog) 是补 CHANGELOG 的维护提交,跳过防 verify 死循环
|
|
365
|
+
result.append(s)
|
|
366
|
+
return result
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def changelog_top_prs(root: Path) -> set[int]:
|
|
370
|
+
"""解析 CHANGELOG.md 最新版本段(首个 ## [ ... 到下一个 ## [)内的 (#NNN) 集合。"""
|
|
371
|
+
cl = root / "CHANGELOG.md"
|
|
372
|
+
if not cl.exists():
|
|
373
|
+
return set()
|
|
374
|
+
text = cl.read_text(encoding="utf-8")
|
|
375
|
+
m = re.search(
|
|
376
|
+
r"^##\s*\[[^\]]+\][^\n]*\n(.*?)(?=^##\s*\[|\Z)",
|
|
377
|
+
text,
|
|
378
|
+
re.MULTILINE | re.DOTALL,
|
|
379
|
+
)
|
|
380
|
+
section = m.group(1) if m else text
|
|
381
|
+
return {int(x) for x in re.findall(r"\(#(\d+)\)", section)}
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def verify(root: Path | None = None) -> dict:
|
|
385
|
+
"""#333:创建 Release 前核对 last_tag..HEAD 功能 commit 的 PR 号是否都被 CHANGELOG 顶段收录。
|
|
386
|
+
|
|
387
|
+
返回 {"ok": bool, "missing": sorted[int], "extra": sorted[int], "functional_commits": int}。
|
|
388
|
+
ok=False 表示有功能 commit 的 PR 未进 CHANGELOG(漂移),应阻止发 Release。
|
|
389
|
+
数据源出错(git log 失败等)时 fail-closed:返回 ok=False + error,迫使流程停而非误判通过。
|
|
390
|
+
"""
|
|
391
|
+
root = Path(root) if root else PROJECT_ROOT
|
|
392
|
+
try:
|
|
393
|
+
func_prs: set[int] = set()
|
|
394
|
+
for s in functional_commits_since_last_tag(root):
|
|
395
|
+
# 只取 subject 末尾的 (#NNN)(squash 标题里的本提交 PR 号),
|
|
396
|
+
# 避免正文引用多个 PR(如协作)把非本提交 PR 误判为必须进 CHANGELOG。
|
|
397
|
+
m = re.search(r"\(#(\d+)\)\s*$", s)
|
|
398
|
+
if m:
|
|
399
|
+
func_prs.add(int(m.group(1)))
|
|
400
|
+
cl_prs = changelog_top_prs(root)
|
|
401
|
+
except (RuntimeError, OSError, UnicodeDecodeError, ValueError) as e:
|
|
402
|
+
return {
|
|
403
|
+
"ok": False,
|
|
404
|
+
"error": f"verify 失败: {e}",
|
|
405
|
+
"missing": [],
|
|
406
|
+
"extra": [],
|
|
407
|
+
"functional_commits": 0,
|
|
408
|
+
}
|
|
409
|
+
missing = sorted(func_prs - cl_prs)
|
|
410
|
+
extra = sorted(cl_prs - func_prs)
|
|
411
|
+
return {
|
|
412
|
+
"ok": not missing,
|
|
413
|
+
"missing": missing,
|
|
414
|
+
"extra": extra,
|
|
415
|
+
"functional_commits": len(func_prs),
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
|
|
292
419
|
def main():
|
|
420
|
+
if len(sys.argv) >= 2 and sys.argv[1] == "verify":
|
|
421
|
+
res = verify()
|
|
422
|
+
output_json(res)
|
|
423
|
+
sys.exit(0 if res["ok"] else 1)
|
|
424
|
+
|
|
293
425
|
if len(sys.argv) < 2:
|
|
294
|
-
output_error("用法: release_helper.py <version|patch|minor|major> [--dry-run]")
|
|
426
|
+
output_error("用法: release_helper.py <version|patch|minor|major|verify> [--dry-run]")
|
|
295
427
|
|
|
296
428
|
version_arg = sys.argv[1]
|
|
297
429
|
dry_run = "--dry-run" in sys.argv
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-all
|
|
3
|
+
description: Release all three components (jfox CLI + cc-plugin + kimi-plugin) in one command. Detects per-component changes since last release, skips unchanged ones, batches bump PRs, then creates the jfox GitHub Release last (with CHANGELOG verify). Triggers on "全发", "release all", "三件套发版", "一起发版".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release-All Skill
|
|
7
|
+
|
|
8
|
+
一条命令编排三条独立发版轨道:jfox CLI → cc-plugin → kimi-plugin。自动检测各组件自上次发版以来的改动,**跳过无改动者**;为有改动者批量建 bump PR;用户合并后,最后发 jfox GitHub Release(带 #333 verify 校验)。
|
|
9
|
+
|
|
10
|
+
⚠️ 三组件版本轨道独立、语义各自不同。本 skill 不统一版本号,只统一编排。
|
|
11
|
+
要单独发某一组件,用 `/release`、`/release-cc-plugin`、`/release-kimi-plugin`。
|
|
12
|
+
|
|
13
|
+
## 用法
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/release-all # 逐组件确认 suggested_bump
|
|
17
|
+
/release-all minor # 统一指定 bump 类型(套用到所有 changed 组件)
|
|
18
|
+
/release-all patch
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 编排模型
|
|
22
|
+
|
|
23
|
+
**批量建 PR + 最后发 jfox Release**:detect 全部 → 展示计划 → 为每个 changed 组件 bump+建 PR → 用户依次合并全部 PR → 拉最新 main → verify → 建 jfox GitHub Release。
|
|
24
|
+
|
|
25
|
+
## 执行流程
|
|
26
|
+
|
|
27
|
+
### Step 1: 前置校验(任一不符即停)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git branch --show-current # 期望 main
|
|
31
|
+
git status --porcelain # 期望 空
|
|
32
|
+
git branch --list 'chore/bump-*' # 期望 空(三组件任一 bump 分支都不能存在)
|
|
33
|
+
# gh pr list --head 只支持精确分支名(不支持通配),改用 json+grep 列 open bump PR
|
|
34
|
+
gh pr list --state open --json headRefName --jq '.[].headRefName' | grep 'chore/bump-' # 期望无输出
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Step 2: 检测
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv run python .claude/skills/release-all/release_all_helper.py detect
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Step 3: 展示合并计划 + 确认 bump 类型
|
|
44
|
+
|
|
45
|
+
展示 detect 结果:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
📦 Release-All 计划:
|
|
49
|
+
jfox 1.5.0 → 1.6.0 (minor) ✓ 有改动 [feat(backup)#339, feat(gem-synth)#335]
|
|
50
|
+
cc-plugin 0.6.0 → 0.6.1 (patch) ✓ 有改动 [docs(promote)#342]
|
|
51
|
+
kimi-plugin 0.14.0 ✗ 无改动,跳过
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- 命令带参数(`/release-all minor`)→ 统一套用到所有 changed 组件。
|
|
55
|
+
- 否则逐组件确认 suggested_bump(用户可改 patch/minor/major 或指定 x.y.z)。
|
|
56
|
+
- 全部 changed=false → 打印「三组件均无未发布改动,无需发版」并结束。
|
|
57
|
+
|
|
58
|
+
### Step 4: 逐组件 bump + 建 PR(顺序 jfox → cc → kimi,仅 changed 者)
|
|
59
|
+
|
|
60
|
+
detect 已算出 suggested_version 并经确认,跳过各单组件 skill 的 dry-run 预览步。
|
|
61
|
+
|
|
62
|
+
⚠️ **每个组件开始前必须先 `git checkout main`**:上一组件的 bump 提交落在它自己的分支上,若不回 main,下一组件的 `git checkout -b` 会从上一组件分支拉出,导致 PR diff 串进别的组件文件(如 cc PR 里混入 jfox 的 pyproject.toml)。
|
|
63
|
+
|
|
64
|
+
对每个 changed 组件,循环执行(helper 在 main 工作树改文件 → 从 main 拉 bump 分支 → commit → push → 开 PR):
|
|
65
|
+
|
|
66
|
+
1. `git checkout main`(确保下一组件从干净的 main 拉分支)
|
|
67
|
+
2. 正式 bump + 建分支 + commit + push + 开 PR(每条都用完整命令,`git push` 须带 `-u` 设 upstream):
|
|
68
|
+
- **jfox**:`uv run python .claude/skills/release/release_helper.py <v>` → `git checkout -b chore/bump-version-<v>` → `git add pyproject.toml jfox/__init__.py uv.lock CHANGELOG.md` → `git commit -m "chore: bump version to <v>"` → `git push -u origin chore/bump-version-<v>` → `gh pr create --title "chore: bump version to <v>" --body "<changelog_preview>"`
|
|
69
|
+
- **cc-plugin**:`uv run python .claude/skills/release-cc-plugin/release_cc_plugin_helper.py <v>` → `git checkout -b chore/bump-cc-plugin-<v>` → `git add packages/cc-plugin/.claude-plugin/plugin.json .claude-plugin/marketplace.json` → `git commit -m "chore(cc-plugin): bump version <cur> → <v>"` → `git push -u origin chore/bump-cc-plugin-<v>` → `gh pr create --title "chore(cc-plugin): bump version <cur> → <v>" --body "<changelog>"`
|
|
70
|
+
- **kimi-plugin**:`uv run python .claude/skills/release-kimi-plugin/release_kimi_plugin_helper.py <v>` → `git checkout -b chore/bump-kimi-plugin-<v>` → `git add packages/kimi-plugin/kimi.plugin.json` → `git commit -m "chore(kimi-plugin): bump version <cur> → <v>"` → `git push -u origin chore/bump-kimi-plugin-<v>` → `gh pr create --title "chore(kimi-plugin): bump version <cur> → <v>" --body "<changelog>"`
|
|
71
|
+
|
|
72
|
+
三组件文件不冲突,PR 可并存。收集所有 PR URL。
|
|
73
|
+
|
|
74
|
+
### Step 5: 告知用户合并
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
已创建 N 个 bump PR:
|
|
78
|
+
- jfox: <URL>
|
|
79
|
+
- cc-plugin: <URL>
|
|
80
|
+
- kimi-plugin: <URL>
|
|
81
|
+
请依次合并后告知我,我将继续创建 jfox GitHub Release(cc/kimi 合 main 即生效,无需 Release)。
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
等待用户确认全部合并。**只有 jfox 在计划内时才需要合并后回流;若 jfox 被跳过,cc/kimi 合完即结束。**
|
|
85
|
+
|
|
86
|
+
### Step 6: 发 jfox Release(仅当 jfox 在计划内)
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git checkout main && git pull origin main
|
|
90
|
+
uv run python .claude/skills/release/release_helper.py verify # #333 兜底
|
|
91
|
+
# verify 退出码 0 才继续:
|
|
92
|
+
gh release create v<jfox_ver> --title "v<jfox_ver>" --notes "<changelog_preview>"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
verify 非 0 → 打印 missing 条目,**停**,提示用户补 CHANGELOG(开 `docs(changelog)` PR 合并)后重跑 verify + release。**不自动建 Release。**
|
|
96
|
+
|
|
97
|
+
## 跳过提示
|
|
98
|
+
|
|
99
|
+
detect 阶段 changed=false 的组件,全程打印一次「<组件> 自 <ver> 以来无改动,跳过」后不再出现。
|
|
100
|
+
|
|
101
|
+
## 错误处理
|
|
102
|
+
|
|
103
|
+
- detect 某组件异常 → 该组件 changed=false + skip_reason,继续其他组件,不中断。
|
|
104
|
+
- 某组件 bump 中途失败 → 已建的早期组件 PR 保留(独立),报告失败组件、停。
|
|
105
|
+
- 用户只合并部分 PR → Step 6 不执行,提示「还有 X 个 PR 未合并」。
|
|
106
|
+
- verify 非 0 → 不建 Release,打印 missing,停。
|
|
107
|
+
|
|
108
|
+
## 与单组件 skill 的关系
|
|
109
|
+
|
|
110
|
+
- `/release`、`/release-cc-plugin`、`/release-kimi-plugin` 不变其单组件职责。
|
|
111
|
+
- 本 skill 是编排层,**委托**三组件 helper,自身只做 detect + 调度 + skip + verify 串接,无特判分支。
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
release-all 编排辅助脚本:检测三组件(jfox CLI / cc-plugin / kimi-plugin)未发布改动,
|
|
4
|
+
跳过无改动者。只做检测,不发版、不碰文件;发版动作由各组件 helper 负责。
|
|
5
|
+
|
|
6
|
+
用法:
|
|
7
|
+
python release_all_helper.py [detect] # 默认 detect
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
# 项目根目录(脚本位于 .claude/skills/release-all/,向上 3 级)
|
|
17
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
|
18
|
+
|
|
19
|
+
PYPROJECT_TOML = "pyproject.toml"
|
|
20
|
+
CC_PLUGIN_JSON = "packages/cc-plugin/.claude-plugin/plugin.json"
|
|
21
|
+
CC_MARKETPLACE = ".claude-plugin/marketplace.json"
|
|
22
|
+
KIMI_PLUGIN_JSON = "packages/kimi-plugin/kimi.plugin.json"
|
|
23
|
+
VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def output_json(data: dict) -> None:
|
|
27
|
+
print(json.dumps(data, ensure_ascii=False))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _git(args: list[str], root: Path) -> subprocess.CompletedProcess:
|
|
31
|
+
return subprocess.run(
|
|
32
|
+
[
|
|
33
|
+
"git",
|
|
34
|
+
"-c",
|
|
35
|
+
"core.quotepath=false",
|
|
36
|
+
"-c",
|
|
37
|
+
"i18n.logoutputencoding=utf-8",
|
|
38
|
+
*args,
|
|
39
|
+
],
|
|
40
|
+
cwd=str(root),
|
|
41
|
+
capture_output=True,
|
|
42
|
+
text=True,
|
|
43
|
+
encoding="utf-8",
|
|
44
|
+
errors="replace",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _bump_version(current: str, spec: str) -> str:
|
|
49
|
+
major, minor, patch = (int(x) for x in current.split("."))
|
|
50
|
+
if spec == "patch":
|
|
51
|
+
return f"{major}.{minor}.{patch + 1}"
|
|
52
|
+
if spec == "minor":
|
|
53
|
+
return f"{major}.{minor + 1}.0"
|
|
54
|
+
if spec == "major":
|
|
55
|
+
return f"{major + 1}.0.0"
|
|
56
|
+
raise ValueError(f"非法 bump 规格: {spec}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _repo_has_v_tag(root: Path) -> bool:
|
|
60
|
+
"""仓库是否存在任意 v* tag(locale 无关地判定「有无 tag」)。tag --list 自身失败则 raise。"""
|
|
61
|
+
out = _git(["tag", "--list", "v*"], root)
|
|
62
|
+
if out.returncode != 0:
|
|
63
|
+
raise RuntimeError(f"git tag --list 失败: {out.stderr.strip()}")
|
|
64
|
+
return bool(out.stdout.strip())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _last_jfox_tag(root: Path) -> str:
|
|
68
|
+
out = _git(["describe", "--tags", "--abbrev=0", "--match", "v*"], root)
|
|
69
|
+
if out.returncode != 0:
|
|
70
|
+
# 用 tag --list(locale 无关)区分「无 v* tag」与真正 git 错误,不依赖 stderr 文本
|
|
71
|
+
if not _repo_has_v_tag(root):
|
|
72
|
+
return "" # 无 v* tag(首次发版),合法
|
|
73
|
+
raise RuntimeError(f"git describe 失败: {out.stderr.strip()}")
|
|
74
|
+
return out.stdout.strip()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _find_last_bump_commit(root: Path, version: str, rel_path: str) -> str:
|
|
78
|
+
"""定位引入该 version 的提交(git log -S),fallback 到最后改该文件的提交。
|
|
79
|
+
|
|
80
|
+
单次 git 错误不致命:-S 失败则试 fallback,-S 无命中也试 fallback。
|
|
81
|
+
仅当两次 git 调用都出错(returncode!=0)才 raise(fail-closed),不计 stderr 文本。
|
|
82
|
+
"""
|
|
83
|
+
needle = f'"version": "{version}"'
|
|
84
|
+
errs: list[str] = []
|
|
85
|
+
for args in (
|
|
86
|
+
["log", "-S", needle, "--format=%H", "--", rel_path],
|
|
87
|
+
["log", "--format=%H", "-1", "--", rel_path],
|
|
88
|
+
):
|
|
89
|
+
out = _git(args, root)
|
|
90
|
+
if out.returncode != 0:
|
|
91
|
+
errs.append(out.stderr.strip())
|
|
92
|
+
continue # git 错误,试 fallback
|
|
93
|
+
hits = [ln for ln in out.stdout.splitlines() if ln.strip()]
|
|
94
|
+
if hits:
|
|
95
|
+
return hits[0]
|
|
96
|
+
# returncode 0 但无命中:继续试下一个 args(-S 无命中 → 试 fallback)
|
|
97
|
+
if len(errs) == 2:
|
|
98
|
+
raise RuntimeError(f"git log 失败(定位 bump commit): {'; '.join(e for e in errs if e)}")
|
|
99
|
+
return ""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _read_json_version(root: Path, rel: str) -> str:
|
|
103
|
+
v = json.loads((root / rel).read_text(encoding="utf-8"))["version"]
|
|
104
|
+
if not VERSION_RE.match(v):
|
|
105
|
+
raise ValueError(f"非法版本号格式: {v!r}({rel})")
|
|
106
|
+
return v
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _read_pyproject_version(root: Path) -> str:
|
|
110
|
+
m = re.search(
|
|
111
|
+
r'^version\s*=\s*"(\d+\.\d+\.\d+)"',
|
|
112
|
+
(root / PYPROJECT_TOML).read_text(encoding="utf-8"),
|
|
113
|
+
re.MULTILINE,
|
|
114
|
+
)
|
|
115
|
+
if not m:
|
|
116
|
+
raise ValueError("未在 pyproject.toml 找到 version")
|
|
117
|
+
return m.group(1)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def detect_jfox(root: Path) -> dict:
|
|
121
|
+
"""jfox CLI:last v* tag..HEAD 是否有非 bump 的功能性 commit。"""
|
|
122
|
+
# 阶段 1:读版本(文件缺失/格式错误)
|
|
123
|
+
try:
|
|
124
|
+
current = _read_pyproject_version(root)
|
|
125
|
+
except (OSError, ValueError) as e:
|
|
126
|
+
return {
|
|
127
|
+
"name": "jfox",
|
|
128
|
+
"changed": False,
|
|
129
|
+
"current_version": "",
|
|
130
|
+
"baseline": "",
|
|
131
|
+
"commits": [],
|
|
132
|
+
"skip_reason": f"读取版本失败: {e}",
|
|
133
|
+
}
|
|
134
|
+
# 阶段 2:git 操作(tag/log;RuntimeError=git 失败,OSError/SubprocessError=git 二进制缺失等)
|
|
135
|
+
tag = ""
|
|
136
|
+
try:
|
|
137
|
+
tag = _last_jfox_tag(root)
|
|
138
|
+
out = _git(["log", f"{tag}..HEAD" if tag else "HEAD", "--format=%s"], root)
|
|
139
|
+
if out.returncode != 0:
|
|
140
|
+
raise RuntimeError(f"git log 失败: {out.stderr.strip()}")
|
|
141
|
+
except (RuntimeError, OSError, subprocess.SubprocessError) as e:
|
|
142
|
+
return {
|
|
143
|
+
"name": "jfox",
|
|
144
|
+
"changed": False,
|
|
145
|
+
"current_version": current,
|
|
146
|
+
"baseline": tag,
|
|
147
|
+
"commits": [],
|
|
148
|
+
"skip_reason": f"git 操作失败: {e}",
|
|
149
|
+
}
|
|
150
|
+
subjects = [
|
|
151
|
+
s.strip() for s in out.stdout.splitlines() if s.strip() and "bump version" not in s.lower()
|
|
152
|
+
]
|
|
153
|
+
if not subjects:
|
|
154
|
+
return {
|
|
155
|
+
"name": "jfox",
|
|
156
|
+
"changed": False,
|
|
157
|
+
"current_version": current,
|
|
158
|
+
"baseline": tag,
|
|
159
|
+
"commits": [],
|
|
160
|
+
"skip_reason": f"自 {tag or '起点'} 以来无改动",
|
|
161
|
+
}
|
|
162
|
+
bump = "minor" if any(s.startswith("feat") for s in subjects) else "patch"
|
|
163
|
+
return {
|
|
164
|
+
"name": "jfox",
|
|
165
|
+
"changed": True,
|
|
166
|
+
"current_version": current,
|
|
167
|
+
"baseline": tag,
|
|
168
|
+
"commits": subjects,
|
|
169
|
+
"suggested_bump": bump,
|
|
170
|
+
"suggested_version": _bump_version(current, bump),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _detect_plugin(
|
|
175
|
+
root: Path, name: str, version_source: str, baseline_file: str, watch_paths: list[str]
|
|
176
|
+
) -> dict:
|
|
177
|
+
"""cc/kimi 通用检测:自上次 bump commit 起是否有改 watch_paths 的提交。
|
|
178
|
+
|
|
179
|
+
- version_source:含顶层 version 字段的文件(读当前版本)。
|
|
180
|
+
- baseline_file:git log -S 定位上次 bump 的文件(cc=marketplace.json、
|
|
181
|
+
kimi=kimi.plugin.json)。
|
|
182
|
+
- watch_paths:检测改动的路径(cc/kimi 各自 packages/ 目录)。
|
|
183
|
+
"""
|
|
184
|
+
# 阶段 1:读版本
|
|
185
|
+
try:
|
|
186
|
+
current = _read_json_version(root, version_source)
|
|
187
|
+
except (OSError, KeyError, ValueError) as e:
|
|
188
|
+
return {
|
|
189
|
+
"name": name,
|
|
190
|
+
"changed": False,
|
|
191
|
+
"current_version": "",
|
|
192
|
+
"baseline": "",
|
|
193
|
+
"commits": [],
|
|
194
|
+
"skip_reason": f"读取版本失败: {e}",
|
|
195
|
+
}
|
|
196
|
+
# 阶段 2:git 操作(baseline/log)
|
|
197
|
+
baseline = ""
|
|
198
|
+
try:
|
|
199
|
+
baseline = _find_last_bump_commit(root, current, baseline_file)
|
|
200
|
+
if baseline:
|
|
201
|
+
out = _git(["log", "--oneline", f"{baseline}..HEAD", "--", *watch_paths], root)
|
|
202
|
+
else:
|
|
203
|
+
out = _git(["log", "--oneline", "--max-count=30", "--", *watch_paths], root)
|
|
204
|
+
if out.returncode != 0:
|
|
205
|
+
raise RuntimeError(f"git log 失败: {out.stderr.strip()}")
|
|
206
|
+
except (RuntimeError, OSError, subprocess.SubprocessError) as e:
|
|
207
|
+
return {
|
|
208
|
+
"name": name,
|
|
209
|
+
"changed": False,
|
|
210
|
+
"current_version": current,
|
|
211
|
+
"baseline": baseline,
|
|
212
|
+
"commits": [],
|
|
213
|
+
"skip_reason": f"git 操作失败: {e}",
|
|
214
|
+
}
|
|
215
|
+
commits = [c for c in out.stdout.splitlines() if c.strip()]
|
|
216
|
+
if not commits:
|
|
217
|
+
return {
|
|
218
|
+
"name": name,
|
|
219
|
+
"changed": False,
|
|
220
|
+
"current_version": current,
|
|
221
|
+
"baseline": baseline,
|
|
222
|
+
"commits": [],
|
|
223
|
+
"skip_reason": f"自 {current} 以来无改动",
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
"name": name,
|
|
227
|
+
"changed": True,
|
|
228
|
+
"current_version": current,
|
|
229
|
+
"baseline": baseline,
|
|
230
|
+
"commits": commits,
|
|
231
|
+
"suggested_bump": "patch",
|
|
232
|
+
"suggested_version": _bump_version(current, "patch"),
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def detect_cc(root: Path) -> dict:
|
|
237
|
+
# plugin.json 含顶层 version(读版本);marketplace.json 用于 -S 定位 bump(与
|
|
238
|
+
# release_cc_plugin_helper 一致);watch packages/cc-plugin/(skills + plugin.json)
|
|
239
|
+
# 外加 marketplace.json 本身——改其非 version 元数据也算 cc 改动,该发版。
|
|
240
|
+
return _detect_plugin(
|
|
241
|
+
root,
|
|
242
|
+
"cc-plugin",
|
|
243
|
+
CC_PLUGIN_JSON,
|
|
244
|
+
CC_MARKETPLACE,
|
|
245
|
+
["packages/cc-plugin/", ".claude-plugin/marketplace.json"],
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def detect_kimi(root: Path) -> dict:
|
|
250
|
+
return _detect_plugin(
|
|
251
|
+
root, "kimi-plugin", KIMI_PLUGIN_JSON, KIMI_PLUGIN_JSON, ["packages/kimi-plugin/"]
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def detect(root: Path) -> dict:
|
|
256
|
+
comps = [detect_jfox(root), detect_cc(root), detect_kimi(root)]
|
|
257
|
+
return {"components": comps, "any_changed": any(c.get("changed") for c in comps)}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def main() -> None:
|
|
261
|
+
# 仅支持 detect(默认);无参数或参数为 detect 都走 detect
|
|
262
|
+
try:
|
|
263
|
+
output_json(detect(PROJECT_ROOT))
|
|
264
|
+
except (OSError, subprocess.SubprocessError, ValueError) as e:
|
|
265
|
+
output_json({"error": f"detect 失败: {e}"})
|
|
266
|
+
sys.exit(1)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
if __name__ == "__main__":
|
|
270
|
+
main()
|