Initial commit — research-en skill bundle

5-skill bundle for structured, resumable, parallel deep research:
- /research              — preliminary outline + fields generation
- /research-add-items    — supplement research objects
- /research-add-fields   — supplement field definitions
- /research-deep         — parallel agents per item, resumable, JSON-validated
- /research-report       — JSON to markdown report with TOC

Includes validate_json.py for fields.yaml coverage check (PyYAML required).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 11:09:40 +03:00
commit c284fb12b0
8 changed files with 777 additions and 0 deletions

58
.gitignore vendored Normal file
View File

@@ -0,0 +1,58 @@
# Python build artifacts
__pycache__/
*.py[cod]
*$py.class
*.egg-info/
*.egg
.eggs/
build/
dist/
# Virtual environments
.venv/
venv/
env/
ENV/
.python-version
# Coverage / testing
.coverage
.coverage.*
htmlcov/
.tox/
.nox/
.pytest_cache/
.mypy_cache/
.ruff_cache/
.dmypy.json
# IDE / OS
.idea/
.vscode/
!.vscode/settings.json
!.vscode/launch.json
!.vscode/extensions.json
*.swp
*.swo
*~
.DS_Store
Thumbs.db
desktop.ini
# Jupyter
.ipynb_checkpoints/
# Secrets — never commit
.env
.env.local
*.key
*.pem
credentials.json
# Temp
*.log
*.tmp
*.bak
*.backup
*.orig
~$*

159
README.md Normal file
View File

