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.
Files changed (241) hide show
  1. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/CHANGELOG.md +11 -0
  2. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/PKG-INFO +8 -1
  3. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/README.md +7 -0
  4. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/installation.md +26 -0
  5. jfox_cli-1.1.0/docs/superpowers/plans/2026-06-17-index-rebuild-backlinks.md +166 -0
  6. jfox_cli-1.1.0/docs/superpowers/plans/2026-06-18-self-update-command.md +175 -0
  7. jfox_cli-1.1.0/docs/superpowers/specs/2026-06-17-index-rebuild-backlinks-design.md +99 -0
  8. jfox_cli-1.1.0/docs/superpowers/specs/2026-06-18-archive-soft-delete-design.md +160 -0
  9. jfox_cli-1.1.0/docs/superpowers/specs/2026-06-18-self-update-command-design.md +149 -0
  10. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/__init__.py +1 -1
  11. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/cli.py +689 -10
  12. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/models.py +18 -0
  13. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/note.py +76 -2
  14. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/note_index.py +49 -2
  15. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/search_engine.py +164 -56
  16. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/pyproject.toml +1 -1
  17. jfox_cli-1.1.0/tests/integration/test_index_rebuild_backlinks.py +207 -0
  18. jfox_cli-1.1.0/tests/unit/test_archive.py +494 -0
  19. jfox_cli-1.1.0/tests/unit/test_rebuild_backlinks_impl.py +218 -0
  20. jfox_cli-1.1.0/tests/unit/test_update.py +587 -0
  21. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/jfox_cli.py +5 -2
  22. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/uv.lock +1 -1
  23. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/settings.local.json +0 -0
  24. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/skills/ci/SKILL.md +0 -0
  25. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/skills/release/SKILL.md +0 -0
  26. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude/skills/release/release_helper.py +0 -0
  27. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.claude-plugin/marketplace.json +0 -0
  28. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.githooks/pre-push +0 -0
  29. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  30. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  31. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/docs_improvement.yml +0 -0
  32. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  33. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  34. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/workflows/integration-test.yml +0 -0
  35. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.github/workflows/publish.yml +0 -0
  36. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.gitignore +0 -0
  37. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/.python-version +0 -0
  38. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/AGENTS.md +0 -0
  39. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/CLAUDE.md +0 -0
  40. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/DEVELOPMENT_PLAN.md +0 -0
  41. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/SESSION.md +0 -0
  42. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/SESSION_SUMMARY.md +0 -0
  43. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/claude-code-plugin-guide.md +0 -0
  44. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-11-bulk-import-bm25-fix.md +0 -0
  45. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-11-edit-command.md +0 -0
  46. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-11-unify-format-option.md +0 -0
  47. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-ci-coverage-optimization.md +0 -0
  48. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-edit-content-file.md +0 -0
  49. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-fix-index-rebuild-clear.md +0 -0
  50. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-fix-index-verify-id-mismatch.md +0 -0
  51. {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
  52. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-index-kb-param.md +0 -0
  53. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-lazy-import-perf.md +0 -0
  54. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-12-skill-redesign.md +0 -0
  55. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-add-content-file.md +0 -0
  56. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-bulk-import-vectorstore-init.md +0 -0
  57. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-ingest-log-command.md +0 -0
  58. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-ingest-skill-sync.md +0 -0
  59. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-suppress-third-party-logging.md +0 -0
  60. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-13-sync-skills-with-cli.md +0 -0
  61. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-14-sync-docs-daemon-show.md +0 -0
  62. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-15-gpu-embedding.md +0 -0
  63. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-15-make-daemon-deps-required.md +0 -0
  64. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-15-session-summary-confirmation.md +0 -0
  65. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-16-fix-windows-daemon-console-window.md +0 -0
  66. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-18-daemon-timeout-and-index-rebuild.md +0 -0
  67. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-22-release-skill.md +0 -0
  68. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-26-fix-daemon-deprecation-warnings.md +0 -0
  69. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-27-intranet-model-download.md +0 -0
  70. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-28-tag-filtering.md +0 -0
  71. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-04-29-daemon-stop-fix.md +0 -0
  72. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-03-jfox-check.md +0 -0
  73. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-03-list-notes-skip-summary.md +0 -0
  74. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-03-log-level-fix.md +0 -0
  75. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-04-list-notes-index.md +0 -0
  76. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-06-atomic-write.md +0 -0
  77. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-09-session-note-type.md +0 -0
  78. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-10-jfox-plugin.md +0 -0
  79. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-19-fix-last-used-never.md +0 -0
  80. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-05-20-modelscope-migration.md +0 -0
  81. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-06-02-todo-note-type.md +0 -0
  82. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-06-16-kimi-code-auto-summary.md +0 -0
  83. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/plans/2026-06-17-using-jfox-skill.md +0 -0
  84. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-03-bugfixes-design.md +0 -0
  85. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-12-skill-redesign-design.md +0 -0
  86. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-13-pr-auto-code-review-design.md +0 -0
  87. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-14-show-command-design.md +0 -0
  88. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-15-gpu-embedding-design.md +0 -0
  89. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-15-session-summary-confirmation-design.md +0 -0
  90. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-21-release-skill-design.md +0 -0
  91. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-26-fix-daemon-deprecation-warnings-design.md +0 -0
  92. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-27-intranet-model-download-design.md +0 -0
  93. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-28-tag-filtering-design.md +0 -0
  94. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-29-daemon-stop-fix-design.md +0 -0
  95. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-04-30-jfox-kb-env-var-design.md +0 -0
  96. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-03-jfox-check-design.md +0 -0
  97. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-03-list-notes-skip-summary-design.md +0 -0
  98. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-03-log-level-fix-design.md +0 -0
  99. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-04-list-notes-index-design.md +0 -0
  100. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-06-atomic-write-design.md +0 -0
  101. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-09-jfox-plugin-design.md +0 -0
  102. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-09-session-note-type-design.md +0 -0
  103. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-05-20-modelscope-migration-design.md +0 -0
  104. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-06-02-todo-note-type-design.md +0 -0
  105. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-06-16-kimi-code-auto-summary-design.md +0 -0
  106. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/superpowers/specs/2026-06-17-using-jfox-skill-design.md +0 -0
  107. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/docs/troubleshooting.md +0 -0
  108. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jessica-jones-static-cable.md +0 -0
  109. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/__main__.py +0 -0
  110. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/__init__.py +0 -0
  111. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/cli.py +0 -0
  112. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/extractor.py +0 -0
  113. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/kimi_source.py +0 -0
  114. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/ledger.py +0 -0
  115. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/loop.py +0 -0
  116. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/runner.py +0 -0
  117. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/scanner.py +0 -0
  118. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/auto_summary/sources.py +0 -0
  119. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/bm25_index.py +0 -0
  120. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/config.py +0 -0
  121. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/__init__.py +0 -0
  122. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/__main__.py +0 -0
  123. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/client.py +0 -0
  124. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/process.py +0 -0
  125. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/daemon/server.py +0 -0
  126. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/embedding_backend.py +0 -0
  127. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/formatters.py +0 -0
  128. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/git_extractor.py +0 -0
  129. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/global_config.py +0 -0
  130. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/graph.py +0 -0
  131. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/indexer.py +0 -0
  132. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/kb_manager.py +0 -0
  133. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/model_downloader.py +0 -0
  134. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/performance.py +0 -0
  135. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/template.py +0 -0
  136. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/template_cli.py +0 -0
  137. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/utils.py +0 -0
  138. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/jfox/vector_store.py +0 -0
  139. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/package.json +0 -0
  140. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/.claude-plugin/plugin.json +0 -0
  141. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/ingest/SKILL.md +0 -0
  142. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/manage/SKILL.md +0 -0
  143. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/organize/SKILL.md +0 -0
  144. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/search/SKILL.md +0 -0
  145. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/session-summary/SKILL.md +0 -0
  146. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/cc-plugin/skills/using-jfox/SKILL.md +0 -0
  147. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/README.md +0 -0
  148. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/kimi.plugin.json +0 -0
  149. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-ingest/SKILL.md +0 -0
  150. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-manage/SKILL.md +0 -0
  151. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-organize/SKILL.md +0 -0
  152. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-search/SKILL.md +0 -0
  153. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/jfox-session-summary/SKILL.md +0 -0
  154. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/packages/kimi-plugin/skills/using-jfox/SKILL.md +0 -0
  155. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/pytest.ini +0 -0
  156. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/run_full_test.ps1 +0 -0
  157. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/scripts/download-model-intranet.sh +0 -0
  158. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/README.md +0 -0
  159. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-common/SKILL.md +0 -0
  160. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-ingest/SKILL.md +0 -0
  161. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-organize/SKILL.md +0 -0
  162. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-search/SKILL.md +0 -0
  163. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/kimi-cli/jfox-session-summary/SKILL.md +0 -0
  164. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-ci/SKILL.md +0 -0
  165. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-common/SKILL.md +0 -0
  166. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-ingest/SKILL.md +0 -0
  167. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-organize/SKILL.md +0 -0
  168. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-release/SKILL.md +0 -0
  169. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-search/SKILL.md +0 -0
  170. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/skills-recommend/pi/jfox-session-summary/SKILL.md +0 -0
  171. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/COVERAGE_PLAN.md +0 -0
  172. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/MIGRATION.md +0 -0
  173. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/TESTS.md +0 -0
  174. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/conftest.py +0 -0
  175. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/__init__.py +0 -0
  176. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_auto_summary_kimi.py +0 -0
  177. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_backlinks.py +0 -0
  178. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_model_download.py +0 -0
  179. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/integration/test_tag_filter_cli.py +0 -0
  180. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/performance/__init__.py +0 -0
  181. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/performance/test_performance.py +0 -0
  182. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_advanced_features.py +0 -0
  183. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_cli_format.py +0 -0
  184. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_config_set_unit.py +0 -0
  185. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_config_unit.py +0 -0
  186. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_core_workflow.py +0 -0
  187. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_embedding_device.py +0 -0
  188. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_hybrid_search.py +0 -0
  189. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_integration.py +0 -0
  190. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_kb_current.py +0 -0
  191. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/test_suggest_links.py +0 -0
  192. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/__init__.py +0 -0
  193. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_atomic_write.py +0 -0
  194. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_config_sources.py +0 -0
  195. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_defaults.py +0 -0
  196. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_extractor.py +0 -0
  197. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_ledger.py +0 -0
  198. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_runner.py +0 -0
  199. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_scanner.py +0 -0
  200. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_auto_summary_status.py +0 -0
  201. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_bm25_batch.py +0 -0
  202. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_check.py +0 -0
  203. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_content_file.py +0 -0
  204. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_daemon_cli.py +0 -0
  205. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_daemon_process.py +0 -0
  206. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_edit.py +0 -0
  207. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_embedding_local_load.py +0 -0
  208. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_format_unify.py +0 -0
  209. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_formatters.py +0 -0
  210. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_git_extractor.py +0 -0
  211. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_global_config.py +0 -0
  212. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_index_kb_param.py +0 -0
  213. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_indexer_clear_before_rebuild.py +0 -0
  214. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_indexer_verify.py +0 -0
  215. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_kb_manager.py +0 -0
  216. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_kimi_extractor.py +0 -0
  217. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_kimi_scanner.py +0 -0
  218. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_lazy_import.py +0 -0
  219. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_ledger_migration.py +0 -0
  220. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_list_notes_skip.py +0 -0
  221. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_logging_config.py +0 -0
  222. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_model_downloader.py +0 -0
  223. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_note_index.py +0 -0
  224. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_release_helper.py +0 -0
  225. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_runner_multi_source.py +0 -0
  226. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_scanner_list.py +0 -0
  227. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_session_file_source.py +0 -0
  228. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_session_note.py +0 -0
  229. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_session_source.py +0 -0
  230. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_show.py +0 -0
  231. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_system_prompt_sections.py +0 -0
  232. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_tag_filter.py +0 -0
  233. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_template.py +0 -0
  234. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_template_cli.py +0 -0
  235. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_use_kb_env_var.py +0 -0
  236. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_using_jfox_skill.py +0 -0
  237. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/unit/test_vector_store_clear.py +0 -0
  238. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/__init__.py +0 -0
  239. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/assertions.py +0 -0
  240. {jfox_cli-1.0.0 → jfox_cli-1.1.0}/tests/utils/note_generator.py +0 -0
  241. {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.0.0
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` 字段,恢复为未归档状态。