psyclaw 0.28.0 → 0.28.2
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.
- package/README.md +5 -5
- package/agents/recommended/catalog.json +99 -0
- package/dist/apps/panel/index.html +5 -5
- package/dist/src/adapters/pi/extension.js +164 -18
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/adapters/pi/rpc.d.ts +2 -0
- package/dist/src/adapters/pi/rpc.js +9 -2
- package/dist/src/adapters/pi/rpc.js.map +1 -1
- package/dist/src/agents/recommended-personas.d.ts +42 -0
- package/dist/src/agents/recommended-personas.js +135 -0
- package/dist/src/agents/recommended-personas.js.map +1 -0
- package/dist/src/ars/mode-editor.d.ts +20 -0
- package/dist/src/ars/mode-editor.js +57 -0
- package/dist/src/ars/mode-editor.js.map +1 -0
- package/dist/src/ars/profile.d.ts +4 -1
- package/dist/src/ars/profile.js +23 -6
- package/dist/src/ars/profile.js.map +1 -1
- package/dist/src/cli.js +6 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/creation/contracts.d.ts +3 -0
- package/dist/src/creation/service.js +17 -5
- package/dist/src/creation/service.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/orchestration/bundled-personas.d.ts +16 -0
- package/dist/src/orchestration/bundled-personas.js +101 -0
- package/dist/src/orchestration/bundled-personas.js.map +1 -0
- package/dist/src/orchestration/effects.d.ts +8 -0
- package/dist/src/orchestration/effects.js +39 -0
- package/dist/src/orchestration/effects.js.map +1 -0
- package/dist/src/orchestration/personas.d.ts +2 -1
- package/dist/src/orchestration/personas.js +20 -7
- package/dist/src/orchestration/personas.js.map +1 -1
- package/dist/src/orchestration/pi-executor.d.ts +9 -5
- package/dist/src/orchestration/pi-executor.js +55 -22
- package/dist/src/orchestration/pi-executor.js.map +1 -1
- package/dist/src/orchestration/research-agents.d.ts +3 -7
- package/dist/src/orchestration/research-agents.js +5 -69
- package/dist/src/orchestration/research-agents.js.map +1 -1
- package/dist/src/panel/server.js +6 -0
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/style/cli-ui.d.ts +14 -0
- package/dist/src/style/cli-ui.js +45 -0
- package/dist/src/style/cli-ui.js.map +1 -1
- package/dist/src/tui/skill-manager.js +1 -1
- package/dist/src/tui/skill-manager.js.map +1 -1
- package/dist/src/updates/update.js +9 -3
- package/dist/src/updates/update.js.map +1 -1
- package/package.json +11 -3
- package/vendor/academic-paper-skills/LICENSE +21 -0
- package/vendor/academic-paper-skills/NOTICE.md +7 -0
- package/vendor/academic-paper-skills/PSYCLAW_SOURCE.json +14 -0
- package/vendor/academic-paper-skills/academic-paper-composer/SKILL.md +840 -0
- package/vendor/academic-paper-skills/academic-paper-composer/references/section_guides.md +675 -0
- package/vendor/academic-paper-skills/academic-paper-composer/references/writing_standards.md +629 -0
- package/vendor/academic-paper-skills/academic-paper-composer/scripts/chapter_quality_check.py +470 -0
- package/vendor/academic-paper-skills/academic-paper-composer/scripts/final_evaluation.py +550 -0
- package/vendor/academic-paper-skills/academic-paper-strategist/SKILL.md +670 -0
- package/vendor/academic-paper-skills/academic-paper-strategist/references/quality_standards.md +336 -0
- package/vendor/academic-paper-skills/academic-paper-strategist/references/search_strategy.md +459 -0
- package/vendor/academic-paper-skills/academic-paper-strategist/scripts/evaluate_samples.py +300 -0
- package/vendor/academic-paper-skills/academic-paper-strategist/scripts/gap_analysis.py +399 -0
- package/vendor/ars/pi/wrapper.js +9 -0
- package/vendor/nature-skills/LICENSE +201 -0
- package/vendor/nature-skills/NOTICE.md +7 -0
- package/vendor/nature-skills/PSYCLAW_SOURCE.json +19 -0
- package/vendor/nature-skills/skills/nature-figure/README.md +107 -0
- package/vendor/nature-skills/skills/nature-figure/README_EN.md +107 -0
- package/vendor/nature-skills/skills/nature-figure/SKILL.md +154 -0
- package/vendor/nature-skills/skills/nature-figure/agents/openai.yaml +4 -0
- package/vendor/nature-skills/skills/nature-figure/evals/evals.json +325 -0
- package/vendor/nature-skills/skills/nature-figure/manifest.yaml +103 -0
- package/vendor/nature-skills/skills/nature-figure/references/ai-graphical-abstract-workflow.md +129 -0
- package/vendor/nature-skills/skills/nature-figure/references/api.md +576 -0
- package/vendor/nature-skills/skills/nature-figure/references/asset-adaptation.md +72 -0
- package/vendor/nature-skills/skills/nature-figure/references/backend-selection.md +118 -0
- package/vendor/nature-skills/skills/nature-figure/references/chart-types.md +348 -0
- package/vendor/nature-skills/skills/nature-figure/references/common-patterns.md +371 -0
- package/vendor/nature-skills/skills/nature-figure/references/demos.md +58 -0
- package/vendor/nature-skills/skills/nature-figure/references/design-theory.md +467 -0
- package/vendor/nature-skills/skills/nature-figure/references/figure-contract.md +122 -0
- package/vendor/nature-skills/skills/nature-figure/references/figure-legend-conventions.md +90 -0
- package/vendor/nature-skills/skills/nature-figure/references/multipanel-evidence-architecture.md +268 -0
- package/vendor/nature-skills/skills/nature-figure/references/nature-2026-observations.md +124 -0
- package/vendor/nature-skills/skills/nature-figure/references/nature-article-requirements.md +149 -0
- package/vendor/nature-skills/skills/nature-figure/references/openrouter-image-generation.md +141 -0
- package/vendor/nature-skills/skills/nature-figure/references/qa-contract.md +344 -0
- package/vendor/nature-skills/skills/nature-figure/references/r-template-index.md +66 -0
- package/vendor/nature-skills/skills/nature-figure/references/r-workflow.md +234 -0
- package/vendor/nature-skills/skills/nature-figure/references/template-catalog.md +33 -0
- package/vendor/nature-skills/skills/nature-figure/references/tutorials.md +260 -0
- package/vendor/nature-skills/skills/nature-figure/requirements.txt +1 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/audit_figure_collisions.py +742 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/audit_panel_alignment.py +933 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/audit_pdf_text.py +152 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/figure_safety.py +50 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/generate_openrouter_schematic.py +260 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/nature_figure_backend.py +93 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/panel_alignment.R +188 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/plot_templates.py +604 -0
- package/vendor/nature-skills/skills/nature-figure/scripts/validate_figure.py +835 -0
- package/vendor/nature-skills/skills/nature-figure/static/core/contract.md +55 -0
- package/vendor/nature-skills/skills/nature-figure/static/core/stance.md +41 -0
- package/vendor/nature-skills/skills/nature-figure/static/fragments/backend/python.md +56 -0
- package/vendor/nature-skills/skills/nature-figure/static/fragments/backend/r.md +63 -0
- package/vendor/nature-skills/skills/nature-figure/tests/test_figure_safety.py +209 -0
- package/vendor/nature-skills/skills/nature-polishing/README.md +58 -0
- package/vendor/nature-skills/skills/nature-polishing/README_EN.md +58 -0
- package/vendor/nature-skills/skills/nature-polishing/SKILL.md +111 -0
- package/vendor/nature-skills/skills/nature-polishing/agents/openai.yaml +4 -0
- package/vendor/nature-skills/skills/nature-polishing/manifest.yaml +101 -0
- package/vendor/nature-skills/skills/nature-polishing/references/latex-layout.md +211 -0
- package/vendor/nature-skills/skills/nature-polishing/references/nat-comms-2025-diction.md +73 -0
- package/vendor/nature-skills/skills/nature-polishing/references/phrasebank-playbook.md +175 -0
- package/vendor/nature-skills/skills/nature-polishing/references/published-article-patterns.md +126 -0
- package/vendor/nature-skills/skills/nature-polishing/references/section-moves.md +252 -0
- package/vendor/nature-skills/skills/nature-polishing/references/style-guardrails.md +94 -0
- package/vendor/nature-skills/skills/nature-polishing/references/writing-strategy.md +160 -0
- package/vendor/nature-skills/skills/nature-polishing/static/core/failure-modes.md +29 -0
- package/vendor/nature-skills/skills/nature-polishing/static/core/output-format.md +21 -0
- package/vendor/nature-skills/skills/nature-polishing/static/core/stance.md +26 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/generic.md +17 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nat-comms.md +38 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nat-mach-intell.md +102 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nature.md +19 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/language/en.md +19 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/language/zh-to-en.md +19 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/algorithmic.md +18 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/hypothesis.md +17 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/methods.md +35 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/research.md +29 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/review.md +17 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/abstract.md +26 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/conclusion.md +15 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/discussion.md +48 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/intro.md +33 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/methods.md +26 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/results.md +61 -0
- package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/title.md +17 -0
- package/vendor/nature-skills/skills/nature-ref-verifier/README.md +44 -0
- package/vendor/nature-skills/skills/nature-ref-verifier/README_EN.md +44 -0
- package/vendor/nature-skills/skills/nature-ref-verifier/SKILL.md +196 -0
- package/vendor/nature-skills/skills/nature-ref-verifier/agents/openai.yaml +4 -0
- package/vendor/nature-skills/skills/nature-ref-verifier/manifest.yaml +19 -0
- package/vendor/nature-skills/skills/nature-ref-verifier/references/common-patterns.md +96 -0
- package/vendor/nature-skills/skills/nature-shared/README.md +43 -0
- package/vendor/nature-skills/skills/nature-shared/README_EN.md +43 -0
- package/vendor/nature-skills/skills/nature-shared/SKILL.md +33 -0
- package/vendor/nature-skills/skills/nature-shared/agents/openai.yaml +6 -0
- package/vendor/nature-skills/skills/nature-shared/core/consistency-sweep.md +122 -0
- package/vendor/nature-skills/skills/nature-shared/core/discussion-argument-language.md +192 -0
- package/vendor/nature-skills/skills/nature-shared/core/ethics.md +88 -0
- package/vendor/nature-skills/skills/nature-shared/core/main-text-discipline.md +179 -0
- package/vendor/nature-skills/skills/nature-shared/core/nature-abstract.md +171 -0
- package/vendor/nature-skills/skills/nature-shared/core/nature-introduction.md +164 -0
- package/vendor/nature-skills/skills/nature-shared/core/nature-results-discussion.md +215 -0
- package/vendor/nature-skills/skills/nature-shared/core/paper-type-taxonomy.md +41 -0
- package/vendor/nature-skills/skills/nature-shared/core/reader-workflow.md +21 -0
- package/vendor/nature-skills/skills/nature-shared/core/research-compliance.md +192 -0
- package/vendor/nature-skills/skills/nature-shared/core/terminology-ledger.md +58 -0
- package/vendor/nature-skills/skills/nature-shared/journal-formats/nat-comms.md +139 -0
- package/vendor/nature-skills/skills/nature-shared/journal-formats/nature-machine-intelligence.md +431 -0
- package/vendor/nature-skills/skills/nature-shared/journal-formats/nature.md +313 -0
- package/vendor/nature-skills/skills/nature-shared/manifest.yaml +52 -0
- package/vendor/nature-skills/skills/nature-shared/scripts/check_consistency.py +273 -0
- package/vendor/nature-skills/skills/nature-shared/tests/test_check_consistency.py +63 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Chapter Quality Check Script
|
|
4
|
+
|
|
5
|
+
This script evaluates individual chapters against quality standards.
|
|
6
|
+
Used after writing each chapter to ensure it meets threshold before proceeding.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Dict, List
|
|
12
|
+
|
|
13
|
+
def create_chapter_evaluation_template() -> Dict:
|
|
14
|
+
"""Create template for evaluating a chapter."""
|
|
15
|
+
return {
|
|
16
|
+
"chapter_id": "", # e.g., "chapter_2"
|
|
17
|
+
"chapter_title": "",
|
|
18
|
+
"target_word_count": 0,
|
|
19
|
+
"actual_word_count": 0,
|
|
20
|
+
"evaluation": {
|
|
21
|
+
"argument_quality": {
|
|
22
|
+
"score": 0, # 1-4
|
|
23
|
+
"notes": ""
|
|
24
|
+
},
|
|
25
|
+
"citation_quality": {
|
|
26
|
+
"score": 0, # 1-4
|
|
27
|
+
"notes": ""
|
|
28
|
+
},
|
|
29
|
+
"clarity_readability": {
|
|
30
|
+
"score": 0, # 1-4
|
|
31
|
+
"notes": ""
|
|
32
|
+
},
|
|
33
|
+
"structure_flow": {
|
|
34
|
+
"score": 0, # 1-4
|
|
35
|
+
"notes": ""
|
|
36
|
+
},
|
|
37
|
+
"platform_conformity": {
|
|
38
|
+
"score": 0, # 1-4
|
|
39
|
+
"notes": ""
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"issues": [], # List of specific issues identified
|
|
43
|
+
"revision_needed": "" # "none", "minor", "moderate", "major"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def calculate_chapter_score(evaluation: Dict) -> Dict:
|
|
47
|
+
"""Calculate total score and assess quality."""
|
|
48
|
+
scores = evaluation.get("evaluation", {})
|
|
49
|
+
|
|
50
|
+
total = sum([
|
|
51
|
+
scores.get("argument_quality", {}).get("score", 0),
|
|
52
|
+
scores.get("citation_quality", {}).get("score", 0),
|
|
53
|
+
scores.get("clarity_readability", {}).get("score", 0),
|
|
54
|
+
scores.get("structure_flow", {}).get("score", 0),
|
|
55
|
+
scores.get("platform_conformity", {}).get("score", 0)
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
percentage = (total / 20) * 100
|
|
59
|
+
passes = total >= 16 # 80% threshold
|
|
60
|
+
|
|
61
|
+
# Categorize scores
|
|
62
|
+
if total >= 18:
|
|
63
|
+
quality_level = "Excellent"
|
|
64
|
+
elif total >= 16:
|
|
65
|
+
quality_level = "Good"
|
|
66
|
+
elif total >= 14:
|
|
67
|
+
quality_level = "Acceptable (needs revision)"
|
|
68
|
+
else:
|
|
69
|
+
quality_level = "Poor (major revision required)"
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
"total_score": total,
|
|
73
|
+
"max_score": 20,
|
|
74
|
+
"percentage": round(percentage, 1),
|
|
75
|
+
"passes_threshold": passes,
|
|
76
|
+
"quality_level": quality_level
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def analyze_word_count(target: int, actual: int) -> Dict:
|
|
80
|
+
"""Analyze word count variance."""
|
|
81
|
+
if target == 0:
|
|
82
|
+
return {
|
|
83
|
+
"within_range": True,
|
|
84
|
+
"variance_percent": 0,
|
|
85
|
+
"assessment": "No target specified"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
variance = abs(actual - target)
|
|
89
|
+
variance_percent = (variance / target) * 100
|
|
90
|
+
|
|
91
|
+
if variance_percent <= 5:
|
|
92
|
+
within_range = True
|
|
93
|
+
assessment = "Perfect"
|
|
94
|
+
elif variance_percent <= 10:
|
|
95
|
+
within_range = True
|
|
96
|
+
assessment = "Good"
|
|
97
|
+
elif variance_percent <= 20:
|
|
98
|
+
within_range = False
|
|
99
|
+
assessment = "Acceptable"
|
|
100
|
+
else:
|
|
101
|
+
within_range = False
|
|
102
|
+
assessment = "Poor"
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
"within_range": within_range,
|
|
106
|
+
"variance_percent": round(variance_percent, 1),
|
|
107
|
+
"assessment": assessment,
|
|
108
|
+
"difference": actual - target
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
def identify_weak_dimensions(evaluation: Dict) -> List[Dict]:
|
|
112
|
+
"""Identify dimensions scoring below 3."""
|
|
113
|
+
scores = evaluation.get("evaluation", {})
|
|
114
|
+
weak_dimensions = []
|
|
115
|
+
|
|
116
|
+
dimensions = [
|
|
117
|
+
("argument_quality", "Argument Quality"),
|
|
118
|
+
("citation_quality", "Citation Quality"),
|
|
119
|
+
("clarity_readability", "Clarity & Readability"),
|
|
120
|
+
("structure_flow", "Structure & Flow"),
|
|
121
|
+
("platform_conformity", "Platform Style Conformity")
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
for key, name in dimensions:
|
|
125
|
+
dim_data = scores.get(key, {})
|
|
126
|
+
score = dim_data.get("score", 0)
|
|
127
|
+
|
|
128
|
+
if score < 3:
|
|
129
|
+
weak_dimensions.append({
|
|
130
|
+
"dimension": name,
|
|
131
|
+
"score": score,
|
|
132
|
+
"max_score": 4,
|
|
133
|
+
"notes": dim_data.get("notes", ""),
|
|
134
|
+
"severity": "high" if score <= 1 else "medium"
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
return weak_dimensions
|
|
138
|
+
|
|
139
|
+
def generate_recommendations(evaluation: Dict, weak_dims: List[Dict],
|
|
140
|
+
word_analysis: Dict) -> List[Dict]:
|
|
141
|
+
"""Generate specific revision recommendations."""
|
|
142
|
+
recommendations = []
|
|
143
|
+
|
|
144
|
+
# Word count issues
|
|
145
|
+
if word_analysis["variance_percent"] > 20:
|
|
146
|
+
recommendations.append({
|
|
147
|
+
"category": "word_count",
|
|
148
|
+
"priority": "medium",
|
|
149
|
+
"issue": f"Word count off by {word_analysis['variance_percent']:.1f}% ({word_analysis['difference']:+d} words)",
|
|
150
|
+
"action": "Cut or expand content to match target" if word_analysis['difference'] > 0 else "Expand content to match target"
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
# Weak dimensions
|
|
154
|
+
for dim in weak_dims:
|
|
155
|
+
priority = dim["severity"]
|
|
156
|
+
recommendations.append({
|
|
157
|
+
"category": dim["dimension"],
|
|
158
|
+
"priority": priority,
|
|
159
|
+
"issue": f"{dim['dimension']} scored {dim['score']}/4",
|
|
160
|
+
"action": f"Address: {dim['notes']}" if dim['notes'] else f"Improve {dim['dimension'].lower()}"
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
# Custom issues from evaluation
|
|
164
|
+
for issue in evaluation.get("issues", []):
|
|
165
|
+
recommendations.append({
|
|
166
|
+
"category": "custom",
|
|
167
|
+
"priority": "high",
|
|
168
|
+
"issue": issue,
|
|
169
|
+
"action": "Address this specific issue"
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
# Sort by priority
|
|
173
|
+
priority_order = {"high": 0, "medium": 1, "low": 2}
|
|
174
|
+
recommendations.sort(key=lambda x: priority_order.get(x["priority"], 3))
|
|
175
|
+
|
|
176
|
+
return recommendations
|
|
177
|
+
|
|
178
|
+
def generate_chapter_report(evaluation: Dict) -> str:
|
|
179
|
+
"""Generate comprehensive chapter evaluation report."""
|
|
180
|
+
score_result = calculate_chapter_score(evaluation)
|
|
181
|
+
word_analysis = analyze_word_count(
|
|
182
|
+
evaluation.get("target_word_count", 0),
|
|
183
|
+
evaluation.get("actual_word_count", 0)
|
|
184
|
+
)
|
|
185
|
+
weak_dims = identify_weak_dimensions(evaluation)
|
|
186
|
+
recommendations = generate_recommendations(evaluation, weak_dims, word_analysis)
|
|
187
|
+
|
|
188
|
+
report = f"""
|
|
189
|
+
# Chapter Quality Report
|
|
190
|
+
|
|
191
|
+
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}
|
|
192
|
+
|
|
193
|
+
## Chapter Information
|
|
194
|
+
|
|
195
|
+
- **Chapter ID**: {evaluation.get('chapter_id', 'N/A')}
|
|
196
|
+
- **Title**: {evaluation.get('chapter_title', 'N/A')}
|
|
197
|
+
- **Target word count**: {evaluation.get('target_word_count', 'N/A')}
|
|
198
|
+
- **Actual word count**: {evaluation.get('actual_word_count', 'N/A')}
|
|
199
|
+
|
|
200
|
+
## Overall Assessment
|
|
201
|
+
|
|
202
|
+
- **Total Score**: {score_result['total_score']}/{score_result['max_score']} ({score_result['percentage']}%)
|
|
203
|
+
- **Quality Level**: {score_result['quality_level']}
|
|
204
|
+
- **Passes Threshold**: {'✓ YES' if score_result['passes_threshold'] else '✗ NO (16/20 required)'}
|
|
205
|
+
|
|
206
|
+
## Dimension Scores
|
|
207
|
+
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
# Add dimension breakdown
|
|
211
|
+
scores = evaluation.get("evaluation", {})
|
|
212
|
+
dimensions = [
|
|
213
|
+
("argument_quality", "Argument Quality"),
|
|
214
|
+
("citation_quality", "Citation Quality"),
|
|
215
|
+
("clarity_readability", "Clarity & Readability"),
|
|
216
|
+
("structure_flow", "Structure & Flow"),
|
|
217
|
+
("platform_conformity", "Platform Style Conformity")
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
for key, name in dimensions:
|
|
221
|
+
dim_data = scores.get(key, {})
|
|
222
|
+
score = dim_data.get("score", 0)
|
|
223
|
+
notes = dim_data.get("notes", "No notes")
|
|
224
|
+
|
|
225
|
+
icon = "✓" if score >= 3 else "⚠"
|
|
226
|
+
report += f"### {icon} {name}: {score}/4\n\n"
|
|
227
|
+
report += f"{notes}\n\n"
|
|
228
|
+
|
|
229
|
+
# Word count analysis
|
|
230
|
+
report += "## Word Count Analysis\n\n"
|
|
231
|
+
report += f"- **Variance**: {word_analysis['variance_percent']:.1f}% ({word_analysis['difference']:+d} words)\n"
|
|
232
|
+
report += f"- **Assessment**: {word_analysis['assessment']}\n"
|
|
233
|
+
|
|
234
|
+
if word_analysis['variance_percent'] > 10:
|
|
235
|
+
report += f"- **Note**: Word count outside recommended ±10% range\n"
|
|
236
|
+
|
|
237
|
+
report += "\n"
|
|
238
|
+
|
|
239
|
+
# Weak dimensions
|
|
240
|
+
if weak_dims:
|
|
241
|
+
report += "## Weak Dimensions (Score <3)\n\n"
|
|
242
|
+
for dim in weak_dims:
|
|
243
|
+
report += f"### {dim['dimension']}: {dim['score']}/4 ({dim['severity'].upper()} priority)\n\n"
|
|
244
|
+
if dim['notes']:
|
|
245
|
+
report += f"{dim['notes']}\n\n"
|
|
246
|
+
|
|
247
|
+
# Recommendations
|
|
248
|
+
if recommendations:
|
|
249
|
+
report += "## Revision Recommendations\n\n"
|
|
250
|
+
report += "Prioritized list of recommended revisions:\n\n"
|
|
251
|
+
|
|
252
|
+
for i, rec in enumerate(recommendations, 1):
|
|
253
|
+
priority_icon = {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(rec['priority'], "⚪")
|
|
254
|
+
report += f"{i}. {priority_icon} **{rec['category'].title()}** ({rec['priority'].upper()} priority)\n"
|
|
255
|
+
report += f" - Issue: {rec['issue']}\n"
|
|
256
|
+
report += f" - Action: {rec['action']}\n\n"
|
|
257
|
+
|
|
258
|
+
# Decision
|
|
259
|
+
report += "## Decision\n\n"
|
|
260
|
+
|
|
261
|
+
if score_result['passes_threshold']:
|
|
262
|
+
report += "✓ **PROCEED**: Chapter meets quality threshold. Proceed to next chapter.\n\n"
|
|
263
|
+
if weak_dims or word_analysis['variance_percent'] > 10:
|
|
264
|
+
report += "**Optional improvements**:\n"
|
|
265
|
+
report += "- Consider minor revisions to address noted issues\n"
|
|
266
|
+
report += "- Not required before proceeding\n"
|
|
267
|
+
else:
|
|
268
|
+
report += "✗ **REVISE**: Chapter below quality threshold. Revise before proceeding.\n\n"
|
|
269
|
+
report += "**Required actions**:\n"
|
|
270
|
+
report += "1. Address all HIGH priority recommendations\n"
|
|
271
|
+
report += "2. Re-evaluate chapter after revisions\n"
|
|
272
|
+
report += "3. Verify score ≥16/20 before proceeding to next chapter\n\n"
|
|
273
|
+
report += "**Why this matters**: Each chapter builds on previous ones. Low-quality chapters create cascading problems.\n"
|
|
274
|
+
|
|
275
|
+
return report
|
|
276
|
+
|
|
277
|
+
def validate_evaluation(evaluation: Dict) -> Dict:
|
|
278
|
+
"""Validate that evaluation is complete and properly formatted."""
|
|
279
|
+
issues = []
|
|
280
|
+
warnings = []
|
|
281
|
+
|
|
282
|
+
# Check required fields
|
|
283
|
+
required_fields = ["chapter_id", "chapter_title", "target_word_count", "actual_word_count"]
|
|
284
|
+
for field in required_fields:
|
|
285
|
+
if not evaluation.get(field):
|
|
286
|
+
issues.append(f"Missing required field: {field}")
|
|
287
|
+
|
|
288
|
+
# Check scores present
|
|
289
|
+
eval_data = evaluation.get("evaluation", {})
|
|
290
|
+
dimensions = ["argument_quality", "citation_quality", "clarity_readability",
|
|
291
|
+
"structure_flow", "platform_conformity"]
|
|
292
|
+
|
|
293
|
+
for dim in dimensions:
|
|
294
|
+
if dim not in eval_data:
|
|
295
|
+
issues.append(f"Missing dimension: {dim}")
|
|
296
|
+
else:
|
|
297
|
+
score = eval_data[dim].get("score", 0)
|
|
298
|
+
if not isinstance(score, int) or score < 1 or score > 4:
|
|
299
|
+
issues.append(f"Invalid score for {dim}: {score} (must be 1-4)")
|
|
300
|
+
|
|
301
|
+
if not eval_data[dim].get("notes"):
|
|
302
|
+
warnings.append(f"No notes provided for {dim} (recommended)")
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
"valid": len(issues) == 0,
|
|
306
|
+
"issues": issues,
|
|
307
|
+
"warnings": warnings
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
def compare_chapters(chapters: List[Dict]) -> str:
|
|
311
|
+
"""Generate comparison report across multiple chapters."""
|
|
312
|
+
if not chapters:
|
|
313
|
+
return "No chapters to compare."
|
|
314
|
+
|
|
315
|
+
report = f"""
|
|
316
|
+
# Multi-Chapter Comparison Report
|
|
317
|
+
|
|
318
|
+
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}
|
|
319
|
+
|
|
320
|
+
## Chapter Score Summary
|
|
321
|
+
|
|
322
|
+
| Chapter | Title | Score | % | Quality | Pass |
|
|
323
|
+
|---------|-------|-------|---|---------|------|
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
total_scores = []
|
|
327
|
+
|
|
328
|
+
for ch in chapters:
|
|
329
|
+
score_result = calculate_chapter_score(ch)
|
|
330
|
+
total_scores.append(score_result['total_score'])
|
|
331
|
+
|
|
332
|
+
pass_icon = "✓" if score_result['passes_threshold'] else "✗"
|
|
333
|
+
report += f"| {ch.get('chapter_id', 'N/A')} | {ch.get('chapter_title', 'N/A')[:30]}... | {score_result['total_score']}/20 | {score_result['percentage']:.1f}% | {score_result['quality_level']} | {pass_icon} |\n"
|
|
334
|
+
|
|
335
|
+
# Statistics
|
|
336
|
+
avg_score = sum(total_scores) / len(total_scores) if total_scores else 0
|
|
337
|
+
min_score = min(total_scores) if total_scores else 0
|
|
338
|
+
max_score = max(total_scores) if total_scores else 0
|
|
339
|
+
passing = sum(1 for s in total_scores if s >= 16)
|
|
340
|
+
|
|
341
|
+
report += f"""
|
|
342
|
+
|
|
343
|
+
## Statistics
|
|
344
|
+
|
|
345
|
+
- **Average score**: {avg_score:.1f}/20 ({(avg_score/20)*100:.1f}%)
|
|
346
|
+
- **Range**: {min_score}-{max_score}
|
|
347
|
+
- **Passing chapters**: {passing}/{len(chapters)}
|
|
348
|
+
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
# Dimension comparison
|
|
352
|
+
report += "## Dimension Averages\n\n"
|
|
353
|
+
|
|
354
|
+
dimensions = [
|
|
355
|
+
("argument_quality", "Argument Quality"),
|
|
356
|
+
("citation_quality", "Citation Quality"),
|
|
357
|
+
("clarity_readability", "Clarity & Readability"),
|
|
358
|
+
("structure_flow", "Structure & Flow"),
|
|
359
|
+
("platform_conformity", "Platform Style Conformity")
|
|
360
|
+
]
|
|
361
|
+
|
|
362
|
+
for key, name in dimensions:
|
|
363
|
+
scores = [ch.get("evaluation", {}).get(key, {}).get("score", 0) for ch in chapters]
|
|
364
|
+
avg = sum(scores) / len(scores) if scores else 0
|
|
365
|
+
report += f"- **{name}**: {avg:.2f}/4\n"
|
|
366
|
+
|
|
367
|
+
report += "\n## Overall Assessment\n\n"
|
|
368
|
+
|
|
369
|
+
if passing == len(chapters):
|
|
370
|
+
report += "✓ All chapters meet quality threshold. Ready for final quality assessment.\n"
|
|
371
|
+
else:
|
|
372
|
+
report += f"⚠ {len(chapters) - passing} chapter(s) below threshold. Revise before proceeding to final assessment.\n"
|
|
373
|
+
|
|
374
|
+
return report
|
|
375
|
+
|
|
376
|
+
def main():
|
|
377
|
+
"""Interactive chapter quality check interface."""
|
|
378
|
+
print("=" * 60)
|
|
379
|
+
print("Chapter Quality Check Tool")
|
|
380
|
+
print("=" * 60)
|
|
381
|
+
print()
|
|
382
|
+
|
|
383
|
+
print("Options:")
|
|
384
|
+
print("1. Create evaluation template")
|
|
385
|
+
print("2. Evaluate chapter from JSON")
|
|
386
|
+
print("3. Compare multiple chapters")
|
|
387
|
+
print()
|
|
388
|
+
|
|
389
|
+
choice = input("Select option (1-3): ").strip()
|
|
390
|
+
|
|
391
|
+
if choice == "1":
|
|
392
|
+
# Create template
|
|
393
|
+
chapter_id = input("Chapter ID (e.g., 'chapter_2'): ").strip()
|
|
394
|
+
chapter_title = input("Chapter title: ").strip()
|
|
395
|
+
target_wc = int(input("Target word count: ").strip())
|
|
396
|
+
|
|
397
|
+
template = create_chapter_evaluation_template()
|
|
398
|
+
template["chapter_id"] = chapter_id
|
|
399
|
+
template["chapter_title"] = chapter_title
|
|
400
|
+
template["target_word_count"] = target_wc
|
|
401
|
+
|
|
402
|
+
filename = f"{chapter_id}_evaluation.json"
|
|
403
|
+
with open(filename, 'w') as f:
|
|
404
|
+
json.dump(template, f, indent=2)
|
|
405
|
+
|
|
406
|
+
print(f"\nTemplate saved to {filename}")
|
|
407
|
+
print("Fill in the evaluation scores and notes, then use option 2 to generate report.")
|
|
408
|
+
|
|
409
|
+
elif choice == "2":
|
|
410
|
+
# Evaluate chapter
|
|
411
|
+
filename = input("Enter evaluation JSON filename: ").strip()
|
|
412
|
+
try:
|
|
413
|
+
with open(filename, 'r') as f:
|
|
414
|
+
evaluation = json.load(f)
|
|
415
|
+
|
|
416
|
+
# Validate
|
|
417
|
+
validation = validate_evaluation(evaluation)
|
|
418
|
+
if not validation['valid']:
|
|
419
|
+
print("\n⚠ Validation Issues:")
|
|
420
|
+
for issue in validation['issues']:
|
|
421
|
+
print(f" - {issue}")
|
|
422
|
+
print("\nPlease fix issues before generating report.")
|
|
423
|
+
return
|
|
424
|
+
|
|
425
|
+
if validation['warnings']:
|
|
426
|
+
print("\nℹ️ Warnings:")
|
|
427
|
+
for warning in validation['warnings']:
|
|
428
|
+
print(f" - {warning}")
|
|
429
|
+
print()
|
|
430
|
+
|
|
431
|
+
# Generate report
|
|
432
|
+
report = generate_chapter_report(evaluation)
|
|
433
|
+
print("\n" + report)
|
|
434
|
+
|
|
435
|
+
# Save report
|
|
436
|
+
report_filename = filename.replace('.json', '_report.md')
|
|
437
|
+
with open(report_filename, 'w') as f:
|
|
438
|
+
f.write(report)
|
|
439
|
+
print(f"\nReport saved to {report_filename}")
|
|
440
|
+
|
|
441
|
+
except FileNotFoundError:
|
|
442
|
+
print(f"Error: File '{filename}' not found")
|
|
443
|
+
except json.JSONDecodeError:
|
|
444
|
+
print(f"Error: '{filename}' is not valid JSON")
|
|
445
|
+
|
|
446
|
+
elif choice == "3":
|
|
447
|
+
# Compare chapters
|
|
448
|
+
filename = input("Enter JSON filename with list of chapter evaluations: ").strip()
|
|
449
|
+
try:
|
|
450
|
+
with open(filename, 'r') as f:
|
|
451
|
+
chapters = json.load(f)
|
|
452
|
+
|
|
453
|
+
report = compare_chapters(chapters)
|
|
454
|
+
print("\n" + report)
|
|
455
|
+
|
|
456
|
+
report_filename = filename.replace('.json', '_comparison_report.md')
|
|
457
|
+
with open(report_filename, 'w') as f:
|
|
458
|
+
f.write(report)
|
|
459
|
+
print(f"\nComparison report saved to {report_filename}")
|
|
460
|
+
|
|
461
|
+
except FileNotFoundError:
|
|
462
|
+
print(f"Error: File '{filename}' not found")
|
|
463
|
+
except json.JSONDecodeError:
|
|
464
|
+
print(f"Error: '{filename}' is not valid JSON")
|
|
465
|
+
|
|
466
|
+
else:
|
|
467
|
+
print("Invalid option")
|
|
468
|
+
|
|
469
|
+
if __name__ == "__main__":
|
|
470
|
+
main()
|