@@ -0,0 +1,159 @@
# research-en
Claude Code skill bundle for **structured, resumable, parallel deep research**. Five user-invocable slash commands take a topic from a back-of-the-envelope idea to a finished Markdown report, with web search, per-item parallel agents, JSON validation, and field-by-field coverage checks.
English variant. The bundle ships five skills that share a single project directory layout (`./{topic_slug}/`) and YAML/JSON contract.
## Pipeline
```
/research <topic> → outline.yaml + fields.yaml
/research-add-items (optional, supplement items)
/research-add-fields (optional, supplement fields)
/research-deep → results/*.json (one per item, parallel agents)
/research-report → generate_report.py + report.md
```
## Skills
| Skill | Trigger | What it does |
|-------|---------|--------------|
| `research` | `/research <topic>` | Step 1 — generate initial item list + field framework from model knowledge; Step 2 — launch 1 background web-search-agent to supplement; Step 3 — merge user's existing fields if any; Step 4 — write `outline.yaml` (items + execution config) and `fields.yaml` (field defs with `detail_level: brief|moderate|detailed`). |
| `research-add-items` | `/research-add-items` | Append new research objects to `outline.yaml` (user input + optional web search), dedup, in-place update. |
| `research-add-fields` | `/research-add-fields` | Append new field definitions to `fields.yaml` (user input + optional web search), category + `detail_level` confirmed by user. |
| `research-deep` | `/research-deep` | Auto-locate `outline.yaml`, resume from completed JSONs, batch-launch background web-search-agents (`items_per_agent` per agent, `batch_size` parallel), each writes `{output_dir}/{item_slug}.json` per `fields.yaml`, validates with `validate_json.py`. |
| `research-report` | `/research-report` | Read all JSONs + `fields.yaml`, ask which numeric fields to surface in the TOC, generate `generate_report.py` (handles flat/nested JSON, multi-language category mapping, complex-value formatting, uncertain skipping), execute it to produce `report.md`. |
## File contracts
### `outline.yaml`
```yaml
topic: <research topic>
items:
- name: <item name>
category: <category>
description: <brief>
execution:
batch_size: <parallel agents>
items_per_agent: <items per agent>
output_dir: ./results
```
### `fields.yaml`
```yaml
field_categories:
- category: Basic Info
fields:
- name: <field>
description: <field description>
detail_level: brief | moderate | detailed
required: true | false
uncertain: [] # reserved, populated during deep phase
```
### `results/{item_slug}.json`
```json
{
"name": "...",
"release_date": "...",
"underlying_model": "[uncertain]",
"uncertain": ["underlying_model", ...]
}
```
Both flat (`{"name": ...}`) and nested (`{"basic_info": {"name": ...}}`) layouts are supported throughout.
## Validation
Every deep-research agent finishes with:
```bash
python research/validate_json.py -f <fields.yaml> -j <result.json>
```
`validate_json.py` reports per-file:
- coverage % (covered / defined fields)
- missing required (FAIL if non-empty)
- missing optional, grouped by category
- extra fields not defined in `fields.yaml`
Exit code is non-zero if any required field is missing.
## Installation
Drop this repo into your vault's Claude Code skills directory:
```bash
git clone https://git.lissad.keenetic.name/Pikaliov/research-en.git \
.claude/skills/research-en
```
Or as a submodule:
```bash
git submodule add https://git.lissad.keenetic.name/Pikaliov/research-en.git \
.claude/skills/research-en
```
The five skills are auto-discovered on the next Claude Code session. Verify with `/help` — you should see `/research`, `/research-add-items`, `/research-add-fields`, `/research-deep`, `/research-report`.
**Runtime dependency** (for `validate_json.py`):
```bash
pip install pyyaml
```
`research-deep` calls `validate_json.py` via the `~/.claude/skills/research/validate_json.py` path inside its prompt template — adjust if your install location differs.
## File layout
```
research-en/
├── README.md — this file
├── research/
│ ├── SKILL.md — /research (preliminary)
│ └── validate_json.py — JSON ↔ fields.yaml coverage check (PyYAML)
├── research-add-items/SKILL.md — /research-add-items
├── research-add-fields/SKILL.md — /research-add-fields
├── research-deep/SKILL.md — /research-deep (parallel, resumable)
└── research-report/SKILL.md — /research-report (markdown synth)
```
## Allowed tools per skill
| Skill | Tools |
|-------|-------|
| `research` | Read, Write, Glob, WebSearch, Task, AskUserQuestion |
| `research-add-items` | Bash, Read, Write, Glob, WebSearch, Task, AskUserQuestion |
| `research-add-fields` | Bash, Read, Write, Glob, WebSearch, Task, AskUserQuestion |
| `research-deep` | Bash, Read, Write, Glob, WebSearch, Task |
| `research-report` | Read, Write, Glob, Bash, AskUserQuestion |
## Worked example
```text
/research "AI Coding Assistants since 2024"
# → ./ai_coding_assistants/{outline.yaml, fields.yaml}
/research-add-items
# → adds Cursor, Windsurf, Aider, Continue, etc.
/research-deep
# → batch=4 agents, 2 items each → results/*.json
# → validate_json.py runs after each → coverage report
/research-report
# → ask: "Which fields in TOC besides name?" → github_stars, swe_bench_score
# → ./ai_coding_assistants/{generate_report.py, report.md}
```
## Hard constraints (in skill prompts)
- All field values must be in **English** (deep phase).
- Mark uncertain values with `[uncertain]`, list them in trailing `uncertain` array.
- Prompt templates inside `research/SKILL.md` and `research-deep/SKILL.md` are **hard-reproduce**: only `{xxx}` placeholders may be substituted, structure and wording must not be modified.
- Resume support: `research-deep` skips items that already have a JSON file in `output_dir`.
- Batch gating: `research-deep` waits for user approval between batches (interactive).

View File

@@ -0,0 +1,32 @@
---
name: research-add-fields
user-invocable: true
description: Add field definitions to existing research outline.
allowed-tools: Bash, Read, Write, Glob, WebSearch, Task, AskUserQuestion
---
# Research Add Fields - Supplement Research Fields
## Trigger
`/research-add-fields`
## Workflow
### Step 1: Auto-locate Fields File
Find `*/fields.yaml` file in current working directory, auto-read existing fields definitions.
### Step 2: Get Supplement Source
Ask user to choose:
- **A. User direct input**: User provides field names and descriptions
- **B. Web Search**: Launch agent to search common fields in this domain
### Step 3: Display and Confirm
- Display suggested new fields list
- User confirms which fields to add
- User specifies field category and detail_level
### Step 4: Save Update
Append confirmed fields to fields.yaml, save file.
## Output
Updated `{topic}/fields.yaml` file (in-place modification, requires user confirmation)

View File

@@ -0,0 +1,30 @@
---
name: research-add-items
user-invocable: true
description: Add items (research objects) to existing research outline.
allowed-tools: Bash, Read, Write, Glob, WebSearch, Task, AskUserQuestion
---
# Research Add Items - Supplement Research Objects
## Trigger
`/research-add-items`
## Workflow
### Step 1: Auto-locate Outline
Find `*/outline.yaml` file in current working directory, auto-read.
### Step 2: Get Supplement Sources in Parallel
Simultaneously:
- **A. Ask user**: What items to supplement? Any specific names?
- **B. Ask if Web Search needed**: Launch agent to search for more items?
### Step 3: Merge and Update
- Append new items to outline.yaml
- Display to user for confirmation
- Avoid duplicates
- Save updated outline
## Output
Updated `{topic}/outline.yaml` file (in-place modification)

