paperfit-cli 1.0.0

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 (65) hide show
  1. package/.claude/commands/adjust-length.md +21 -0
  2. package/.claude/commands/check-visual.md +27 -0
  3. package/.claude/commands/fix-layout.md +31 -0
  4. package/.claude/commands/migrate-template.md +23 -0
  5. package/.claude/commands/repair-table.md +21 -0
  6. package/.claude/commands/show-status.md +32 -0
  7. package/.claude-plugin/README.md +77 -0
  8. package/.claude-plugin/marketplace.json +41 -0
  9. package/.claude-plugin/plugin.json +39 -0
  10. package/CLAUDE.md +266 -0
  11. package/CONTRIBUTING.md +131 -0
  12. package/LICENSE +21 -0
  13. package/README.md +164 -0
  14. package/agents/code-surgeon-agent.md +214 -0
  15. package/agents/layout-detective-agent.md +229 -0
  16. package/agents/orchestrator-agent.md +254 -0
  17. package/agents/quality-gatekeeper-agent.md +270 -0
  18. package/agents/rule-engine-agent.md +224 -0
  19. package/agents/semantic-polish-agent.md +250 -0
  20. package/bin/paperfit.js +176 -0
  21. package/config/agent_roles.yaml +56 -0
  22. package/config/layout_rules.yaml +54 -0
  23. package/config/templates.yaml +241 -0
  24. package/config/vto_taxonomy.yaml +489 -0
  25. package/config/writing_rules.yaml +64 -0
  26. package/install.sh +30 -0
  27. package/package.json +52 -0
  28. package/requirements.txt +5 -0
  29. package/scripts/benchmark_runner.py +629 -0
  30. package/scripts/compile.sh +244 -0
  31. package/scripts/config_validator.py +339 -0
  32. package/scripts/cv_detector.py +600 -0
  33. package/scripts/evidence_collector.py +167 -0
  34. package/scripts/float_fixers.py +861 -0
  35. package/scripts/inject_defects.py +549 -0
  36. package/scripts/install-claude-global.js +148 -0
  37. package/scripts/install.js +66 -0
  38. package/scripts/install.sh +106 -0
  39. package/scripts/overflow_fixers.py +656 -0
  40. package/scripts/package-for-opensource.sh +138 -0
  41. package/scripts/parse_log.py +260 -0
  42. package/scripts/postinstall.js +38 -0
  43. package/scripts/pre_tool_use.py +265 -0
  44. package/scripts/render_pages.py +244 -0
  45. package/scripts/session_logger.py +329 -0
  46. package/scripts/space_util_fixers.py +773 -0
  47. package/scripts/state_manager.py +352 -0
  48. package/scripts/test_commands.py +187 -0
  49. package/scripts/test_cv_detector.py +214 -0
  50. package/scripts/test_integration.py +290 -0
  51. package/skills/consistency-polisher/SKILL.md +337 -0
  52. package/skills/float-optimizer/SKILL.md +284 -0
  53. package/skills/latex_fixers/__init__.py +82 -0
  54. package/skills/latex_fixers/float_fixers.py +392 -0
  55. package/skills/latex_fixers/fullwidth_fixers.py +375 -0
  56. package/skills/latex_fixers/overflow_fixers.py +250 -0
  57. package/skills/latex_fixers/semantic_micro_tuning.py +362 -0
  58. package/skills/latex_fixers/space_util_fixers.py +389 -0
  59. package/skills/latex_fixers/utils.py +55 -0
  60. package/skills/overflow-repair/SKILL.md +304 -0
  61. package/skills/space-util-fixer/SKILL.md +307 -0
  62. package/skills/taxonomy-vto/SKILL.md +486 -0
  63. package/skills/template-migrator/SKILL.md +251 -0
  64. package/skills/visual-inspector/SKILL.md +217 -0
  65. package/skills/writing-polish/SKILL.md +289 -0
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 证据收集器
4
+
5
+ 汇总多模态证据链,供 quality-gatekeeper-agent 验收使用。
6
+ 收集内容包括:编译日志摘要、页图清单、代码变更 diff、诊断报告路径等。
7
+
8
+ 用法:
9
+ python evidence_collector.py --round <N> --output <dir>
10
+ """
11
+
12
+ import json
13
+ import shutil
14
+ import argparse
15
+ import subprocess
16
+ from pathlib import Path
17
+ from datetime import datetime
18
+ from typing import Dict, List, Any, Optional
19
+
20
+
21
+ class EvidenceCollector:
22
+ """证据收集器"""
23
+
24
+ def __init__(self, project_root: Path = None):
25
+ self.project_root = project_root or Path.cwd()
26
+ self.data_dir = self.project_root / "data"
27
+ self.evidence_dir = self.data_dir / "evidence"
28
+
29
+ def collect(
30
+ self,
31
+ round_num: int,
32
+ log_file: Optional[Path] = None,
33
+ pages_dir: Optional[Path] = None,
34
+ diagnostic_report: Optional[Path] = None,
35
+ modified_files: Optional[List[Path]] = None,
36
+ state_file: Optional[Path] = None
37
+ ) -> Dict[str, Any]:
38
+ """收集指定轮次的所有证据并打包"""
39
+ self.evidence_dir.mkdir(parents=True, exist_ok=True)
40
+ round_evidence_dir = self.evidence_dir / f"round_{round_num:02d}"
41
+ round_evidence_dir.mkdir(exist_ok=True)
42
+
43
+ evidence = {
44
+ "round": round_num,
45
+ "timestamp": datetime.now().isoformat(),
46
+ "files": {}
47
+ }
48
+
49
+ # 1. 复制编译日志
50
+ if log_file and log_file.exists():
51
+ dest = round_evidence_dir / "compile.log"
52
+ shutil.copy2(log_file, dest)
53
+ evidence["files"]["log"] = str(dest)
54
+ evidence["log_summary"] = self._summarize_log(log_file)
55
+
56
+ # 2. 复制页图目录
57
+ if pages_dir and pages_dir.exists():
58
+ dest_pages = round_evidence_dir / "pages"
59
+ if dest_pages.exists():
60
+ shutil.rmtree(dest_pages)
61
+ shutil.copytree(pages_dir, dest_pages)
62
+ evidence["files"]["pages_dir"] = str(dest_pages)
63
+ evidence["page_count"] = len(list(dest_pages.glob("page_*.png")))
64
+
65
+ # 3. 复制诊断报告
66
+ if diagnostic_report and diagnostic_report.exists():
67
+ dest = round_evidence_dir / diagnostic_report.name
68
+ shutil.copy2(diagnostic_report, dest)
69
+ evidence["files"]["diagnostic_report"] = str(dest)
70
+
71
+ # 4. 记录修改的文件及 diff
72
+ if modified_files:
73
+ evidence["modified_files"] = [str(f) for f in modified_files]
74
+ evidence["diffs"] = self._collect_diffs(modified_files, round_evidence_dir)
75
+
76
+ # 5. 复制状态文件快照
77
+ if state_file and state_file.exists():
78
+ dest = round_evidence_dir / "state_snapshot.json"
79
+ shutil.copy2(state_file, dest)
80
+ evidence["files"]["state_snapshot"] = str(dest)
81
+
82
+ # 6. 生成证据清单
83
+ manifest_path = round_evidence_dir / "manifest.json"
84
+ with open(manifest_path, "w", encoding="utf-8") as f:
85
+ json.dump(evidence, f, indent=2, ensure_ascii=False)
86
+
87
+ return evidence
88
+
89
+ def _summarize_log(self, log_path: Path) -> Dict[str, int]:
90
+ """快速提取日志摘要"""
91
+ summary = {
92
+ "errors": 0,
93
+ "warnings": 0,
94
+ "overfull_hbox": 0,
95
+ "underfull_hbox": 0
96
+ }
97
+ try:
98
+ content = log_path.read_text(encoding="utf-8", errors="ignore")
99
+ summary["errors"] = content.count("!")
100
+ summary["warnings"] = content.count("Warning:")
101
+ summary["overfull_hbox"] = content.count("Overfull \\hbox")
102
+ summary["underfull_hbox"] = content.count("Underfull \\hbox")
103
+ except Exception:
104
+ pass
105
+ return summary
106
+
107
+ def _collect_diffs(self, modified_files: List[Path], dest_dir: Path) -> Dict[str, str]:
108
+ """收集文件的 git diff 或简单前后对比(需要备份支持)"""
109
+ diffs = {}
110
+ backups_dir = self.data_dir / "backups"
111
+
112
+ for file_path in modified_files:
113
+ # 尝试找到最近的备份
114
+ backup_pattern = f"{file_path.name}_*"
115
+ backups = sorted(backups_dir.glob(backup_pattern), reverse=True)
116
+ if backups:
117
+ # 生成 diff
118
+ try:
119
+ result = subprocess.run(
120
+ ["diff", "-u", str(backups[0]), str(file_path)],
121
+ capture_output=True,
122
+ text=True
123
+ )
124
+ diffs[str(file_path)] = result.stdout
125
+ except Exception:
126
+ diffs[str(file_path)] = f"# Unable to generate diff for {file_path}"
127
+ else:
128
+ diffs[str(file_path)] = f"# No backup found for {file_path}"
129
+
130
+ # 保存 diff 文件
131
+ diff_file = dest_dir / "changes.diff"
132
+ with open(diff_file, "w", encoding="utf-8") as f:
133
+ for path, diff_content in diffs.items():
134
+ f.write(f"--- {path}\n")
135
+ f.write(diff_content)
136
+ f.write("\n\n")
137
+
138
+ return diffs
139
+
140
+
141
+ def main():
142
+ parser = argparse.ArgumentParser(description="收集 PaperFit 迭代证据")
143
+ parser.add_argument("--round", "-r", type=int, required=True, help="迭代轮次")
144
+ parser.add_argument("--log", help="编译日志路径")
145
+ parser.add_argument("--pages", help="页图目录")
146
+ parser.add_argument("--report", help="诊断报告路径")
147
+ parser.add_argument("--modified", nargs="+", help="修改的文件列表")
148
+ parser.add_argument("--state", help="状态文件路径")
149
+ parser.add_argument("--output", "-o", help="输出目录", default="data/evidence")
150
+
151
+ args = parser.parse_args()
152
+
153
+ collector = EvidenceCollector()
154
+ evidence = collector.collect(
155
+ round_num=args.round,
156
+ log_file=Path(args.log) if args.log else None,
157
+ pages_dir=Path(args.pages) if args.pages else None,
158
+ diagnostic_report=Path(args.report) if args.report else None,
159
+ modified_files=[Path(f) for f in args.modified] if args.modified else None,
160
+ state_file=Path(args.state) if args.state else None
161
+ )
162
+
163
+ print(json.dumps(evidence, indent=2, ensure_ascii=False))
164
+
165
+
166
+ if __name__ == "__main__":
167
+ main()