jfox-cli 1.0.0__tar.gz → 1.1.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.0.0 → jfox_cli-1.1.0}/CHANGELOG.md +11 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/PKG-INFO +8 -1
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/README.md +7 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/installation.md +26 -0
- jfox_cli-1.1.0/docs/superpowers/plans/2026-06-17-index-rebuild-backlinks.md +166 -0
- jfox_cli-1.1.0/docs/superpowers/plans/2026-06-18-self-update-command.md +175 -0
- jfox_cli-1.1.0/docs/superpowers/specs/2026-06-17-index-rebuild-backlinks-design.md +99 -0
- jfox_cli-1.1.0/docs/superpowers/specs/2026-06-18-archive-soft-delete-design.md +160 -0
- jfox_cli-1.1.0/docs/superpowers/specs/2026-06-18-self-update-command-design.md +149 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/__init__.py +1 -1
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/cli.py +689 -10
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/models.py +18 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/note.py +76 -2
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/note_index.py +49 -2
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/search_engine.py +164 -56
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/pyproject.toml +1 -1
- jfox_cli-1.1.0/tests/integration/test_index_rebuild_backlinks.py +207 -0
- jfox_cli-1.1.0/tests/unit/test_archive.py +494 -0
- jfox_cli-1.1.0/tests/unit/test_rebuild_backlinks_impl.py +218 -0
- jfox_cli-1.1.0/tests/unit/test_update.py +587 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/jfox_cli.py +5 -2
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/uv.lock +1 -1
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/settings.local.json +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/skills/ci/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/skills/release/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/skills/release/release_helper.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude-plugin/marketplace.json +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.githooks/pre-push +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/docs_improvement.yml +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/workflows/integration-test.yml +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/workflows/publish.yml +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.gitignore +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.python-version +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/AGENTS.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/CLAUDE.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/DEVELOPMENT_PLAN.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/SESSION.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/SESSION_SUMMARY.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/claude-code-plugin-guide.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-11-bulk-import-bm25-fix.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-11-edit-command.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-11-unify-format-option.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-ci-coverage-optimization.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-edit-content-file.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-fix-index-rebuild-clear.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-fix-index-verify-id-mismatch.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-fix-jfox-health-skill-kb-param.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-index-kb-param.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-lazy-import-perf.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-skill-redesign.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-add-content-file.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-bulk-import-vectorstore-init.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-ingest-log-command.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-ingest-skill-sync.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-suppress-third-party-logging.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-sync-skills-with-cli.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-14-sync-docs-daemon-show.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-15-gpu-embedding.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-15-make-daemon-deps-required.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-15-session-summary-confirmation.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-16-fix-windows-daemon-console-window.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-18-daemon-timeout-and-index-rebuild.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-22-release-skill.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-26-fix-daemon-deprecation-warnings.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-27-intranet-model-download.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-28-tag-filtering.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-29-daemon-stop-fix.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-03-jfox-check.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-03-list-notes-skip-summary.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-03-log-level-fix.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-04-list-notes-index.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-06-atomic-write.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-09-session-note-type.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-10-jfox-plugin.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-19-fix-last-used-never.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-20-modelscope-migration.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-06-02-todo-note-type.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-06-16-kimi-code-auto-summary.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-06-17-using-jfox-skill.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-03-bugfixes-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-12-skill-redesign-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-13-pr-auto-code-review-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-14-show-command-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-15-gpu-embedding-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-15-session-summary-confirmation-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-21-release-skill-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-26-fix-daemon-deprecation-warnings-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-27-intranet-model-download-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-28-tag-filtering-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-29-daemon-stop-fix-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-30-jfox-kb-env-var-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-03-jfox-check-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-03-list-notes-skip-summary-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-03-log-level-fix-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-04-list-notes-index-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-06-atomic-write-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-09-jfox-plugin-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-09-session-note-type-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-20-modelscope-migration-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-06-02-todo-note-type-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-06-16-kimi-code-auto-summary-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-06-17-using-jfox-skill-design.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/troubleshooting.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jessica-jones-static-cable.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/__main__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/__init__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/cli.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/extractor.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/kimi_source.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/ledger.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/loop.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/runner.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/scanner.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/sources.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/bm25_index.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/config.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/__init__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/__main__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/client.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/process.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/server.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/embedding_backend.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/formatters.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/git_extractor.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/global_config.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/graph.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/indexer.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/kb_manager.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/model_downloader.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/performance.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/template.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/template_cli.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/utils.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/vector_store.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/package.json +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/.claude-plugin/plugin.json +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/ingest/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/manage/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/organize/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/search/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/session-summary/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/using-jfox/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/README.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/kimi.plugin.json +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-manage/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/using-jfox/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/pytest.ini +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/run_full_test.ps1 +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/scripts/download-model-intranet.sh +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/README.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-common/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-ci/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-common/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-release/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/COVERAGE_PLAN.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/MIGRATION.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/TESTS.md +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/conftest.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/__init__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_auto_summary_kimi.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_backlinks.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_model_download.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_tag_filter_cli.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/performance/__init__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/performance/test_performance.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_advanced_features.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_cli_format.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_config_set_unit.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_config_unit.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_core_workflow.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_embedding_device.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_hybrid_search.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_integration.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_kb_current.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_suggest_links.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/__init__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_atomic_write.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_config_sources.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_defaults.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_extractor.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_ledger.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_runner.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_scanner.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_status.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_bm25_batch.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_check.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_content_file.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_daemon_cli.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_daemon_process.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_edit.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_embedding_local_load.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_format_unify.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_formatters.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_git_extractor.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_global_config.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_index_kb_param.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_indexer_clear_before_rebuild.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_indexer_verify.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_kb_manager.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_kimi_extractor.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_kimi_scanner.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_lazy_import.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_ledger_migration.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_list_notes_skip.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_logging_config.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_model_downloader.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_note_index.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_release_helper.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_runner_multi_source.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_scanner_list.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_session_file_source.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_session_note.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_session_source.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_show.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_system_prompt_sections.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_tag_filter.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_template.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_template_cli.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_use_kb_env_var.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_using_jfox_skill.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_vector_store_clear.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/__init__.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/assertions.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/note_generator.py +0 -0
- {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/temp_kb.py +0 -0
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to jfox-cli will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.1.0] - 2026-06-21
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
- **cli**: add self-update command (#258)
|
|
9
|
+
- 笔记归档/软删除功能(archive/unarchive) (#260)
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
- **cli**: jfox index rebuild --backlinks recalculates wiki links and backlinks
|
|
13
|
+
|
|
14
|
+
[1.1.0]: https://github.com/zhuxixi/jfox/compare/v1.0.0...v1.1.0
|
|
15
|
+
|
|
5
16
|
## [1.0.0] - 2026-06-18
|
|
6
17
|
|
|
7
18
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jfox-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: JFox - Zettelkasten 知识管理 CLI 工具
|
|
5
5
|
Project-URL: Homepage, https://github.com/zhuxixi/jfox
|
|
6
6
|
Project-URL: Repository, https://github.com/zhuxixi/jfox
|
|
@@ -392,6 +392,13 @@ A session is considered "finished" when its file has not been modified for `idle
|
|
|
392
392
|
|
|
393
393
|
> **Privacy note:** Auto-summary sends session text to Anthropic API via `claude -p` to generate summaries. Only session content is transmitted.
|
|
394
394
|
|
|
395
|
+
### Self-Update
|
|
396
|
+
|
|
397
|
+
| Command | Description |
|
|
398
|
+
|---------|-------------|
|
|
399
|
+
| `jfox update` | Upgrade jfox to the latest version (auto-detects pip/pipx/uv) |
|
|
400
|
+
| `jfox update --json` | JSON output with before/after version info |
|
|
401
|
+
|
|
395
402
|
### Global Options
|
|
396
403
|
|
|
397
404
|
| Option | Description |
|
|
@@ -353,6 +353,13 @@ A session is considered "finished" when its file has not been modified for `idle
|
|
|
353
353
|
|
|
354
354
|
> **Privacy note:** Auto-summary sends session text to Anthropic API via `claude -p` to generate summaries. Only session content is transmitted.
|
|
355
355
|
|
|
356
|
+
### Self-Update
|
|
357
|
+
|
|
358
|
+
| Command | Description |
|
|
359
|
+
|---------|-------------|
|
|
360
|
+
| `jfox update` | Upgrade jfox to the latest version (auto-detects pip/pipx/uv) |
|
|
361
|
+
| `jfox update --json` | JSON output with before/after version info |
|
|
362
|
+
|
|
356
363
|
### Global Options
|
|
357
364
|
|
|
358
365
|
| Option | Description |
|
|
@@ -28,6 +28,32 @@ jfox --version
|
|
|
28
28
|
pip install -e ".[dev]"
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## Upgrade
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Auto-detect installation method and upgrade
|
|
35
|
+
jfox update
|
|
36
|
+
|
|
37
|
+
# JSON output with version info
|
|
38
|
+
jfox update --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If `jfox update` fails (e.g. behind a proxy or on an unsupported install method), use the manual command for your install method:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# uv tool users
|
|
45
|
+
uv tool upgrade jfox-cli
|
|
46
|
+
|
|
47
|
+
# pipx users
|
|
48
|
+
pipx upgrade jfox-cli
|
|
49
|
+
|
|
50
|
+
# pip users
|
|
51
|
+
pip install --upgrade jfox-cli
|
|
52
|
+
|
|
53
|
+
# Development mode (git clone + uv sync --extra dev)
|
|
54
|
+
git pull && uv sync --extra dev
|
|
55
|
+
```
|
|
56
|
+
|
|
31
57
|
## Uninstall
|
|
32
58
|
|
|
33
59
|
```bash
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Issue #252: `jfox index rebuild` 重新计算 backlinks Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** 让 `jfox index rebuild` 支持 `--backlinks` 选项,在重建索引时重新解析 wiki links 并计算 backlinks。
|
|
6
|
+
|
|
7
|
+
**Architecture:** 在 `jfox/cli.py` 中新增 `_rebuild_backlinks_impl()` 内部函数,在 `index rebuild` action 中按用户传入的 `--backlinks` 标志调用;函数全量加载笔记、解析 `[[...]]` 链接、解析目标 ID、重新计算并写回变化的笔记。
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python, Typer, pytest
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: 实现 `_rebuild_backlinks_impl()` 内部函数
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Modify: `jfox/cli.py`(在 `extract_wiki_links` / `find_note_id_by_title_or_id` 附近或 `_add_note_impl` 之后新增函数)
|
|
17
|
+
|
|
18
|
+
- [ ] **Step 1: 编写核心函数**
|
|
19
|
+
|
|
20
|
+
新增函数签名:
|
|
21
|
+
```python
|
|
22
|
+
def _rebuild_backlinks_impl(output_format: str = "table") -> Dict[str, Any]:
|
|
23
|
+
...
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
功能:
|
|
27
|
+
1. 调用 `note.list_notes(limit=10000)` 加载所有笔记
|
|
28
|
+
2. 遍历笔记,调用 `extract_wiki_links(note.content)` 提取链接
|
|
29
|
+
3. 调用 `find_note_id_by_title_or_id(link_text)` 解析目标 ID
|
|
30
|
+
4. 构建 `new_links: Dict[str, List[str]]` 和 `new_backlinks: Dict[str, List[str]]`
|
|
31
|
+
5. 对每个笔记,比较现有 `links`/`backlinks` 与新计算结果
|
|
32
|
+
6. 如有变化,更新 Note 对象并调用 `note.save_note(note, add_to_index=False)`
|
|
33
|
+
7. 返回统计信息:`backlinks_rebuilt`, `backlinks_updated`, `backlinks_total`, `unresolved_links`
|
|
34
|
+
|
|
35
|
+
- [ ] **Step 2: 处理边界情况**
|
|
36
|
+
|
|
37
|
+
- 空知识库:返回 `backlinks_total=0`, `backlinks_updated=0`
|
|
38
|
+
- 未解析链接:收集到 `unresolved_links` 列表中用于警告输出
|
|
39
|
+
- 保存失败:记录 warning,不中断流程
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
### Task 2: 在 `index rebuild` action 中集成 `--backlinks` 选项
|
|
44
|
+
|
|
45
|
+
**Files:**
|
|
46
|
+
- Modify: `jfox/cli.py:index()` 命令参数
|
|
47
|
+
- Modify: `jfox/cli.py` 中 `action == "rebuild"` 分支
|
|
48
|
+
|
|
49
|
+
- [ ] **Step 1: 添加 Typer 选项**
|
|
50
|
+
|
|
51
|
+
在 `index()` 命令中新增:
|
|
52
|
+
```python
|
|
53
|
+
backlinks: bool = typer.Option(False, "--backlinks", "-b", help="重建时重新计算 backlinks"),
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- [ ] **Step 2: 在 rebuild action 中调用**
|
|
57
|
+
|
|
58
|
+
在 `action == "rebuild"` 分支中,BM25 重建完成后:
|
|
59
|
+
```python
|
|
60
|
+
if backlinks:
|
|
61
|
+
bl_result = _rebuild_backlinks_impl(output_format)
|
|
62
|
+
result.update(bl_result)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- [ ] **Step 3: 更新输出**
|
|
66
|
+
|
|
67
|
+
JSON 输出:合并 `backlinks_rebuilt`, `backlinks_updated`, `backlinks_total`, `unresolved_links`
|
|
68
|
+
Table 输出:增加对应行,显示 `Backlinks rebuilt: X / Y`,以及未解析链接警告
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
### Task 3: 编写测试
|
|
73
|
+
|
|
74
|
+
**Files:**
|
|
75
|
+
- New: `tests/integration/test_index_rebuild_backlinks.py`
|
|
76
|
+
- New: `tests/unit/test_rebuild_backlinks_impl.py`
|
|
77
|
+
|
|
78
|
+
- [ ] **Step 1: 编写集成测试**
|
|
79
|
+
|
|
80
|
+
`tests/integration/test_index_rebuild_backlinks.py`:
|
|
81
|
+
- 使用 `cli` fixture 初始化临时知识库
|
|
82
|
+
- 创建目标笔记 A 和源笔记 B(B 引用 A)
|
|
83
|
+
- 手动修改 A 的 frontmatter,移除其 `backlinks`
|
|
84
|
+
- 执行 `cli.index("rebuild", "--backlinks")`
|
|
85
|
+
- 验证 `refs A` 的 `backward_links` 包含 B
|
|
86
|
+
- 验证 JSON 输出中的 `backlinks_updated > 0`
|
|
87
|
+
|
|
88
|
+
- [ ] **Step 2: 编写单元测试**
|
|
89
|
+
|
|
90
|
+
`tests/unit/test_rebuild_backlinks_impl.py`:
|
|
91
|
+
- Mock `note.list_notes()` 返回若干笔记对象
|
|
92
|
+
- 调用 `_rebuild_backlinks_impl()`
|
|
93
|
+
- 验证统计信息正确
|
|
94
|
+
- 验证 `save_note` 只在变化时调用
|
|
95
|
+
|
|
96
|
+
- [ ] **Step 3: 验证默认行为不变**
|
|
97
|
+
|
|
98
|
+
集成测试增加:
|
|
99
|
+
- 默认 `jfox index rebuild`(无 `--backlinks`)不应修改任何笔记的 backlinks
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### Task 4: 运行测试与代码检查
|
|
104
|
+
|
|
105
|
+
- [ ] **Step 1: 运行新增单元测试**
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv run pytest tests/unit/test_rebuild_backlinks_impl.py -v
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- [ ] **Step 2: 运行新增集成测试**
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run pytest tests/integration/test_index_rebuild_backlinks.py -v
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- [ ] **Step 3: 运行快速测试集**
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv run pytest tests/ -m "not embedding and not slow" -q
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- [ ] **Step 4: 代码风格检查**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
uv run ruff check jfox/cli.py tests/unit/test_rebuild_backlinks_impl.py tests/integration/test_index_rebuild_backlinks.py
|
|
127
|
+
uv run black --check jfox/cli.py tests/unit/test_rebuild_backlinks_impl.py tests/integration/test_index_rebuild_backlinks.py
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### Task 5: 提交与 PR
|
|
133
|
+
|
|
134
|
+
- [ ] **Step 1: 整理提交**
|
|
135
|
+
|
|
136
|
+
使用 Conventional Commits:
|
|
137
|
+
```bash
|
|
138
|
+
git add jfox/cli.py tests/unit/test_rebuild_backlinks_impl.py tests/integration/test_index_rebuild_backlinks.py docs/superpowers/specs/2026-06-17-index-rebuild-backlinks-design.md docs/superpowers/plans/2026-06-17-index-rebuild-backlinks.md
|
|
139
|
+
git commit -m "fix(cli): jfox index rebuild --backlinks recalculates wiki links and backlinks
|
|
140
|
+
|
|
141
|
+
- Add --backlinks/-b option to jfox index rebuild
|
|
142
|
+
- Add _rebuild_backlinks_impl() to parse [[...]] links and recompute backlinks
|
|
143
|
+
- Update output JSON/table with backlinks rebuild stats
|
|
144
|
+
- Add unit and integration tests
|
|
145
|
+
|
|
146
|
+
Closes #252"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- [ ] **Step 2: 推送分支**
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
git push origin fix/252-index-rebuild-backlinks
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- [ ] **Step 3: 创建 PR**
|
|
156
|
+
|
|
157
|
+
PR 标题:`fix(cli): jfox index rebuild --backlinks recalculates wiki links and backlinks`
|
|
158
|
+
PR 描述包含:
|
|
159
|
+
- 问题描述
|
|
160
|
+
- 修改内容
|
|
161
|
+
- 测试说明
|
|
162
|
+
- `Closes #252`
|
|
163
|
+
|
|
164
|
+
- [ ] **Step 4: 等待 CI 通过**
|
|
165
|
+
|
|
166
|
+
在 PR 页面确认 Fast / Core workflow 通过。
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# jfox self-update 命令 Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Add a `jfox update` command that detects the current installation method (dev / uv / pipx / pip) and invokes the appropriate upgrade command, showing before/after version info and providing manual fallback guidance on failure.
|
|
6
|
+
|
|
7
|
+
**Architecture:** A new `update` Typer command in `jfox/cli.py` plus helper functions for install-method detection and upgrade command execution. Detection is path-based with command fallbacks. A unit test file `tests/unit/test_update.py` mocks filesystem paths and subprocess calls.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3.10+, Typer, Rich, pytest, unittest.mock.
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-06-18-self-update-command-design.md`
|
|
12
|
+
|
|
13
|
+
**Work context:** Work in the isolated worktree `/home/elling/git-repo/github/jfox-wt-238` on branch `feat/issue-238-self-update` (based off `main`). All paths below are relative to that worktree root.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## File Structure
|
|
18
|
+
|
|
19
|
+
| File | Action | Responsibility |
|
|
20
|
+
|------|--------|----------------|
|
|
21
|
+
| `jfox/cli.py` | **Modify** | Add `_detect_install_method`, `_build_upgrade_command`, `_run_upgrade`, `_update_impl`, and `update` Typer command. |
|
|
22
|
+
| `tests/unit/test_update.py` | **Create** | Unit tests for detection, command selection, JSON output, dev-mode prompt, and failure handling. |
|
|
23
|
+
| `README.md` | **Modify** | Add `jfox update` to the command reference table. |
|
|
24
|
+
| `docs/installation.md` | **Modify** | Add an "Upgrade" section with `jfox update` and manual fallbacks. |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Task 1: Implement install-method detection helpers
|
|
29
|
+
|
|
30
|
+
**Files:** `jfox/cli.py`
|
|
31
|
+
|
|
32
|
+
- [ ] **Step 1: Add `_is_dev_installation` helper**
|
|
33
|
+
|
|
34
|
+
Determine development mode by checking whether `jfox.__file__` resolves to a source tree containing `pyproject.toml` with `project.name == "jfox-cli"` and a `.git` directory, or whether the path indicates an editable install (`.egg-link`).
|
|
35
|
+
|
|
36
|
+
- [ ] **Step 2: Add `_is_uv_tool_installation` helper**
|
|
37
|
+
|
|
38
|
+
Run `uv tool dir` to obtain the uv tools root. Return `True` if `jfox.__file__` is relative to `<uv-tool-dir>/jfox-cli/`. If `uv` is unavailable, fall back to path-segment matching (`uv/tools/jfox-cli/`).
|
|
39
|
+
|
|
40
|
+
- [ ] **Step 3: Add `_is_pipx_installation` helper**
|
|
41
|
+
|
|
42
|
+
Run `pipx environment --value PIPX_HOME` to obtain pipx home. Return `True` if `jfox.__file__` is relative to `<pipx-home>/venvs/jfox-cli/`. If `pipx` is unavailable, fall back to path-segment matching (`pipx/venvs/jfox-cli/`).
|
|
43
|
+
|
|
44
|
+
- [ ] **Step 4: Add `_detect_install_method` helper**
|
|
45
|
+
|
|
46
|
+
Apply the detection order: dev → uv → pipx → pip. Return a string identifier (`"dev"`, `"uv"`, `"pipx"`, `"pip"`).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Task 2: Implement upgrade execution and version comparison
|
|
51
|
+
|
|
52
|
+
**Files:** `jfox/cli.py`
|
|
53
|
+
|
|
54
|
+
- [ ] **Step 1: Add `_build_upgrade_command` helper**
|
|
55
|
+
|
|
56
|
+
Map the detected install method to the concrete command list:
|
|
57
|
+
- `"uv"` → `["uv", "tool", "upgrade", "jfox-cli"]`
|
|
58
|
+
- `"pipx"` → `["pipx", "upgrade", "jfox-cli"]`
|
|
59
|
+
- `"pip"` → `[sys.executable, "-m", "pip", "install", "--upgrade", "jfox-cli"]`
|
|
60
|
+
- `"dev"` → return `None`
|
|
61
|
+
|
|
62
|
+
- [ ] **Step 2: Add `_run_upgrade` helper**
|
|
63
|
+
|
|
64
|
+
Use `subprocess.run(..., check=True, capture_output=True, text=True)` to execute the command. On success return stdout; on `CalledProcessError` raise a custom exception or return an error dict. Surface stderr to the user.
|
|
65
|
+
|
|
66
|
+
- [ ] **Step 3: Add `_get_installed_version` helper**
|
|
67
|
+
|
|
68
|
+
Run `jfox --version` via subprocess and parse the version string. Return `"unknown"` if the call fails.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Task 3: Wire up the `update` Typer command
|
|
73
|
+
|
|
74
|
+
**Files:** `jfox/cli.py`
|
|
75
|
+
|
|
76
|
+
- [ ] **Step 1: Add `_update_impl` internal function**
|
|
77
|
+
|
|
78
|
+
1. Read current `__version__`.
|
|
79
|
+
2. Detect install method.
|
|
80
|
+
3. If dev, return a result dict with `success=True`, `method="dev"`, and an instruction string.
|
|
81
|
+
4. Build and run the upgrade command.
|
|
82
|
+
5. Call `_get_installed_version` for the after version.
|
|
83
|
+
6. Return a result dict: `success`, `method`, `previous_version`, `current_version`, `command`, `output`.
|
|
84
|
+
|
|
85
|
+
- [ ] **Step 2: Add `update` Typer command**
|
|
86
|
+
|
|
87
|
+
Support `--format json/table` and `--json` shortcut. Wrap the implementation in a try/except, print table or JSON output, and exit with code 1 on failure.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Task 4: Add unit tests
|
|
92
|
+
|
|
93
|
+
**Files:** `tests/unit/test_update.py`
|
|
94
|
+
|
|
95
|
+
- [ ] **Step 1: Test dev-mode detection**
|
|
96
|
+
|
|
97
|
+
Mock `jfox.__file__` to a source tree path with `pyproject.toml` and `.git/`. Assert `jfox update` prints the dev-mode instruction and does not invoke subprocess.
|
|
98
|
+
|
|
99
|
+
- [ ] **Step 2: Test uv / pipx / pip command selection**
|
|
100
|
+
|
|
101
|
+
Mock `jfox.__file__` to each tool's typical site-packages path and assert the correct subprocess command is invoked.
|
|
102
|
+
|
|
103
|
+
- [ ] **Step 3: Test JSON output**
|
|
104
|
+
|
|
105
|
+
Run `jfox update --json` and verify the parsed JSON contains expected keys.
|
|
106
|
+
|
|
107
|
+
- [ ] **Step 4: Test upgrade failure handling**
|
|
108
|
+
|
|
109
|
+
Make `subprocess.run` raise `CalledProcessError`. Assert exit code 1 and manual command guidance appears in output/JSON.
|
|
110
|
+
|
|
111
|
+
- [ ] **Step 5: Test version before/after display**
|
|
112
|
+
|
|
113
|
+
Mock `jfox.__version__` and the post-upgrade `jfox --version` subprocess to verify both versions appear.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Task 5: Update documentation
|
|
118
|
+
|
|
119
|
+
**Files:** `README.md`, `docs/installation.md`
|
|
120
|
+
|
|
121
|
+
- [ ] **Step 1: Add `jfox update` to README command table**
|
|
122
|
+
|
|
123
|
+
Place it under a new "Self-update" row or in the most appropriate existing section.
|
|
124
|
+
|
|
125
|
+
- [ ] **Step 2: Add upgrade section to `docs/installation.md`**
|
|
126
|
+
|
|
127
|
+
Document `jfox update` and the manual fallbacks for each install method.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Task 6: Run tests and static checks
|
|
132
|
+
|
|
133
|
+
- [ ] **Step 1: Run the new unit tests**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
uv run pytest tests/unit/test_update.py -v
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- [ ] **Step 2: Run fast unit tests**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
uv run pytest tests/unit/ -m "not embedding and not slow" -q
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- [ ] **Step 3: Run ruff lint**
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
uv run ruff check jfox/cli.py tests/unit/test_update.py
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Task 7: Commit and create PR
|
|
154
|
+
|
|
155
|
+
- [ ] **Step 1: Stage and commit changes**
|
|
156
|
+
|
|
157
|
+
Use Conventional Commits:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
git add jfox/cli.py tests/unit/test_update.py README.md docs/installation.md docs/superpowers/
|
|
161
|
+
git commit -m "feat(cli): add self-update command (jfox update)"
|
|
162
|
+
git commit -m "docs(readme): document jfox update command" -- README.md docs/installation.md
|
|
163
|
+
git commit -m "docs(superpowers): add self-update design and plan"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
- [ ] **Step 2: Push branch and create PR**
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
git push -u origin feat/issue-238-self-update
|
|
170
|
+
gh pr create --title "feat(cli): add self-update command" --body "Closes #238" --base main
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
- [ ] **Step 3: Wait for CI**
|
|
174
|
+
|
|
175
|
+
Monitor the PR checks and address any failures.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Issue #252: `jfox index rebuild` 重新计算 backlinks 设计文档
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
`jfox index rebuild` 当前只重建:
|
|
8
|
+
1. ChromaDB 向量索引(`Indexer.index_all()` → `vector_store.reset_collection()` + 全量 add)
|
|
9
|
+
2. BM25 关键词索引(`bm25_index.rebuild_from_notes()`)
|
|
10
|
+
|
|
11
|
+
但它**没有**重新解析笔记正文中的 `[[笔记标题]]` 维基链接,也没有根据解析结果更新笔记 frontmatter 中的 `links` 和 `backlinks` 字段。
|
|
12
|
+
|
|
13
|
+
这会导致以下问题:
|
|
14
|
+
- 手动修改笔记文件后,即使执行 `jfox index rebuild`,backlinks 关系也不会恢复
|
|
15
|
+
- 知识库迁移/整理后,链接网络丢失
|
|
16
|
+
- 必须通过 `jfox add` 重新创建笔记才能建立链接
|
|
17
|
+
|
|
18
|
+
## 目标
|
|
19
|
+
|
|
20
|
+
让 `jfox index rebuild` 支持可选的 `--backlinks` 参数,在重建索引的同时:
|
|
21
|
+
1. 全量加载所有笔记
|
|
22
|
+
2. 解析每篇笔记正文中的 `[[...]]` 维基链接
|
|
23
|
+
3. 按标题/ID 解析链接目标
|
|
24
|
+
4. 更新每篇笔记的 `links` 字段(按当前内容重新生成)
|
|
25
|
+
5. 根据所有笔记的 `links` 重新计算每篇笔记的 `backlinks`
|
|
26
|
+
6. 只写入确实有变化的笔记文件,避免无意义的 I/O
|
|
27
|
+
|
|
28
|
+
## 非目标
|
|
29
|
+
|
|
30
|
+
- 不修改 `jfox add` / `jfox edit` 的现有 backlinks 维护逻辑
|
|
31
|
+
- 不改变 `jfox index rebuild` 的默认行为(默认不重建 backlinks,避免意外覆盖用户手动写入的 frontmatter)
|
|
32
|
+
- 不引入新的持久化存储格式
|
|
33
|
+
|
|
34
|
+
## 方案
|
|
35
|
+
|
|
36
|
+
### 1. 新增 `jfox index rebuild --backlinks`
|
|
37
|
+
|
|
38
|
+
在 `jfox/cli.py` 的 `index()` 命令中:
|
|
39
|
+
- 为 `rebuild` action 增加 `--backlinks` / `-b` 选项
|
|
40
|
+
- 当用户传入 `--backlinks` 时,在 `indexer.index_all()` 和 BM25 重建完成后调用 backlinks 重建逻辑
|
|
41
|
+
|
|
42
|
+
### 2. 独立函数 `_rebuild_backlinks_impl()`
|
|
43
|
+
|
|
44
|
+
在 `jfox/cli.py` 中新增内部实现函数,职责单一:
|
|
45
|
+
- 通过 `note.list_notes()` 加载所有笔记
|
|
46
|
+
- 构建 `标题/ID → note_id` 的解析映射
|
|
47
|
+
- 遍历每篇笔记,调用现有 `extract_wiki_links()` 提取链接
|
|
48
|
+
- 对每个链接调用 `find_note_id_by_title_or_id()` 解析
|
|
49
|
+
- 汇总得到新的 `links` 和 `backlinks`
|
|
50
|
+
- 与现有值比较,仅当变化时才调用 `note.save_note(note, add_to_index=False)` 写回文件
|
|
51
|
+
|
|
52
|
+
### 3. 输出与结果
|
|
53
|
+
|
|
54
|
+
JSON 输出增加字段:
|
|
55
|
+
- `backlinks_rebuilt`: bool
|
|
56
|
+
- `backlinks_updated`: int(实际写入文件的笔记数量)
|
|
57
|
+
- `backlinks_total`: int(扫描的笔记总数)
|
|
58
|
+
- `unresolved_links`: List[str](无法解析的链接文本,去重)
|
|
59
|
+
|
|
60
|
+
Table 输出增加对应行:
|
|
61
|
+
- `✓ Backlinks rebuilt: X notes updated / Y scanned`
|
|
62
|
+
- 如有未解析链接,显示警告
|
|
63
|
+
|
|
64
|
+
### 4. 错误处理
|
|
65
|
+
|
|
66
|
+
- 加载失败的笔记跳过,记录 warning
|
|
67
|
+
- 保存失败不中断整个流程,记录 warning,但 `backlinks_rebuilt` 仍为 True(部分成功)
|
|
68
|
+
- 无任何笔记时返回成功
|
|
69
|
+
|
|
70
|
+
## 关键代码位置
|
|
71
|
+
|
|
72
|
+
- `jfox/cli.py:1935-1959`:`rebuild` action 的现有实现
|
|
73
|
+
- `jfox/cli.py:237-243`:现有 `extract_wiki_links()`
|
|
74
|
+
- `jfox/cli.py:246-290`:现有 `find_note_id_by_title_or_id()`
|
|
75
|
+
- `jfox/note.py:97-125`:`save_note()` 支持 `add_to_index=False`
|
|
76
|
+
|
|
77
|
+
## 测试策略
|
|
78
|
+
|
|
79
|
+
1. **集成测试**:在临时知识库中创建笔记 A、B,B 引用 A,手动清空 A 的 `backlinks` 字段,执行 `jfox index rebuild --backlinks`,验证 A 的 backlinks 恢复
|
|
80
|
+
2. **单元测试**:测试 `_rebuild_backlinks_impl()` 的链接解析与 backlinks 计算逻辑(使用 mock 笔记)
|
|
81
|
+
3. **边界测试**:
|
|
82
|
+
- 空知识库
|
|
83
|
+
- 链接到不存在的笔记
|
|
84
|
+
- 多个笔记链接到同一目标
|
|
85
|
+
- 没有 `--backlinks` 时不应修改任何笔记
|
|
86
|
+
|
|
87
|
+
## 兼容性
|
|
88
|
+
|
|
89
|
+
- 默认行为不变,不影响现有用户
|
|
90
|
+
- 新增的 `--backlinks` 选项是可选的
|
|
91
|
+
- 输出 JSON 新增字段,不会破坏现有解析(新增字段是兼容的)
|
|
92
|
+
|
|
93
|
+
## 验收标准
|
|
94
|
+
|
|
95
|
+
- [ ] `jfox index rebuild --backlinks` 成功重新计算并写入所有 backlinks
|
|
96
|
+
- [ ] 默认 `jfox index rebuild` 行为与修改前一致
|
|
97
|
+
- [ ] 新增集成测试通过
|
|
98
|
+
- [ ] ruff / black 检查通过
|
|
99
|
+
- [ ] PR 描述关联 issue #252
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Issue #259 设计文档:笔记归档/软删除
|
|
2
|
+
|
|
3
|
+
## 背景与目标
|
|
4
|
+
|
|
5
|
+
用户在把 session 笔记提炼为 permanent 笔记后,希望这些源 session 笔记不再出现在默认列表和搜索结果中,但又不想被永久删除。因此需要引入类似邮箱"归档"的软删除机制。
|
|
6
|
+
|
|
7
|
+
目标:
|
|
8
|
+
- 不真正删除文件,仅通过 frontmatter 标记 `archived: true`。
|
|
9
|
+
- 默认的 `jfox list` / `jfox search` 隐藏已归档笔记。
|
|
10
|
+
- 提供显式命令查看/恢复归档笔记。
|
|
11
|
+
- `jfox show` 和 `jfox delete` 行为不受影响。
|
|
12
|
+
|
|
13
|
+
## 需求拆解
|
|
14
|
+
|
|
15
|
+
| 用户命令 | 行为 |
|
|
16
|
+
|---------|------|
|
|
17
|
+
| `jfox archive <note_id>` | 将笔记标记为 `archived: true`,更新 `updated` 时间戳 |
|
|
18
|
+
| `jfox unarchive <note_id>` | 将笔记标记为 `archived: false`(frontmatter 中移除该字段),更新 `updated` 时间戳 |
|
|
19
|
+
| `jfox list` | 默认只列出未归档笔记 |
|
|
20
|
+
| `jfox list --archived` | 只列出已归档笔记 |
|
|
21
|
+
| `jfox list --include-archived` | 列出全部笔记(包含归档) |
|
|
22
|
+
| `jfox search "关键词"` | 默认排除已归档笔记 |
|
|
23
|
+
| `jfox search "关键词" --include-archived` | 搜索时包含已归档笔记 |
|
|
24
|
+
| `jfox show <id>` | 不受归档状态影响,始终可查看 |
|
|
25
|
+
| `jfox delete <id>` | 真删除,不受归档状态影响 |
|
|
26
|
+
|
|
27
|
+
## 方案设计
|
|
28
|
+
|
|
29
|
+
### 1. 数据模型(`jfox/models.py`)
|
|
30
|
+
|
|
31
|
+
在 `Note` 数据类中新增字段:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
archived: bool = False
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- `to_markdown()`:仅在 `archived=True` 时把 `archived: true` 写入 frontmatter,保持未归档笔记 frontmatter 干净。
|
|
38
|
+
- `from_markdown()`:读取 `archived` 字段,缺失时默认 `False`。
|
|
39
|
+
- `to_dict()`:增加 `"archived"` 字段,便于 JSON 输出。
|
|
40
|
+
|
|
41
|
+
### 2. 元数据索引(`jfox/note_index.py`)
|
|
42
|
+
|
|
43
|
+
在 `NoteMeta` 中新增 `archived: bool = False`。
|
|
44
|
+
|
|
45
|
+
索引构建时从 frontmatter 读取 `archived`,缺失默认 `False`。`list_meta()` 增加过滤参数:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
def list_meta(
|
|
49
|
+
self,
|
|
50
|
+
note_type: Optional[NoteType] = None,
|
|
51
|
+
tags: Optional[List[str]] = None,
|
|
52
|
+
limit: Optional[int] = None,
|
|
53
|
+
archived_only: bool = False,
|
|
54
|
+
include_archived: bool = False,
|
|
55
|
+
) -> List[NoteMeta]:
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
过滤规则:
|
|
59
|
+
- `archived_only=True`:只返回 `archived=True`。
|
|
60
|
+
- `include_archived=True`:返回全部。
|
|
61
|
+
- 默认:只返回 `archived=False`。
|
|
62
|
+
|
|
63
|
+
### 3. 笔记 CRUD(`jfox/note.py`)
|
|
64
|
+
|
|
65
|
+
新增:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
def archive_note(note_id: str) -> bool
|
|
69
|
+
def unarchive_note(note_id: str) -> bool
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
实现:
|
|
73
|
+
1. 通过 `load_note_by_id(note_id)` 加载笔记。
|
|
74
|
+
2. 设置 `note.archived = True/False`。
|
|
75
|
+
3. 调用 `update_note(note)` 持久化并同步索引(向量 + BM25)。
|
|
76
|
+
|
|
77
|
+
复用 `update_note` 可以自动处理:
|
|
78
|
+
- 原子写入;
|
|
79
|
+
- 文件名变化(归档不会改变文件名,但保留路径);
|
|
80
|
+
- 索引更新(先删除旧向量/BM25,再添加新内容)。
|
|
81
|
+
|
|
82
|
+
`list_notes()` 增加参数 `archived_only` 和 `include_archived`,透传给 `NoteIndex.list_meta()`。
|
|
83
|
+
|
|
84
|
+
`search_notes()` 增加参数 `include_archived`,透传给搜索引擎。
|
|
85
|
+
|
|
86
|
+
### 4. 搜索引擎(`jfox/search_engine.py`)
|
|
87
|
+
|
|
88
|
+
`HybridSearchEngine.search()` 增加 `include_archived: bool = False`。
|
|
89
|
+
|
|
90
|
+
由于现有 ChromaDB 集合里的笔记元数据不一定包含 `archived` 字段,直接在 ChromaDB 层过滤会导致旧数据被误排除。因此采用**后过滤**策略:
|
|
91
|
+
|
|
92
|
+
1. 搜索时先按原逻辑获取结果,但 `exclude archived` 时多取一些结果(over-fetch)。
|
|
93
|
+
2. 使用 `NoteIndex` 判断每个结果是否归档(`meta.archived`)。
|
|
94
|
+
3. 过滤后取前 `top_k` 条。
|
|
95
|
+
|
|
96
|
+
over-fetch 策略:
|
|
97
|
+
- `_semantic_search` / `_keyword_search`:当 `not include_archived` 时,`search_k = max(top_k * 5, 20)`。
|
|
98
|
+
- `_hybrid_search`:同样 over-fetch,融合后再过滤。
|
|
99
|
+
|
|
100
|
+
这样可以在绝大多数场景下保证返回数量,同时避免旧索引数据兼容性问题。
|
|
101
|
+
|
|
102
|
+
### 5. CLI(`jfox/cli.py`)
|
|
103
|
+
|
|
104
|
+
新增命令:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
@app.command()
|
|
108
|
+
def archive(
|
|
109
|
+
note_id: str = typer.Argument(..., help="笔记 ID"),
|
|
110
|
+
kb: Optional[str] = typer.Option(None, "--kb", "-k", help="目标知识库名称"),
|
|
111
|
+
output_format: str = typer.Option("table", "--format", "-f", help="输出格式: json, table"),
|
|
112
|
+
json_output: bool = typer.Option(False, "--json", help="JSON 输出"),
|
|
113
|
+
):
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
@app.command()
|
|
117
|
+
def unarchive(...):
|
|
118
|
+
...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`list` 命令增加选项:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
archived_only: bool = typer.Option(False, "--archived", help="仅显示已归档笔记"),
|
|
125
|
+
include_archived: bool = typer.Option(False, "--include-archived", help="包含已归档笔记"),
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`--archived` 与 `--include-archived` 互斥,同时指定时报错。
|
|
129
|
+
|
|
130
|
+
`search` 命令增加选项:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
include_archived: bool = typer.Option(False, "--include-archived", help="包含已归档笔记"),
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`_list_impl` / `_search_impl` 相应增加参数透传。
|
|
137
|
+
|
|
138
|
+
### 6. 测试策略
|
|
139
|
+
|
|
140
|
+
新增 `tests/unit/test_archive.py`,覆盖:
|
|
141
|
+
|
|
142
|
+
- `Note.to_markdown()` / `from_markdown()` 正确读写 `archived`。
|
|
143
|
+
- `NoteIndex.list_meta()` 默认排除归档、支持 `archived_only` / `include_archived`。
|
|
144
|
+
- `archive_note()` / `unarchive_note()` 更新文件与索引。
|
|
145
|
+
- `list` CLI 默认排除归档、`--archived`、`--include-archived`。
|
|
146
|
+
- `search` CLI 默认排除归档、`--include-archived`。
|
|
147
|
+
- `show` / `delete` 对归档笔记行为不变。
|
|
148
|
+
|
|
149
|
+
使用现有 fixture(`temp_kb`、`cli_fast`)避免加载 embedding 模型。
|
|
150
|
+
|
|
151
|
+
## 兼容性
|
|
152
|
+
|
|
153
|
+
- 旧笔记没有 `archived` 字段,解析时默认视为未归档,行为不变。
|
|
154
|
+
- 已写入的 ChromaDB/BM25 索引不需要重建即可工作(后过滤)。
|
|
155
|
+
- 归档/取消归档会触发 `update_note()`,自动更新索引内容。
|
|
156
|
+
|
|
157
|
+
## 风险与回滚
|
|
158
|
+
|
|
159
|
+
- 风险:后过滤可能导致 `top_k` 返回数量不足(如果前 N 条中归档笔记很多)。通过 over-fetch(`*5` 且至少 20)缓解。
|
|
160
|
+
- 回滚:取消归档会移除 frontmatter 中的 `archived` 字段,恢复为未归档状态。
|