100
research-deep/SKILL.md Normal file
View File

@@ -0,0 +1,100 @@
---
name: research-deep
user-invocable: true
description: Read research outline, launch independent agent for each item for deep research. Disable task output.
allowed-tools: Bash, Read, Write, Glob, WebSearch, Task
---
# Research Deep - Deep Research
## Trigger
`/research-deep`
## Workflow
### Step 1: Auto-locate Outline
Find `*/outline.yaml` file in current working directory, read items list, execution config (including items_per_agent).
### Step 2: Resume Check
- Check completed JSON files in output_dir
- Skip completed items
### Step 3: Batch Execution
- Batch by batch_size (need user approval before next batch)
- Each agent handles items_per_agent items
- Launch web-search-agent (background parallel, disable task output)
**Parameter Retrieval**:
- `{topic}`: topic field from outline.yaml
- `{item_name}`: item's name field
- `{item_related_info}`: item's complete yaml content (name + category + description etc.)
- `{output_dir}`: execution.output_dir from outline.yaml (default: ./results)
- `{fields_path}`: absolute path to {topic}/fields.yaml
- `{output_path}`: absolute path to {output_dir}/{item_name_slug}.json (slugify item_name: replace spaces with _, remove special chars)
**Hard Constraint**: The following prompt must be strictly reproduced, only replacing variables in {xxx}, do not modify structure or wording.
**Prompt Template**:
```python
prompt = f"""## Task
Research {item_related_info}, output structured JSON to {output_path}
## Field Definitions
Read {fields_path} to get all field definitions
## Output Requirements
1. Output JSON according to fields defined in fields.yaml
2. Mark uncertain field values with [uncertain]
3. Add uncertain array at the end of JSON, listing all uncertain field names
4. All field values must be in English
## Output Path
{output_path}
## Validation
After completing JSON output, run validation script to ensure complete field coverage:
python ~/.claude/skills/research/validate_json.py -f {fields_path} -j {output_path}
Task is complete only after validation passes.
"""
```
**One-shot Example** (assuming researching GitHub Copilot):
```
## Task
Research name: GitHub Copilot
category: International Product
description: Developed by Microsoft/GitHub, first mainstream AI coding assistant, ~40% market share, output structured JSON to {project_dir}/results/GitHub_Copilot.json
## Field Definitions
Read {project_dir}/fields.yaml to get all field definitions
## Output Requirements
1. Output JSON according to fields defined in fields.yaml
2. Mark uncertain field values with [uncertain]
3. Add uncertain array at the end of JSON, listing all uncertain field names
4. All field values must be in English
## Output Path
{project_dir}/results/GitHub_Copilot.json
## Validation
After completing JSON output, run validation script to ensure complete field coverage:
python ~/.claude/skills/research/validate_json.py -f {project_dir}/fields.yaml -j {project_dir}/results/GitHub_Copilot.json
Task is complete only after validation passes.
```
### Step 4: Wait and Monitor
- Wait for current batch to complete
- Launch next batch
- Display progress
### Step 5: Summary Report
After all complete, output:
- Completion count
- Failed/uncertain marked items
- Output directory
## Agent Config
- Background execution: Yes
- Task Output: Disabled (agent has explicit output file when complete)
- Resume support: Yes

93
research-report/SKILL.md Normal file
View File

