jfox-cli 0.2.1__tar.gz → 0.2.2__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-0.2.1 → jfox_cli-0.2.2}/CLAUDE.md +1 -1
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/PKG-INFO +1 -1
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/__init__.py +1 -1
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/cli.py +50 -12
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/pyproject.toml +1 -1
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skills-recommend/claude-code/jfox-common/SKILL.md +91 -7
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skills-recommend/claude-code/jfox-ingest/SKILL.md +43 -31
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skills-recommend/claude-code/jfox-organize/SKILL.md +13 -3
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_cli_format.py +49 -0
- jfox_cli-0.2.2/tests/unit/test_logging_config.py +34 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/uv.lock +1 -1
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/.githooks/pre-push +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/.github/workflows/integration-test.yml +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/.github/workflows/publish.yml +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/.gitignore +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/.python-version +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/AGENTS.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/CHANGELOG.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/DEVELOPMENT_PLAN.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/README.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/SESSION.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/SESSION_SUMMARY.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-11-bulk-import-bm25-fix.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-11-edit-command.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-11-unify-format-option.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-ci-coverage-optimization.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-edit-content-file.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-fix-index-rebuild-clear.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-fix-index-verify-id-mismatch.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-fix-jfox-health-skill-kb-param.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-index-kb-param.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-lazy-import-perf.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-skill-redesign.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/specs/2026-04-03-bugfixes-design.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/specs/2026-04-12-skill-redesign-design.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/specs/2026-04-13-pr-auto-code-review-design.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jessica-jones-static-cable.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/__main__.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/bm25_index.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/config.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/embedding_backend.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/formatters.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/git_extractor.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/global_config.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/graph.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/indexer.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/kb_manager.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/models.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/note.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/performance.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/search_engine.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/template.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/template_cli.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/jfox/vector_store.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/pytest.ini +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/run_full_test.ps1 +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skill/evals/evals.json +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skill/knowledge-base-notes/SKILL.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skill/knowledge-base-workspace/SKILL.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skills-recommend/README.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/skills-recommend/claude-code/jfox-search/SKILL.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/COVERAGE_PLAN.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/MIGRATION.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/TESTS.md +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/conftest.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/integration/__init__.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/integration/test_backlinks.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/performance/__init__.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/performance/test_performance.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_advanced_features.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_config_unit.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_core_workflow.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_hybrid_search.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_integration.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_kb_current.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/test_suggest_links.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/__init__.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_bm25_batch.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_edit.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_format_unify.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_formatters.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_git_extractor.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_global_config.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_index_kb_param.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_indexer_clear_before_rebuild.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_indexer_verify.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_kb_manager.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_lazy_import.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_template.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_template_cli.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/unit/test_vector_store_clear.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/utils/__init__.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/utils/assertions.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/utils/jfox_cli.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/utils/note_generator.py +0 -0
- {jfox_cli-0.2.1 → jfox_cli-0.2.2}/tests/utils/temp_kb.py +0 -0
|
@@ -91,7 +91,7 @@ Notes are Markdown files with YAML frontmatter stored under `~/.zettelkasten/<kb
|
|
|
91
91
|
|
|
92
92
|
## Conventions
|
|
93
93
|
|
|
94
|
-
- **Version bump**: 发版时必须同时修改 `pyproject.toml` 和 `
|
|
94
|
+
- **Version bump**: 发版时必须同时修改 `pyproject.toml`、`jfox/__init__.py` 和 `uv.lock` 三处版本号。先改前两个文件,再跑 `uv lock` 更新 lock 文件(曾有 #88 遗漏 `__init__.py` 的教训)
|
|
95
95
|
- **Line length**: 100 chars (black + ruff configured in `pyproject.toml`)
|
|
96
96
|
- **Comments/docs**: Chinese (中文)
|
|
97
97
|
- **Adding a CLI command**: Add `@app.command()` in `cli.py`, implement `_xxx_impl()` helper, add `--kb` and `--format json` support
|
|
@@ -37,6 +37,19 @@ from .template_cli import template_app
|
|
|
37
37
|
logging.basicConfig(
|
|
38
38
|
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
39
39
|
)
|
|
40
|
+
|
|
41
|
+
# 抑制第三方库的 INFO/DEBUG 日志,避免污染 CLI 输出
|
|
42
|
+
for _lib in (
|
|
43
|
+
"sentence_transformers",
|
|
44
|
+
"torch",
|
|
45
|
+
"chromadb",
|
|
46
|
+
"tqdm",
|
|
47
|
+
"urllib3",
|
|
48
|
+
"watchdog",
|
|
49
|
+
"PIL",
|
|
50
|
+
):
|
|
51
|
+
logging.getLogger(_lib).setLevel(logging.WARNING)
|
|
52
|
+
|
|
40
53
|
logger = logging.getLogger(__name__)
|
|
41
54
|
|
|
42
55
|
# 创建应用
|
|
@@ -365,7 +378,7 @@ def _add_note_impl(
|
|
|
365
378
|
|
|
366
379
|
@app.command()
|
|
367
380
|
def add(
|
|
368
|
-
content: str = typer.Argument(
|
|
381
|
+
content: Optional[str] = typer.Argument(None, help="笔记内容(支持 [[笔记标题]] 格式链接)"),
|
|
369
382
|
title: Optional[str] = typer.Option(None, "--title", "-t", help="笔记标题"),
|
|
370
383
|
note_type: str = typer.Option(
|
|
371
384
|
"fleeting", "--type", help="笔记类型 (fleeting/literature/permanent)"
|
|
@@ -375,6 +388,9 @@ def add(
|
|
|
375
388
|
template: Optional[str] = typer.Option(
|
|
376
389
|
None, "--template", "-T", help="使用模板创建笔记 (quick/meeting/literature)"
|
|
377
390
|
),
|
|
391
|
+
content_file: Optional[str] = typer.Option(
|
|
392
|
+
None, "--content-file", help="从文件读取内容(用 - 表示 stdin)"
|
|
393
|
+
),
|
|
378
394
|
kb: Optional[str] = typer.Option(None, "--kb", "-k", help="目标知识库名称"),
|
|
379
395
|
output_format: str = typer.Option("table", "--format", "-f", help="输出格式: json, table"),
|
|
380
396
|
json_output: bool = typer.Option(
|
|
@@ -387,6 +403,18 @@ def add(
|
|
|
387
403
|
if json_output:
|
|
388
404
|
output_format = "json"
|
|
389
405
|
|
|
406
|
+
# content 和 --content-file 互斥
|
|
407
|
+
if content is not None and content_file is not None:
|
|
408
|
+
raise ValueError("不能同时指定内容参数和 --content-file,请选择其一")
|
|
409
|
+
|
|
410
|
+
# 从文件读取内容
|
|
411
|
+
if content_file is not None:
|
|
412
|
+
content = _read_content_file(content_file)
|
|
413
|
+
|
|
414
|
+
# 至少提供一种内容来源
|
|
415
|
+
if not content:
|
|
416
|
+
raise ValueError("请提供笔记内容(位置参数或 --content-file)")
|
|
417
|
+
|
|
390
418
|
# 如果指定了知识库,临时切换
|
|
391
419
|
if kb:
|
|
392
420
|
from .config import use_kb
|
|
@@ -956,6 +984,26 @@ def delete(
|
|
|
956
984
|
raise typer.Exit(1)
|
|
957
985
|
|
|
958
986
|
|
|
987
|
+
def _read_content_file(content_file: str) -> str:
|
|
988
|
+
"""从文件或 stdin 读取内容(--content-file 共用逻辑)"""
|
|
989
|
+
if content_file == "-":
|
|
990
|
+
import sys
|
|
991
|
+
|
|
992
|
+
return sys.stdin.read()
|
|
993
|
+
|
|
994
|
+
p = Path(content_file)
|
|
995
|
+
if not p.exists():
|
|
996
|
+
raise ValueError(f"文件不存在: {content_file}")
|
|
997
|
+
if not p.is_file():
|
|
998
|
+
raise ValueError(f"路径不是文件: {content_file}")
|
|
999
|
+
try:
|
|
1000
|
+
return p.read_text(encoding="utf-8")
|
|
1001
|
+
except PermissionError:
|
|
1002
|
+
raise ValueError(f"无权限读取文件: {content_file}")
|
|
1003
|
+
except UnicodeDecodeError:
|
|
1004
|
+
raise ValueError(f"文件编码错误(需要 UTF-8): {content_file}")
|
|
1005
|
+
|
|
1006
|
+
|
|
959
1007
|
def _edit_impl(
|
|
960
1008
|
note_id: str,
|
|
961
1009
|
content: Optional[str],
|
|
@@ -973,17 +1021,7 @@ def _edit_impl(
|
|
|
973
1021
|
|
|
974
1022
|
# 从文件读取内容
|
|
975
1023
|
if content_file is not None:
|
|
976
|
-
|
|
977
|
-
if not p.exists():
|
|
978
|
-
raise ValueError(f"文件不存在: {content_file}")
|
|
979
|
-
if not p.is_file():
|
|
980
|
-
raise ValueError(f"路径不是文件: {content_file}")
|
|
981
|
-
try:
|
|
982
|
-
content = p.read_text(encoding="utf-8")
|
|
983
|
-
except PermissionError:
|
|
984
|
-
raise ValueError(f"无权限读取文件: {content_file}")
|
|
985
|
-
except UnicodeDecodeError:
|
|
986
|
-
raise ValueError(f"文件编码错误(需要 UTF-8): {content_file}")
|
|
1024
|
+
content = _read_content_file(content_file)
|
|
987
1025
|
|
|
988
1026
|
# 验证:至少指定一个编辑字段
|
|
989
1027
|
if all(v is None for v in [content, title, tags, note_type, source]):
|
|
@@ -79,6 +79,67 @@ jfox kb current --format json # 当前知识库
|
|
|
79
79
|
jfox kb rename <old> <new> # 重命名
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
## 笔记 CRUD
|
|
83
|
+
|
|
84
|
+
### 添加笔记
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 快速添加(内容直接作为参数)
|
|
88
|
+
jfox add "笔记内容,支持 [[其他笔记标题]] 链接" --title "笔记标题"
|
|
89
|
+
|
|
90
|
+
# 指定类型和标签
|
|
91
|
+
jfox add "内容" --title "标题" --type permanent --tag design --tag backend
|
|
92
|
+
|
|
93
|
+
# 从文件读取内容(v0.2.1+,适合长文本)
|
|
94
|
+
jfox add --content-file notes/draft.md --title "标题" --type literature
|
|
95
|
+
|
|
96
|
+
# 从 stdin 读取
|
|
97
|
+
cat notes.txt | jfox add --content-file - --title "标题"
|
|
98
|
+
|
|
99
|
+
# 使用模板
|
|
100
|
+
jfox add --template meeting --title "周会记录"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
笔记类型:
|
|
104
|
+
- `fleeting`(默认)— 快速捕获,稍后提炼
|
|
105
|
+
- `literature` — 阅读笔记
|
|
106
|
+
- `permanent` — 已提炼的知识
|
|
107
|
+
|
|
108
|
+
### 编辑笔记
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# 编辑内容和标题
|
|
112
|
+
jfox edit <note_id> --content "新内容" --title "新标题"
|
|
113
|
+
|
|
114
|
+
# 从文件读取内容(v0.2.1+,适合长文本)
|
|
115
|
+
jfox edit <note_id> --content-file updated.md
|
|
116
|
+
|
|
117
|
+
# 修改标签和类型
|
|
118
|
+
jfox edit <note_id> --tag new-tag1 --tag new-tag2 --type permanent
|
|
119
|
+
|
|
120
|
+
# 在指定知识库中编辑
|
|
121
|
+
jfox edit <note_id> --kb work --content "新内容"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
编辑会保留原始笔记 ID 和创建时间。
|
|
125
|
+
|
|
126
|
+
### 删除笔记
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
jfox delete <note_id> # 需确认
|
|
130
|
+
jfox delete <note_id> --force # 跳过确认
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 查看笔记
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
jfox list --format json --limit 50 # 列出笔记
|
|
137
|
+
jfox list --type permanent --format json # 按类型筛选
|
|
138
|
+
jfox daily --json # 今天的笔记
|
|
139
|
+
jfox daily --date 2026-04-01 --json # 指定日期
|
|
140
|
+
jfox refs --search "<标题>" --format json # 查看反向链接
|
|
141
|
+
```
|
|
142
|
+
|
|
82
143
|
### 删除知识库
|
|
83
144
|
|
|
84
145
|
```bash
|
|
@@ -228,7 +289,9 @@ Score = 100
|
|
|
228
289
|
|
|
229
290
|
## 命令参考
|
|
230
291
|
|
|
231
|
-
|
|
292
|
+
以下仅列出知识库管理、笔记 CRUD 和健康检查相关命令。所有命令支持 `--kb <name>` 指定知识库,省略时使用当前默认知识库。
|
|
293
|
+
|
|
294
|
+
**约定**:所有命令均支持 `--format json` 输出 JSON,也可使用快捷方式 `--json`(两者等价)。下文示例统一使用 `--json`。
|
|
232
295
|
|
|
233
296
|
### 知识库管理
|
|
234
297
|
|
|
@@ -244,15 +307,35 @@ jfox kb remove <name> --force # 删除(含文件,不可恢复
|
|
|
244
307
|
jfox status --format json # 知识库状态
|
|
245
308
|
```
|
|
246
309
|
|
|
310
|
+
### 笔记 CRUD
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
jfox add "<content>" --title "<title>" --type <type> --tag <tags> # 添加笔记
|
|
314
|
+
jfox add --content-file <path> --title "<title>" # 从文件添加
|
|
315
|
+
jfox edit <id> --content "<new>" --title "<title>" # 编辑笔记
|
|
316
|
+
jfox edit <id> --content-file <path> # 从文件编辑
|
|
317
|
+
jfox delete <id> --force # 删除笔记
|
|
318
|
+
jfox list --format json --limit <N> # 列出笔记
|
|
319
|
+
jfox daily --json # 今天的笔记
|
|
320
|
+
jfox daily --date YYYY-MM-DD --json # 指定日期
|
|
321
|
+
jfox refs --search "<title>" --format json # 反向链接
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### 数据导入
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
jfox ingest-log <repo-path> --limit <N> --type fleeting --kb <name> # Git 仓库导入
|
|
328
|
+
jfox bulk-import <file.json> --type fleeting --kb <name> # 批量导入
|
|
329
|
+
```
|
|
330
|
+
|
|
247
331
|
### 健康检查
|
|
248
332
|
|
|
249
333
|
```bash
|
|
250
|
-
jfox graph --stats --json
|
|
251
|
-
jfox graph --orphans --json
|
|
252
|
-
jfox index verify
|
|
253
|
-
jfox index rebuild
|
|
254
|
-
jfox
|
|
255
|
-
jfox inbox --json --limit <N> # 未处理笔记
|
|
334
|
+
jfox graph --stats --json # 图谱指标(与 --orphans 互斥,分开运行)
|
|
335
|
+
jfox graph --orphans --json # 孤立笔记列表
|
|
336
|
+
jfox index verify # 索引完整性验证
|
|
337
|
+
jfox index rebuild # 重建索引
|
|
338
|
+
jfox inbox --json --limit <N> # 未处理笔记
|
|
256
339
|
```
|
|
257
340
|
|
|
258
341
|
> 搜索、导入、整理等高频操作命令见对应技能文档(jfox-search、jfox-ingest、jfox-organize)。
|
|
@@ -265,3 +348,4 @@ jfox inbox --json --limit <N> # 未处理笔记
|
|
|
265
348
|
| "Path is outside managed directory" | 所有知识库必须在 `~/.zettelkasten/` 下 |
|
|
266
349
|
| `jfox: command not found` | 安装:`uv tool install jfox-cli` |
|
|
267
350
|
| 索引过时或 `jfox index verify` 异常 | 运行 `jfox index rebuild` 重建搜索索引 |
|
|
351
|
+
| `ingest-log` 报 "Not a git repository" | 提供正确的 Git 仓库路径 |
|
|
@@ -57,17 +57,19 @@ git -C <path> remote get-url origin
|
|
|
57
57
|
|
|
58
58
|
### Step 3: 采集 git log
|
|
59
59
|
|
|
60
|
+
使用 `jfox ingest-log` 命令(基于 `jfox/git_extractor.py` 模块),一行完成提取 + 转换 + 导入:
|
|
61
|
+
|
|
60
62
|
```bash
|
|
61
|
-
|
|
63
|
+
jfox ingest-log path/to/repo --limit 50 --kb name --type fleeting
|
|
62
64
|
```
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
66
|
+
该命令会:
|
|
67
|
+
- 调用 `git log` 提取 commit 历史
|
|
68
|
+
- 自动解析为结构化数据(hash, subject, author, date, body)
|
|
69
|
+
- 转换为 fleeting 笔记并批量导入知识库
|
|
70
|
+
- 自动添加标签:`source:repo-name`, `source:git-log`
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
生成笔记示例:
|
|
71
73
|
```
|
|
72
74
|
Commit: a1b2c3d
|
|
73
75
|
Author: 张三
|
|
@@ -78,6 +80,8 @@ feat: add user authentication module
|
|
|
78
80
|
实现了 JWT 认证,支持 refresh token 机制。
|
|
79
81
|
```
|
|
80
82
|
|
|
83
|
+
> **约定**:`jfox ingest-log` 支持 `--format json` 输出 JSON,也可使用快捷方式 `--json`(两者等价)。下文示例统一使用 `--json`。
|
|
84
|
+
|
|
81
85
|
### Step 4: 采集 GitHub PRs
|
|
82
86
|
|
|
83
87
|
仅当 Step 1 检测到 GitHub 仓库时执行。
|
|
@@ -139,38 +143,40 @@ Comments:
|
|
|
139
143
|
- @zhangsan: 已修复,请验证
|
|
140
144
|
```
|
|
141
145
|
|
|
142
|
-
### Step 6:
|
|
146
|
+
### Step 6: 导入 GitHub 数据(git-log 已在 Step 3 完成)
|
|
147
|
+
|
|
148
|
+
git-log 数据已通过 `jfox ingest-log` 完成导入,此步骤仅处理 GitHub PR/Issues 数据。
|
|
143
149
|
|
|
144
150
|
**去重检查**:导入前检查知识库中是否已有该仓库的数据:
|
|
145
151
|
```bash
|
|
146
|
-
jfox search "
|
|
152
|
+
jfox search "repo-name" --format json
|
|
147
153
|
```
|
|
148
154
|
|
|
149
|
-
如果已有记录,只导入新增的条目(通过
|
|
155
|
+
如果已有记录,只导入新增的条目(通过 PR 编号、Issue 编号判断)。
|
|
150
156
|
|
|
151
|
-
**生成临时 JSON
|
|
157
|
+
**生成临时 JSON 文件**:将 PR/Issues 数据组装为 JSON 数组(仅 GitHub 数据):
|
|
152
158
|
|
|
153
159
|
```json
|
|
154
160
|
[
|
|
155
|
-
{
|
|
156
|
-
"title": "feat: add user authentication module",
|
|
157
|
-
"content": "Commit: a1b2c3d\nAuthor: 张三\nDate: 2026-04-10\n\n实现了 JWT 认证,支持 refresh token 机制。",
|
|
158
|
-
"tags": ["source:my-app", "source:git-log"]
|
|
159
|
-
},
|
|
160
161
|
{
|
|
161
162
|
"title": "Add user authentication",
|
|
162
163
|
"content": "PR #42: Add user authentication\nState: merged\nAuthor: zhangsan\n...",
|
|
163
164
|
"tags": ["source:my-app", "source:pr"]
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"title": "Login page crashes on mobile",
|
|
168
|
+
"content": "Issue #15: Login page crashes on mobile\nState: closed\n...",
|
|
169
|
+
"tags": ["source:my-app", "source:issue"]
|
|
164
170
|
}
|
|
165
171
|
]
|
|
166
172
|
```
|
|
167
173
|
|
|
168
174
|
保存到临时文件(使用跨平台路径),然后执行导入:
|
|
169
175
|
```bash
|
|
170
|
-
jfox bulk-import
|
|
176
|
+
jfox bulk-import temp-file.json --type fleeting --kb name
|
|
171
177
|
```
|
|
172
178
|
|
|
173
|
-
>
|
|
179
|
+
> **约定**:`jfox bulk-import` 默认输出 JSON。使用 `--no-json` 切换为 table 格式,或 `--format table` 显式指定。
|
|
174
180
|
|
|
175
181
|
### Step 7: 确认报告
|
|
176
182
|
|
|
@@ -191,10 +197,12 @@ jfox bulk-import <temp-file.json> --type fleeting [--kb <name>]
|
|
|
191
197
|
jfox add "<content>" --title "<title>" --type fleeting --tag <tags> [--kb <name>]
|
|
192
198
|
```
|
|
193
199
|
|
|
194
|
-
>
|
|
200
|
+
> **约定**:`jfox add` 支持 `--format json` 输出 JSON,也可使用快捷方式 `--json`(两者等价)。
|
|
195
201
|
|
|
196
202
|
## 笔记格式规范
|
|
197
203
|
|
|
204
|
+
> git-log 格式由 `jfox ingest-log` 自动处理,以下规范主要供理解输出结构参考,以及手动处理 GitHub PR/Issues 数据时使用。
|
|
205
|
+
|
|
198
206
|
| 数据源 | title 来源 | content 内容 | 额外标签 |
|
|
199
207
|
|--------|-----------|-------------|---------|
|
|
200
208
|
| git-log | commit subject | hash, author, date, body | `source:<repo-name>`, `source:git-log` |
|
|
@@ -212,26 +220,30 @@ jfox add "<content>" --title "<title>" --type fleeting --tag <tags> [--kb <name>
|
|
|
212
220
|
|
|
213
221
|
```bash
|
|
214
222
|
# 检测仓库类型
|
|
215
|
-
git -C
|
|
223
|
+
git -C path/to/repo remote get-url origin
|
|
216
224
|
gh auth status
|
|
217
225
|
|
|
218
|
-
#
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
gh
|
|
226
|
+
# 采集 git log(一行完成提取+导入)
|
|
227
|
+
jfox ingest-log path/to/repo --limit 50 --kb name --type fleeting
|
|
228
|
+
|
|
229
|
+
# 采集 GitHub 数据
|
|
230
|
+
gh pr list --repo owner/repo --state all --limit 20 --json number,title,body,state,author,createdAt,updatedAt,labels
|
|
231
|
+
gh pr view number --repo owner/repo --json comments
|
|
232
|
+
gh issue list --repo owner/repo --state all --limit 30 --json number,title,body,state,author,createdAt,labels,comments
|
|
223
233
|
|
|
224
234
|
# 去重检查
|
|
225
|
-
jfox search "
|
|
235
|
+
jfox search "repo-name" --format json
|
|
236
|
+
|
|
237
|
+
# 导入 GitHub 数据
|
|
238
|
+
jfox bulk-import file.json --type fleeting --kb name
|
|
226
239
|
|
|
227
|
-
#
|
|
228
|
-
jfox
|
|
229
|
-
jfox add "<content>" --title "<title>" --type fleeting [--kb <name>]
|
|
240
|
+
# 手动添加单条笔记
|
|
241
|
+
jfox add "content" --title "title" --type fleeting --kb name
|
|
230
242
|
```
|
|
231
243
|
|
|
232
244
|
## 错误处理
|
|
233
245
|
|
|
234
|
-
- **"Not a git repository"**:
|
|
235
|
-
- **`gh: not found`** 或 `gh auth status` 失败: 跳过 GitHub PR/Issues
|
|
246
|
+
- **"Not a git repository"**: `jfox ingest-log` 会报错,提示用户提供正确的仓库路径
|
|
247
|
+
- **`gh: not found`** 或 `gh auth status` 失败: 跳过 GitHub PR/Issues 导入,仅用 `jfox ingest-log` 导入 git log
|
|
236
248
|
- **"Knowledge base not found"**: 提示用户先运行 `/jfox-common` 创建知识库
|
|
237
249
|
- **Bulk import 部分失败**: 报告成功/失败数量,失败记录不重试
|
|
@@ -62,7 +62,7 @@ jfox inbox --json --limit 50
|
|
|
62
62
|
jfox delete <原始-id> --force
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
>
|
|
65
|
+
> **约定**:所有命令均支持 `--format json` 输出 JSON,也可使用快捷方式 `--json`(两者等价)。下文示例统一使用 `--json`。
|
|
66
66
|
|
|
67
67
|
### 提炼策略表
|
|
68
68
|
|
|
@@ -91,9 +91,12 @@ jfox graph --orphans --json
|
|
|
91
91
|
3. 如果有匹配度 >= 0.6 的结果,建议添加链接:
|
|
92
92
|
```bash
|
|
93
93
|
jfox edit <孤立笔记_id> --content "原内容... [[相关笔记标题]]"
|
|
94
|
+
|
|
95
|
+
# 使用文件内容编辑(适合长文本)
|
|
96
|
+
jfox edit <孤立笔记_id> --content-file updated.md
|
|
94
97
|
```
|
|
95
98
|
|
|
96
|
-
>
|
|
99
|
+
> **约定**:`jfox edit` 支持 `--format json`,也可使用快捷方式 `--json`(两者等价)。
|
|
97
100
|
|
|
98
101
|
### 确认改善
|
|
99
102
|
|
|
@@ -117,6 +120,12 @@ jfox graph --stats --json
|
|
|
117
120
|
**创建笔记**:
|
|
118
121
|
```bash
|
|
119
122
|
jfox add "<内容>" --title "<标题>" --type <fleeting|permanent> --tag <tags> [--kb <name>]
|
|
123
|
+
|
|
124
|
+
# 从文件读取内容(适合长文本,避免 shell 转义问题)
|
|
125
|
+
jfox add --content-file notes/draft.md --title "<标题>" --type permanent --tag <tags> [--kb <name>]
|
|
126
|
+
|
|
127
|
+
# 从 stdin 读取
|
|
128
|
+
echo "内容" | jfox add --content-file - --title "<标题>" --type fleeting
|
|
120
129
|
```
|
|
121
130
|
|
|
122
131
|
对于 permanent 笔记,先运行 `jfox suggest-links` 查找 `[[wiki links]]` 再插入。
|
|
@@ -151,6 +160,7 @@ jfox graph --orphans --json # 查找孤立笔记
|
|
|
151
160
|
jfox graph --stats --json # 图谱统计指标
|
|
152
161
|
jfox list --format json --limit <N> # 列出笔记
|
|
153
162
|
jfox daily --json # 查看今天的笔记
|
|
163
|
+
jfox daily --date 2026-04-01 --json # 查看指定日期的笔记
|
|
154
164
|
```
|
|
155
165
|
|
|
156
166
|
## 错误处理
|
|
@@ -158,7 +168,7 @@ jfox daily --json # 查看今天的笔记
|
|
|
158
168
|
- **收件箱为空** → 告知用户 "收件箱为空,无需整理",可跳到 Step 3 图谱优化
|
|
159
169
|
- **`jfox suggest-links` 返回低匹配度**(score < 0.6)→ 跳过链接推荐,不强制添加
|
|
160
170
|
- **`jfox delete` 目标 ID 不存在** → 报告错误,跳过继续处理其他笔记
|
|
161
|
-
- **`jfox add` / `jfox edit` / `jfox delete
|
|
171
|
+
- **`jfox add` / `jfox edit` / `jfox delete`** 支持 `--format json`,也可使用快捷方式 `--json`
|
|
162
172
|
|
|
163
173
|
## 使用建议
|
|
164
174
|
|
|
@@ -324,6 +324,55 @@ class TestCLIFormat:
|
|
|
324
324
|
# ==========================================================================
|
|
325
325
|
# Add 命令测试
|
|
326
326
|
# ==========================================================================
|
|
327
|
+
def test_add_content_file(self, cli, tmp_path):
|
|
328
|
+
"""测试 add 命令 --content-file 从文件读取内容"""
|
|
329
|
+
content_file = tmp_path / "note.md"
|
|
330
|
+
content_file.write_text("从文件读取的笔记内容", encoding="utf-8")
|
|
331
|
+
|
|
332
|
+
result = cli.run("add", "--content-file", str(content_file), "--title", "FileContent")
|
|
333
|
+
|
|
334
|
+
assert result.success
|
|
335
|
+
data = result.data
|
|
336
|
+
assert data["success"] is True
|
|
337
|
+
assert data["note"]["title"] == "FileContent"
|
|
338
|
+
|
|
339
|
+
def test_add_content_file_mutual_exclusive(self, cli, tmp_path):
|
|
340
|
+
"""测试 add 命令 content 参数和 --content-file 不能同时指定"""
|
|
341
|
+
content_file = tmp_path / "note.md"
|
|
342
|
+
content_file.write_text("file content", encoding="utf-8")
|
|
343
|
+
|
|
344
|
+
result = cli.run("add", "直接内容", "--content-file", str(content_file))
|
|
345
|
+
|
|
346
|
+
assert not result.success
|
|
347
|
+
assert "不能同时指定" in result.stderr or "不能同时指定" in result.stdout
|
|
348
|
+
|
|
349
|
+
def test_add_content_file_not_found(self, cli):
|
|
350
|
+
"""测试 add 命令 --content-file 文件不存在时报错"""
|
|
351
|
+
result = cli.run("add", "--content-file", "/nonexistent/file.md")
|
|
352
|
+
|
|
353
|
+
assert not result.success
|
|
354
|
+
assert "文件不存在" in result.stderr or "文件不存在" in result.stdout
|
|
355
|
+
|
|
356
|
+
def test_add_content_file_empty(self, cli):
|
|
357
|
+
"""测试 add 命令不提供内容时报错"""
|
|
358
|
+
result = cli.run("add")
|
|
359
|
+
|
|
360
|
+
assert not result.success
|
|
361
|
+
|
|
362
|
+
def test_add_content_file_with_wiki_links(self, cli, tmp_path):
|
|
363
|
+
"""测试 add 命令 --content-file 内容中的 [[wiki链接]] 正常解析"""
|
|
364
|
+
# 先创建一个目标笔记
|
|
365
|
+
cli.add("目标笔记内容", title="目标笔记")
|
|
366
|
+
|
|
367
|
+
content_file = tmp_path / "linked.md"
|
|
368
|
+
content_file.write_text("引用 [[目标笔记]] 的内容", encoding="utf-8")
|
|
369
|
+
|
|
370
|
+
result = cli.run("add", "--content-file", str(content_file), "--title", "带链接的笔记")
|
|
371
|
+
|
|
372
|
+
assert result.success
|
|
373
|
+
data = result.data
|
|
374
|
+
assert data["note"]["links"]
|
|
375
|
+
|
|
327
376
|
def test_add_format_json(self, cli):
|
|
328
377
|
"""测试 add 命令 --format json"""
|
|
329
378
|
result = cli.run("add", "test content", "--title", "FormatTest", "--format", "json")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""测试日志配置:第三方库日志级别应被抑制为 WARNING"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_third_party_loggers_suppressed():
|
|
7
|
+
"""导入 jfox.cli 后,第三方库的日志级别应为 WARNING 或更高"""
|
|
8
|
+
import jfox.cli # noqa: F401
|
|
9
|
+
|
|
10
|
+
noisy_libs = [
|
|
11
|
+
"sentence_transformers",
|
|
12
|
+
"torch",
|
|
13
|
+
"chromadb",
|
|
14
|
+
"tqdm",
|
|
15
|
+
"urllib3",
|
|
16
|
+
"watchdog",
|
|
17
|
+
"PIL",
|
|
18
|
+
]
|
|
19
|
+
for lib in noisy_libs:
|
|
20
|
+
lib_logger = logging.getLogger(lib)
|
|
21
|
+
assert (
|
|
22
|
+
lib_logger.level >= logging.WARNING
|
|
23
|
+
), f"{lib} logger level is {lib_logger.level}, expected >= {logging.WARNING}"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_jfox_own_logger_unchanged():
|
|
27
|
+
"""jfox 自身的日志不应被抑制(保持继承 root 的 INFO)"""
|
|
28
|
+
import jfox.cli # noqa: F401
|
|
29
|
+
|
|
30
|
+
jfox_logger = logging.getLogger("jfox")
|
|
31
|
+
# jfox logger 未被显式设为 WARNING,保持 NOTSET 并继承 root 的 INFO
|
|
32
|
+
assert (
|
|
33
|
+
jfox_logger.level < logging.WARNING
|
|
34
|
+
), f"jfox logger level is {jfox_logger.level}, should NOT be suppressed to WARNING"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-ci-coverage-optimization.md
RENAMED
|
File without changes
|
|
File without changes
|
{jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-fix-index-rebuild-clear.md
RENAMED
|
File without changes
|
{jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/plans/2026-04-12-fix-index-verify-id-mismatch.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/specs/2026-04-12-skill-redesign-design.md
RENAMED
|
File without changes
|
{jfox_cli-0.2.1 → jfox_cli-0.2.2}/docs/superpowers/specs/2026-04-13-pr-auto-code-review-design.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|