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:
58
.gitignore
vendored
Normal file
58
.gitignore
vendored
Normal 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
159
README.md
Normal 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).
|
||||||
32
research-add-fields/SKILL.md
Normal file
32
research-add-fields/SKILL.md
Normal 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)
|
||||||
30
research-add-items/SKILL.md
Normal file
30
research-add-items/SKILL.md
Normal 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
100
research-deep/SKILL.md
Normal 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
93
research-report/SKILL.md
Normal 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
145
research/SKILL.md
Normal 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
160
research/validate_json.py
Normal 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()
|
||||||
Reference in New Issue
Block a user