@@ -0,0 +1,93 @@
---
name: research-report
user-invocable: true
description: Summarize deep research results into markdown report, cover all fields, skip uncertain values.
allowed-tools: Read, Write, Glob, Bash, AskUserQuestion
---
# Research Report - Summary Report
## Trigger
`/research-report`
## Workflow
### Step 1: Locate Results Directory
Find `*/outline.yaml` in current working directory, read topic and output_dir config.
### Step 2: Scan Optional Summary Fields
Read all JSON results, extract fields suitable for TOC display (numeric, short metrics), e.g.:
- github_stars
- google_scholar_cites
- swe_bench_score
- user_scale
- valuation
- release_date
Use AskUserQuestion to ask user:
- Which fields to display in TOC besides item name?
- Provide dynamic options list (based on actual fields in JSON)
### Step 3: Generate Python Conversion Script
Generate `generate_report.py` in `{topic}/` directory, script requirements:
- Read all JSON from output_dir
- Read fields.yaml to get field structure
- Cover all field values from each JSON
- Skip fields with values containing [uncertain]
- Skip fields listed in uncertain array
- Generate markdown report format: Table of contents (with anchor links + user-selected summary fields) + Detailed content (by field category)
- Save to `{topic}/report.md`
**TOC Format Requirements**:
- Must include every item
- Each item displays: number, name (anchor link), user-selected summary fields
- Example: `1. [GitHub Copilot](#github-copilot) - Stars: 10k | Score: 85%`
#### Script Technical Requirements (Must Follow)
**1. JSON Structure Compatibility**
Support two JSON structures:
- Flat structure: Fields directly at top level `{"name": "xxx", "release_date": "xxx"}`
- Nested structure: Fields in category sub-dict `{"basic_info": {"name": "xxx"}, "technical_features": {...}}`
Field lookup order: Top level -> category mapping key -> Traverse all nested dicts
**2. Category Multi-language Mapping**
fields.yaml category names and JSON keys can be any combination (CN-CN, CN-EN, EN-CN, EN-EN). Must establish bidirectional mapping:
```python
CATEGORY_MAPPING = {
"Basic Info": ["basic_info", "Basic Info"],
"Technical Features": ["technical_features", "technical_characteristics", "Technical Features"],
"Performance Metrics": ["performance_metrics", "performance", "Performance Metrics"],
"Milestone Significance": ["milestone_significance", "milestones", "Milestone Significance"],
"Business Info": ["business_info", "commercial_info", "Business Info"],
"Competition & Ecosystem": ["competition_ecosystem", "competition", "Competition & Ecosystem"],
"History": ["history", "History"],
"Market Positioning": ["market_positioning", "market", "Market Positioning"],
}
```
**3. Complex Value Formatting**
- list of dicts (e.g., key_events, funding_history): Format each dict as one line, separate kv with ` | `
- Normal list: Short lists joined with comma, long lists displayed with line breaks
- Nested dict: Recursive formatting, display with semicolon or line breaks
- Long text strings (over 100 chars): Add line breaks `<br>` or use blockquote format for readability
**4. Extra Fields Collection**
Collect fields that exist in JSON but not defined in fields.yaml, put in "Other Info" category. Note to filter:
- Internal fields: `_source_file`, `uncertain`
- Nested structure top-level keys: `basic_info`, `technical_features` etc.
- `uncertain` array: Display each field name on separate line, don't compress into one line
**5. Uncertain Value Skipping**
Skip conditions:
- Field value contains `[uncertain]` string
- Field name is in `uncertain` array
- Field value is None or empty string
### Step 4: Execute Script
Run `python {topic}/generate_report.py`
## Output
- `{topic}/generate_report.py` - Conversion script
- `{topic}/report.md` - Summary report

145
research/SKILL.md Normal file
View File

@@ -0,0 +1,145 @@
---
name: research
user-invocable: true
allowed-tools: Read, Write, Glob, WebSearch, Task, AskUserQuestion
description: Conduct preliminary research on a topic and generate research outline. For academic research, benchmark research, technology selection, etc.
---
# Research Skill - Preliminary Research
## Trigger
`/research <topic>`
## Workflow
### Step 1: Generate Initial Framework from Model Knowledge
Based on topic, use model's existing knowledge to generate:
- Main research objects/items list in this domain
- Suggested research field framework
Output {step1_output}, use AskUserQuestion to confirm:
- Need to add/remove items?
- Does field framework meet requirements?
### Step 2: Web Search Supplement
Use AskUserQuestion to ask for time range (e.g., last 6 months, since 2024, unlimited).
**Parameter Retrieval**:
- `{topic}`: User input research topic
- `{YYYY-MM-DD}`: Current date
- `{step1_output}`: Complete output from Step 1
- `{time_range}`: User specified time range
**Hard Constraint**: The following prompt must be strictly reproduced, only replacing variables in {xxx}, do not modify structure or wording.
Launch 1 web-search-agent (background), **Prompt Template**:
```python
prompt = f"""## Task
Research topic: {topic}
Current date: {YYYY-MM-DD}
Based on the following initial framework, supplement latest items and recommended research fields.
## Existing Framework
{step1_output}
## Goals
1. Verify if existing items are missing important objects
2. Supplement items based on missing objects
3. Continue searching for {topic} related items within {time_range} and supplement
4. Supplement new fields
## Output Requirements
Return structured results directly (do not write files):
### Supplementary Items
- item_name: Brief explanation (why it should be added)
...
### Recommended Supplementary Fields
- field_name: Field description (why this dimension is needed)
...
### Sources
- [Source1](url1)
- [Source2](url2)
"""
```
**One-shot Example** (assuming researching AI Coding History):
```
## Task
Research topic: AI Coding History
Current date: 2025-12-30
Based on the following initial framework, supplement latest items and recommended research fields.
## Existing Framework
### Items List
1. GitHub Copilot: Developed by Microsoft/GitHub, first mainstream AI coding assistant
2. Cursor: AI-first IDE, based on VSCode
...
### Field Framework
- Basic Info: name, release_date, company
- Technical Features: underlying_model, context_window
...
## Goals
1. Verify if existing items are missing important objects
2. Supplement items based on missing objects
3. Continue searching for AI Coding History related items within since 2024 and supplement
4. Supplement new fields
## Output Requirements
Return structured results directly (do not write files):
### Supplementary Items
- item_name: Brief explanation (why it should be added)
...
### Recommended Supplementary Fields
- field_name: Field description (why this dimension is needed)
...
### Sources
- [Source1](url1)
- [Source2](url2)
```
### Step 3: Ask User for Existing Fields
Use AskUserQuestion to ask if user has existing field definition file, if so read and merge.
### Step 4: Generate Outline (Separate Files)
Merge {step1_output}, {step2_output} and user's existing fields, generate two files:
**outline.yaml** (items + config):
- topic: Research topic
- items: Research objects list
- execution:
- batch_size: Number of parallel agents (confirm with AskUserQuestion)
- items_per_agent: Items per agent (confirm with AskUserQuestion)
- output_dir: Results output directory (default: ./results)
**fields.yaml** (field definitions):
- Field categories and definitions
- Each field's name, description, detail_level
- detail_level hierarchy: brief -> moderate -> detailed
- uncertain: Uncertain fields list (reserved field, auto-filled in deep phase)
### Step 5: Output and Confirm
- Create directory: `./{topic_slug}/`
- Save: `outline.yaml` and `fields.yaml`
- Show to user for confirmation
## Output Path
```
{current_working_directory}/{topic_slug}/
├── outline.yaml # items list + execution config
└── fields.yaml # field definitions
```
## Follow-up Commands
- `/research-add-items` - Supplement items
- `/research-add-fields` - Supplement fields
- `/research-deep` - Start deep research

160
research/validate_json.py Normal file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import sys
from collections import defaultdict
from pathlib import Path
import yaml
CATEGORY_MAPPING = {
"basic_info": ["basic_info", "Basic Info"],
"technical_features": ["technical_features", "technical_characteristics", "Technical Features"],
"performance_metrics": ["performance_metrics", "performance", "Performance Metrics"],
"milestone_significance": ["milestone_significance", "milestones", "Milestone Significance"],
"business_info": ["business_info", "commercial_info", "Business Info"],
"competition_ecosystem": ["competition_ecosystem", "competition", "Competition Ecosystem"],
"history": ["history", "History"],
"market_positioning": ["market_positioning", "market", "Market Positioning"],
}
_SKIP_KEYS = {"_source_file", "uncertain"}
def load_fields_yaml(fields_path):
with fields_path.open(encoding="utf-8") as f:
data = yaml.safe_load(f)
items = [
(field["name"], category["category"], field.get("required", False))
for category in data.get("field_categories", [])
for field in category.get("fields", [])
]
all_fields = {name for name, _, _ in items}
required_fields = {name for name, _, required in items if required}
field_categories = {name: category for name, category, _ in items}
return all_fields, required_fields, field_categories
def extract_json_fields(data, category_mapping=None):
category_mapping = CATEGORY_MAPPING if category_mapping is None else category_mapping
nested_keys = {k for keys in category_mapping.values() for k in keys}
fields = set()
stack = [(data, True)]
while stack:
obj, is_category_level = stack.pop()
if isinstance(obj, dict):
for k, v in obj.items():
if k in _SKIP_KEYS:
continue
if is_category_level and k in nested_keys:
if isinstance(v, dict):
stack.append((v, True))
continue
fields.add(k)
elif isinstance(obj, list):
stack.extend((item, is_category_level) for item in obj if isinstance(item, dict))
return fields
def validate_json(json_path, all_fields, required_fields, field_categories):
with json_path.open(encoding="utf-8") as f:
data = json.load(f)
json_fields = extract_json_fields(data)
covered = all_fields & json_fields
missing = all_fields - json_fields
extra = json_fields - all_fields
missing_required = missing & required_fields
missing_by_category = defaultdict(list)
for field in missing:
missing_by_category[field_categories.get(field, "Unknown")].append(field)
return {
"file": json_path.name,
"total_defined": len(all_fields),
"covered": len(covered),
"missing": len(missing),
"extra": len(extra),
"coverage_rate": len(covered) / len(all_fields) * 100 if all_fields else 100,
"missing_required": sorted(missing_required),
"missing_optional": sorted(missing - required_fields),
"missing_by_category": {k: sorted(v) for k, v in missing_by_category.items()},
"extra_fields": sorted(extra),
"valid": len(missing_required) == 0,
}
def print_result(result, verbose=True):
status = "PASS" if result["valid"] else "FAIL"
line = "=" * 60
print(f"\n{line}")
print(f"[{status}] {result['file']}")
print(line)
print(f"Coverage: {result['coverage_rate']:.1f}% ({result['covered']}/{result['total_defined']})")
if result["missing_required"]:
print(f"\n[ERROR] Missing required fields ({len(result['missing_required'])}):")
print("\n".join(f" - {f}" for f in result["missing_required"]))
if verbose and result["missing_optional"]:
missing_required = set(result["missing_required"])
print(f"\n[WARN] Missing optional fields ({len(result['missing_optional'])}):")
for cat in sorted(result["missing_by_category"]):
optional = [f for f in result["missing_by_category"][cat] if f not in missing_required]
if optional:
print(f" [{cat}]: {', '.join(optional)}")
if verbose and result["extra_fields"]:
extra = result["extra_fields"]
print(f"\n[INFO] Extra fields ({len(extra)}):")
print(f" {', '.join(extra[:10])}")
if len(extra) > 10:
print(f" ... and {len(extra) - 10} more")
def main():
import argparse
parser = argparse.ArgumentParser(description="Validate whether JSON files cover all fields defined in fields.yaml")
parser.add_argument("--fields", "-f", type=str, help="Path to fields.yaml", default="fields.yaml")
parser.add_argument("--json", "-j", type=str, nargs="*", help="JSON file paths to validate")
parser.add_argument("--dir", "-d", type=str, help="Directory containing JSON files", default="results")
parser.add_argument("--quiet", "-q", action="store_true", help="Show summary only")
args = parser.parse_args()
fields_path = Path(args.fields)
if not fields_path.exists():
for p in (Path.cwd() / "fields.yaml", Path.cwd().parent / "fields.yaml"):
if p.exists():
fields_path = p
break
if not fields_path.exists():
print(f"[ERROR] fields.yaml not found: {fields_path}")
sys.exit(1)
print(f"Field definition file: {fields_path}")
all_fields, required_fields, field_categories = load_fields_yaml(fields_path)
print(f"Total fields: {len(all_fields)} (required: {len(required_fields)}, optional: {len(all_fields) - len(required_fields)})")
json_files = (
[Path(p) for p in args.json]
if args.json
else sorted(Path(args.dir).glob("*.json")) if Path(args.dir).exists() else []
)
if not json_files:
print("[WARN] No JSON files found")
sys.exit(0)
results = []
for json_path in json_files:
if not json_path.exists():
print(f"[WARN] File not found: {json_path}")
continue
result = validate_json(json_path, all_fields, required_fields, field_categories)
results.append(result)
print_result(result, verbose=not args.quiet)
line = "=" * 60
print(f"\n{line}")
print("Summary")
print(line)
passed = sum(1 for r in results if r["valid"])
avg_coverage = sum(r["coverage_rate"] for r in results) / len(results) if results else 0
print(f"Validation passed: {passed}/{len(results)}")
print(f"Average coverage: {avg_coverage:.1f}%")
if passed < len(results):
sys.exit(1)
if __name__ == "__main__":
main()