
Explore
- 40 installs
- 213 repo stars
- Updated August 4, 2026
- yonatangross/skillforge-claude-plugin
Helps with ai & agent building tasks.
About
explore is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- explore
- AI & Agent Building
- AI-coding skill
Explore by the numbers
- 40 all-time installs (skills.sh)
- Ranked #8,266 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yonatangross/skillforge-claude-plugin --skill exploreAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 40 |
|---|---|
| repo stars | ★ 213 |
| Last updated | August 4, 2026 |
| Repository | yonatangross/skillforge-claude-plugin ↗ |
What it does
Helps with ai & agent building tasks.
Files
Codebase Exploration
Multi-angle codebase exploration using 3-5 parallel agents.
🎯 Quick Start
/ork:explore authenticationOpus 4.8: Exploration agents use native adaptive thinking for deeper pattern recognition across large codebases.
---
STEP -0.5: Effort-Aware Agent Scaling (CC 2.1.120+)
Read ${CLAUDE_EFFORT} to scale exploration depth before any other decision.
# CC 2.1.120+ env var; explicit --effort= overrides
EFFORT = os.environ.get("CLAUDE_EFFORT")
for token in "$ARGUMENTS".split():
if token.startswith("--effort="):
EFFORT = token.split("=", 1)[1]
EFFORT = EFFORT or "high" # default| Effort | Agent count | Phases | Time |
|---|---|---|---|
low | 1 (structure-only) | 1, 2, 8 | ~1 min |
medium | 2 (structure + data flow) | 1, 2, 3 (subset), 8 | ~3 min |
high (default) | 4 (full parallel team) | 1–8 | ~6 min |
xhigh (Opus 4.8) | 5 (+ uncertainty pass on health scores) | 1–8 + caveats | ~8 min |
Override gate: if the user passes --effort=high explicitly while ${CLAUDE_EFFORT} is low, the flag wins. /ork:doctor warns when xhigh is requested without Opus 4.8.
---
STEP 0: Verify User Intent with AskUserQuestion
BEFORE creating tasks, clarify what the user wants to explore:
AskUserQuestion(
questions=[{
"question": "What aspect do you want to explore?",
"header": "Focus",
"options": [
{"label": "Full exploration (Recommended)", "description": "Code structure + data flow + architecture + health assessment"},
{"label": "Quick scan", "description": "Find relevant files + structure, skip deep analysis"},
{"label": "Data flow", "description": "Trace how data moves through the system"},
{"label": "Architecture patterns", "description": "Identify design patterns and integrations"}
],
"multiSelect": false
}]
)Based on answer, adjust workflow:
- Full exploration: All phases, all parallel agents
- Quick scan: Files + structure only (phases 1-2), skip health/deps/product — no deep agents
- Data flow: Focus phase 3 agents on data tracing
- Architecture patterns: Focus on backend-system-architect agent
---
STEP 0b: Select Orchestration Mode
MCP Probe
# memory is alwaysLoad in .mcp.json (CC 2.1.121+, #1541) — probe below kept as fallback for older CC:
ToolSearch(query="select:mcp__memory__search_nodes")
Write(".claude/chain/capabilities.json", { memory, timestamp })
if capabilities.memory:
mcp__memory__search_nodes({ query: "architecture decisions for {path}" })
# Enrich exploration with past decisionsExploration Handoff
After exploration completes, write results for downstream skills:
Write(".claude/chain/exploration.json", JSON.stringify({
"phase": "explore", "skill": "explore",
"timestamp": now(), "status": "completed",
"outputs": {
"architecture_map": { ... },
"patterns_found": ["repository", "service-layer"],
"complexity_hotspots": ["src/auth/", "src/payments/"]
}
}))---
Choose Agent Teams (mesh) or Task tool (star):
1. Agent Teams mode (GA since CC 2.1.33) → recommended for 4+ agents 2. Task tool mode → for quick/single-focus exploration 3. ORCHESTKIT_FORCE_TASK_TOOL=1 → Task tool (override)
| Aspect | Task Tool | Agent Teams |
|---|---|---|
| Discovery sharing | Lead synthesizes after all complete | Explorers share discoveries as they go |
| Cross-referencing | Lead connects dots | Data flow explorer alerts architecture explorer |
| Cost | ~150K tokens | ~400K tokens |
| Best for | Quick/focused searches | Deep full-codebase exploration |
Fallback: If Agent Teams encounters issues, fall back to Task tool for remaining exploration.
---
🚨 Task Management (MANDATORY)
BEFORE doing ANYTHING else, create tasks to show progress:
# 1. Create main task IMMEDIATELY
TaskCreate(subject="Explore: {topic}", description="Deep codebase exploration for {topic}", activeForm="Exploring {topic}")
# 2. Create subtasks for each phase
TaskCreate(subject="Initial file search", activeForm="Searching files") # id=2
TaskCreate(subject="Check knowledge graph", activeForm="Checking memory") # id=3
TaskCreate(subject="Launch exploration agents", activeForm="Dispatching explorers") # id=4
TaskCreate(subject="Assess code health (0-10)", activeForm="Assessing code health") # id=5
TaskCreate(subject="Map dependency hotspots", activeForm="Mapping dependencies") # id=6
TaskCreate(subject="Add product perspective", activeForm="Adding product context") # id=7
TaskCreate(subject="Generate exploration report", activeForm="Generating report") # id=8
# 3. Set dependencies for sequential phases
TaskUpdate(taskId="3", addBlockedBy=["2"]) # Memory check needs file search first
TaskUpdate(taskId="4", addBlockedBy=["3"]) # Agents need memory context
TaskUpdate(taskId="5", addBlockedBy=["4"]) # Health needs exploration done
TaskUpdate(taskId="6", addBlockedBy=["4"]) # Hotspots need exploration done
TaskUpdate(taskId="7", addBlockedBy=["4"]) # Product needs exploration done
TaskUpdate(taskId="8", addBlockedBy=["5", "6", "7"]) # Report needs all analysis done
# 4. Before starting each task, verify it's unblocked
task = TaskGet(taskId="2") # Verify blockedBy is empty
# 5. Update status as you progress
TaskUpdate(taskId="2", status="in_progress") # When starting
TaskUpdate(taskId="2", status="completed") # When done — repeat for each subtask---
🔄 Workflow Overview
| Phase | Activities | Output |
|---|---|---|
| 1. Initial Search | Grep, Glob for matches | File locations |
| 2. Memory Check | Search knowledge graph | Prior context |
| 3. Deep Exploration | 4 parallel explorers | Multi-angle analysis |
| 4. AI System (if applicable) | LangGraph, prompts, RAG | AI-specific findings |
| 5. Code Health | Rate code 0-10 | Quality scores |
| 6. Dependency Hotspots | Identify coupling | Hotspot visualization |
| 7. Product Perspective | Business context | Findability suggestions |
| 8. Report Generation | Compile findings | Actionable report |
Progressive Output (CC 2.1.76)
Output findings incrementally as each phase completes — don't batch until the report:
| After Phase | Show User |
|---|---|
| 1. Initial Search | File matches, grep results |
| 2. Memory Check | Prior decisions and relevant context |
| 3. Deep Exploration | Each explorer agent's findings as they return |
| 5. Code Health | Health score with dimension breakdown |
For Phase 3 parallel agents, output each agent's findings as soon as it returns — don't wait for all 4 explorers. Early findings from one agent may answer the user's question before remaining agents complete, allowing early termination.
---
Phase 1: Initial Search
# PARALLEL - Quick searches
Grep(pattern="$ARGUMENTS[0]", output_mode="files_with_matches")
Glob(pattern="**/*$ARGUMENTS[0]*")Phase 2: Memory Check
mcp__memory__search_nodes(query="$ARGUMENTS[0]")
mcp__memory__search_nodes(query="architecture")Phase 3: Parallel Deep Exploration (4 Agents)
Load Read("${CLAUDE_SKILL_DIR}/rules/exploration-agents.md") for Task tool mode prompts.
Load Read("${CLAUDE_SKILL_DIR}/rules/agent-teams-mode.md") for Agent Teams alternative.
Phase 4: AI System Exploration (If Applicable)
For AI/ML topics, add exploration of: LangGraph workflows, prompt templates, RAG pipeline, caching strategies.
Phase 5: Code Health Assessment
Load Read("${CLAUDE_SKILL_DIR}/rules/code-health-assessment.md") for agent prompt. Load Read("${CLAUDE_SKILL_DIR}/references/code-health-rubric.md") for scoring criteria.
Phase 6: Dependency Hotspot Map
Load Read("${CLAUDE_SKILL_DIR}/rules/dependency-hotspot-analysis.md") for agent prompt. Load Read("${CLAUDE_SKILL_DIR}/references/dependency-analysis.md") for metrics.
Phase 7: Product Perspective
Load Read("${CLAUDE_SKILL_DIR}/rules/product-perspective.md") for agent prompt. Load Read("${CLAUDE_SKILL_DIR}/references/findability-patterns.md") for best practices.
Phase 8: Generate Report
Load Read("${CLAUDE_SKILL_DIR}/references/exploration-report-template.md").
Phase 8b: Emit Dashboard Spec (json-render)
Parse --render= from $ARGUMENTS. Default is both.
| Mode | Behavior |
|---|---|
markdown | Current behavior — markdown report only. No spec emitted. |
json-render | Emit .claude/chain/explore-dashboard.json only. Skip markdown report. |
both | Emit spec and markdown. Default — gives the human a report and downstream skills a structured handoff. |
When emitting a spec:
1. Load the format and catalog: Read("${CLAUDE_SKILL_DIR}/references/dashboard-spec.md"). Reference example: references/dashboard-example.json. 2. Build the spec object using only catalog component types: Card, StatGrid, DataTable, StatusBadge, BarMeter, Heatmap, Markdown. 3. Write to .claude/chain/explore-dashboard.json with compact JSON (no indentation) — minimizes token cost for downstream consumers. 4. Validate before declaring success:
node "${CLAUDE_SKILL_DIR}/scripts/render-spec.mjs" .claude/chain/explore-dashboard.json --checkIf validation fails (exit ≠ 0), do not emit — fall back to markdown-only and surface the error to the user. Never write a partial or invalid spec.
5. For --render=both, render the markdown view from the spec for consistency:
node "${CLAUDE_SKILL_DIR}/scripts/render-spec.mjs" .claude/chain/explore-dashboard.jsonPipe the output into the user-facing markdown report (or use it as-is). This guarantees the JSON spec and markdown report stay in sync — a single source of truth.
Why this matters: Downstream skills (/ork:fix-issue, /ork:implement, /ork:create-pr) parse .claude/chain/explore-dashboard.json directly instead of re-reading 3000-token markdown. Measured: spec ≈ 580 tokens for the same content. Backwards-compatible: old chained workflows that read markdown keep working in both mode.
Phase 6.5 — Notebook summary (signal-fired, optional)
After the session synthesis lands, optionally invoke scripts/post_explore_summary.py <session-dir> to auto-emit a notebook-backed summary of the exploration. Self-skips on every non-happy-path so it never breaks the run:
python3 ${CLAUDE_SKILL_DIR}/scripts/post_explore_summary.py "$CLAUDE_JOB_DIR"Auto-skip conditions (all exit 0, all WARN-logged):
| Skip reason | Trigger |
|---|---|
signal absent | len(dirs_scanned) < 3 (or field missing on explore-output.json) |
yg-mcp-core not importable | yg-mcp-core>=0.3.0 not installed (orchestkit is public; yg-mcp-core lives on private pypi.yonyon.ai — HQ-only) |
hq-content MCP unreachable | MCP server down OR .mcp.json doesn't define hq-content |
Session dir must contain explore-output.json (with dirs_scanned: list[str], optional synthesis: str, required notebook_id: str). Handoff JSON at <session-dir>/explore-summary.json records status (fired / skipped) and summary_path on success.
Mirrors the /ork:brainstorm post-synth podcast pattern from PR #1889. Closes orchestkit#1893.
Notes for long explorations
Oversized reads (CC 2.1.144+): Read returns a[PARTIAL view]truncated first page (not a hard error) when a whole-file read exceeds the token limit. When traversing large files, detect that notice and re-read with explicitoffset/limitto page through the rest — never treat the partial as the full file.
When context fills (CC 2.1.141+): Use the rewind menu's "Summarize up to here" to compress earlier turns while keeping recent context, instead of restarting. Reactive compaction (CC 2.1.142+) now sizes the first summarize to the actual overflow, so a second mid-turn pass is rare.
Common Exploration Queries
- "How does authentication work?"
- "Where are API endpoints defined?"
- "Find all usages of EventBroadcaster"
- "What's the workflow for content analysis?"
Running unattended with /goal
Set a completion condition with /goal (CC 2.1.139+) and this skill will keep working across turns until the condition is met. Works in interactive, -p, and Remote Control. The overlay panel shows live elapsed / turns / tokens.
Example completion condition for this skill:
/goal until report.has_architecture_diagram AND patterns.detected_count >= 5Stops when: codebase architecture diagram is generated and at least 5 design patterns have been classified. Compatible with claude.ai Remote Control runs.
📜 Related Skills
ork:implement: Implement after exploration
---
Version: 2.6.0 (April 2026) — ${CLAUDE_EFFORT} env var scales agent count (CC 2.1.120, #1540)
Exploration Report: {{TOPIC}}
Generated: {{DATE}} Explorer: {{AGENT}}
---
Overview
{{BRIEF_SUMMARY}}
---
Architecture
{{ASCII_ARCHITECTURE_DIAGRAM}}Key Components:
- {{COMPONENT_1}}: {{PURPOSE_1}}
- {{COMPONENT_2}}: {{PURPOSE_2}}
---
Entry Points
| File | Purpose | Start Here If... |
|---|---|---|
{{FILE_1}} | {{PURPOSE}} | {{REASON}} |
{{FILE_2}} | {{PURPOSE}} | {{REASON}} |
Search Keywords: {{KEYWORD_1}}, {{KEYWORD_2}}, {{KEYWORD_3}}
---
Health Assessment
| Dimension | Score | Notes |
|---|---|---|
| Readability | {{SCORE}}/10 | {{NOTES}} |
| Maintainability | {{SCORE}}/10 | {{NOTES}} |
| Testability | {{SCORE}}/10 | {{NOTES}} |
| Complexity | {{SCORE}}/10 | {{NOTES}} |
| Documentation | {{SCORE}}/10 | {{NOTES}} |
| Overall | {{OVERALL}}/10 |
Hotspots:
{{FILE}}:{{LINE}}- {{ISSUE}}
---
Dependency Map
{{HOTSPOT_DIAGRAM}}- Coupling Score: {{SCORE}}/10
- Fan-In: {{N}} files depend on this
- Fan-Out: {{M}} dependencies
- Circular Dependencies: {{LIST_OR_NONE}}
---
Recommendations
1. Immediate: {{ACTION_1}} 2. Short-term: {{ACTION_2}} 3. Long-term: {{ACTION_3}}
---
How to Modify
1. {{STEP_1}} 2. {{STEP_2}} 3. {{STEP_3}}
---
Related Resources
- {{LINK_1}}
- {{LINK_2}}
Dependency Hotspot Diagram Templates
Basic Dependency Graph
┌──────────────┐
│ Target │
│ Module │
└──────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Dep A │ │ Dep B │ │ Dep C │
└──────────┘ └──────────┘ └──────────┘Fan-In / Fan-Out Chart
Fan-In (Dependents) Fan-Out (Dependencies)
━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━
module_a.py ████████ 8 lib_x ██████ 6
module_b.py ██████ 6 lib_y ████ 4
module_c.py ████ 4 lib_z ██ 2
module_d.py ██ 2 internal █ 1Coupling Matrix
│ auth │ user │ api │ db │
─────────────┼──────┼──────┼─────┼────┤
auth │ - │ X │ X │ X │
user │ X │ - │ X │ X │
api │ │ │ - │ │
db │ │ │ │ - │
X = bidirectional dependency (potential circular)Circular Dependency Visualization
┌─────────────────────────────┐
│ │
▼ │
┌───────┐ ┌───────┐ ┌───────┐
│ A │────▶│ B │────▶│ C │
└───────┘ └───────┘ └───────┘
▲ │
│ │
└─────────────────────────────┘
CIRCULAR: A → B → C → AChange Impact Blast Radius
[CHANGED FILE]
│
┌────────────────┼────────────────┐
│ │ │
[Direct: 3] [Direct: 5] [Direct: 2]
│ │
┌────┴────┐ ┌────┴────┐
│ │ │ │
[Trans: 2] [Trans: 4] [Trans: 1] [Trans: 3]
Total Impact: 3 + 5 + 2 = 10 direct
2 + 4 + 1 + 3 = 10 transitiveCode Health Rubric
Standardized 0-10 scoring criteria for assessing code quality across five dimensions.
Scoring Scale
| Score | Rating | Description |
|---|---|---|
| 9-10 | Excellent | Production-ready, exemplary code |
| 7-8 | Good | Minor improvements possible |
| 5-6 | Adequate | Functional but needs attention |
| 3-4 | Poor | Significant issues, refactor recommended |
| 0-2 | Critical | Major problems, immediate action required |
---
1. Readability (0-10)
| Score | Criteria |
|---|---|
| 10 | Self-documenting, intuitive naming, perfect structure |
| 7-8 | Clear names, logical flow, minimal cognitive load |
| 5-6 | Understandable with effort, some unclear sections |
| 3-4 | Confusing logic, poor naming, requires context |
| 0-2 | Incomprehensible, magic numbers, no conventions |
2. Maintainability (0-10)
| Score | Criteria |
|---|---|
| 10 | SRP adherence, loose coupling, DRY, easy to modify |
| 7-8 | Good separation, minor duplication, clear boundaries |
| 5-6 | Some coupling, moderate duplication, changes ripple |
| 3-4 | High coupling, significant duplication, fragile |
| 0-2 | Spaghetti code, any change breaks multiple areas |
3. Testability (0-10)
| Score | Criteria |
|---|---|
| 10 | Pure functions, DI, 90%+ coverage, mocks easy |
| 7-8 | Most logic testable, some DI, 70%+ coverage |
| 5-6 | Testable with effort, some hidden dependencies |
| 3-4 | Hard to isolate, global state, 30% coverage |
| 0-2 | Untestable, tightly coupled, no test infrastructure |
4. Complexity (0-10, inverted: 10=simple)
| Score | Criteria |
|---|---|
| 10 | Cyclomatic <5, max 2 nesting, <20 line functions |
| 7-8 | Cyclomatic 5-10, 3 nesting, <40 line functions |
| 5-6 | Cyclomatic 10-15, 4 nesting, some long functions |
| 3-4 | Cyclomatic 15-25, deep nesting, 100+ line functions |
| 0-2 | Cyclomatic >25, 6+ nesting, god functions |
5. Documentation (0-10)
| Score | Criteria |
|---|---|
| 10 | Complete API docs, examples, architecture notes |
| 7-8 | Public API documented, inline comments where needed |
| 5-6 | Some docstrings, missing edge cases |
| 3-4 | Sparse comments, outdated documentation |
| 0-2 | No documentation, misleading comments |
---
Overall Score Calculation
overall = (readability + maintainability + testability + complexity + documentation) / 5Score Interpretation:
- 8.0+: Ship it
- 6.0-7.9: Acceptable, plan improvements
- 4.0-5.9: Technical debt, prioritize refactoring
- <4.0: Stop and fix before proceeding
{
"root": "report",
"version": "1.0.0",
"skill": "explore",
"topic": "authentication",
"elements": {
"report": {
"type": "Card",
"props": { "title": "Exploration Report — authentication" },
"children": ["summary", "health", "deps", "files"]
},
"summary": {
"type": "StatGrid",
"props": {
"items": [
{ "label": "Files matched", "value": "23" },
{ "label": "Health score", "value": "7.4/10", "color": "green" },
{ "label": "Coupling", "value": "Medium", "color": "yellow" },
{ "label": "Patterns", "value": "service-layer, repository" }
]
}
},
"health": {
"type": "Card",
"props": { "title": "Code Health" },
"children": ["health-readability", "health-maintainability", "health-testability"]
},
"health-readability": { "type": "BarMeter", "props": { "label": "Readability", "value": 8.0, "color": "green" } },
"health-maintainability": { "type": "BarMeter", "props": { "label": "Maintainability", "value": 7.0, "color": "green" } },
"health-testability": { "type": "BarMeter", "props": { "label": "Testability", "value": 6.5, "color": "yellow" } },
"deps": {
"type": "Card",
"props": { "title": "Dependency Hotspots" },
"children": ["deps-table"]
},
"deps-table": {
"type": "DataTable",
"props": {
"columns": [
{ "key": "file", "label": "File" },
{ "key": "fanin", "label": "Fan-in" },
{ "key": "fanout", "label": "Fan-out" },
{ "key": "score", "label": "Coupling" }
],
"rows": [
{ "file": "src/auth/session.ts", "fanin": "12", "fanout": "4", "score": "high" },
{ "file": "src/auth/jwt.ts", "fanin": "8", "fanout": "2", "score": "med" }
]
}
},
"files": {
"type": "DataTable",
"props": {
"columns": [
{ "key": "file", "label": "File" },
{ "key": "purpose", "label": "Purpose" },
{ "key": "score", "label": "Health" }
],
"rows": [
{ "file": "src/auth/session.ts", "purpose": "Session tokens + refresh", "score": "8.0" },
{ "file": "src/auth/jwt.ts", "purpose": "JWT sign/verify wrapper", "score": "7.5" }
]
}
}
}
}
Exploration Dashboard Spec
When --render=json-render or --render=both is passed to /ork:explore, Phase 8 emits a json-render-compatible JSON spec to .claude/chain/explore-dashboard.json instead of (or in addition to) the markdown report.
The spec follows the flat element-map format documented in ork:mcp-visual-output — { root, elements } with each element keyed by id and referencing children by id. This is the format consumed by @json-render/mcp 0.17+ when an MCP host iframe-renders it, and is also consumable by downstream skills as a structured handoff.
Catalog
These are the only component types the spec is allowed to use. They map to @json-render/shadcn registry entries when rendered visually.
| Type | Purpose | Required Props |
|---|---|---|
Card | Section wrapper with optional title | title?: string |
StatGrid | 2–6 metrics in a grid | items: { label, value, trend?, color? }[] |
DataTable | Tabular rows | columns: { key, label }[], rows: Record<string,string>[] |
StatusBadge | Single status indicator | label: string, `status: success |
BarMeter | 0-10 score bar | label: string, value: number (0-10), color?: string |
Heatmap | Coupling/dependency matrix | xLabels: string[], yLabels: string[], cells: number[][] |
Markdown | Free-text fallback for prose sections | content: string |
trend enum: up | down | flat. color enum: green | red | yellow | blue | gray.
Elements may reference children only by id from the same elements map. Recursion is bounded — Cards can contain other Cards but no deeper than 2 levels.
Example
A complete spec for /ork:explore authentication:
{
"root": "report",
"version": "1.0.0",
"skill": "explore",
"topic": "authentication",
"elements": {
"report": {
"type": "Card",
"props": { "title": "Exploration Report — authentication" },
"children": ["summary", "health", "deps", "files"]
},
"summary": {
"type": "StatGrid",
"props": {
"items": [
{ "label": "Files matched", "value": "23" },
{ "label": "Health score", "value": "7.4/10", "color": "green" },
{ "label": "Coupling", "value": "Medium", "color": "yellow" },
{ "label": "Patterns", "value": "service-layer, repository" }
]
}
},
"health": {
"type": "Card",
"props": { "title": "Code Health" },
"children": ["health-readability", "health-maintainability", "health-testability"]
},
"health-readability": { "type": "BarMeter", "props": { "label": "Readability", "value": 8.0, "color": "green" } },
"health-maintainability": { "type": "BarMeter", "props": { "label": "Maintainability", "value": 7.0, "color": "green" } },
"health-testability": { "type": "BarMeter", "props": { "label": "Testability", "value": 6.5, "color": "yellow" } },
"deps": {
"type": "Card",
"props": { "title": "Dependency Hotspots" },
"children": ["deps-table"]
},
"deps-table": {
"type": "DataTable",
"props": {
"columns": [
{ "key": "file", "label": "File" },
{ "key": "fanin", "label": "Fan-in" },
{ "key": "fanout", "label": "Fan-out" },
{ "key": "score", "label": "Coupling" }
],
"rows": [
{ "file": "src/auth/session.ts", "fanin": "12", "fanout": "4", "score": "high" },
{ "file": "src/auth/jwt.ts", "fanin": "8", "fanout": "2", "score": "med" }
]
}
},
"files": {
"type": "DataTable",
"props": {
"columns": [
{ "key": "file", "label": "File" },
{ "key": "purpose", "label": "Purpose" },
{ "key": "score", "label": "Health" }
],
"rows": [
{ "file": "src/auth/session.ts", "purpose": "Session tokens + refresh", "score": "8.0" },
{ "file": "src/auth/jwt.ts", "purpose": "JWT sign/verify wrapper", "score": "7.5" }
]
}
}
}
}Token cost (measured, not promised)
The example above serializes to ~700 tokens (compact JSON). The equivalent markdown report from references/exploration-report-template.md is ~3000 tokens when filled with the same content, mostly because of repeated table syntax and ASCII art.
The savings show up when downstream skills (e.g., /ork:fix-issue, /ork:implement) parse this spec from .claude/chain/explore-dashboard.json instead of re-reading the human-facing markdown. The markdown still gets written for the human reader when --render=both (the default for /ork:explore).
Validation
The companion script scripts/render-spec.mjs validates the spec on emission:
- All children ids resolve in
elements - Component types are in the catalog
- Required props are present
- BarMeter values are in [0, 10]
- DataTable rows match column keys
Validation failure aborts emission with a non-zero exit. The skill MUST fall back to markdown when validation fails — never emit a partial or invalid spec.
How downstream skills consume it
spec = JSON.parse(Read(".claude/chain/explore-dashboard.json"))
hotspots = spec.elements["deps-table"].props.rows # structured rows
overall_health = spec.elements["summary"].props.items[1].value # "7.4/10"No regex. No markdown table parsing. The spec is the structured handoff; the markdown is the human view.
Dependency Analysis
Identify coupling hotspots and dependency patterns in codebases.
Fan-In / Fan-Out Metrics
| Metric | Definition | Implication |
|---|---|---|
| Fan-In | Files that import this module | High = many dependents, changes risky |
| Fan-Out | Modules this file imports | High = many dependencies, fragile |
| Instability | Fan-Out / (Fan-In + Fan-Out) | 0 = stable, 1 = unstable |
Ideal Patterns:
- Core utilities: High fan-in, low fan-out (stable)
- Feature modules: Low fan-in, moderate fan-out
- Entry points: Low fan-in, high fan-out
---
Hotspot Identification
High-Risk Indicators
| Pattern | Risk | Action |
|---|---|---|
| Fan-in > 10 | Blast radius large | Add interface/abstraction |
| Fan-out > 8 | Too many dependencies | Extract facades |
| Instability = 1, Fan-in > 5 | Unstable core | Stabilize or decouple |
Coupling Score Formula
coupling_score = min(10, (fan_in + fan_out) / 3)- 0-3: Low coupling (healthy)
- 4-6: Moderate coupling (monitor)
- 7-10: High coupling (refactor)
---
Circular Dependency Detection
Signs of Circular Dependencies: 1. Import errors at runtime 2. Mysterious None values 3. Files that always change together 4. Cannot extract to separate package
Detection Approach:
A imports B
B imports C
C imports A <- CIRCULARResolution Strategies: 1. Extract shared interface 2. Dependency inversion (depend on abstractions) 3. Merge tightly coupled modules 4. Event-driven decoupling
---
Change Impact Analysis
Questions to Answer: 1. If I modify this file, what breaks? 2. Which files always change together? 3. What is the blast radius of a refactor?
Measuring Impact:
- Direct Impact: Files importing the changed module
- Transitive Impact: Files importing those files
- Co-Change Frequency: Git history of files changed together
High Impact Indicators:
- > 5 direct dependents
- > 20 transitive dependents
- > 80% co-change frequency with another file
Exploration Report Template
Use this template for Phase 8 report generation.
# Exploration Report: $ARGUMENTS
## Quick Answer
[1-2 sentence summary]
## File Locations
| File | Purpose | Health Score |
|------|---------|--------------|
| `path/to/file.py` | [description] | [N.N/10] |
## Code Health Summary
| Dimension | Score | Notes |
|-----------|-------|-------|
| Readability | [N/10] | [note] |
| Maintainability | [N/10] | [note] |
| Testability | [N/10] | [note] |
| Complexity | [N/10] | [note] |
| Documentation | [N/10] | [note] |
| **Overall** | **[N.N/10]** | |
## Architecture Overview
[ASCII diagram]
## Dependency Hotspot Map[Incoming deps] → [TARGET] → [Outgoing deps]
- **Coupling Score:** [N/10]
- **Fan-in:** [N] files depend on this
- **Fan-out:** [M] dependencies
- **Circular Dependencies:** [list or "None"]
## Data Flow
1. [Entry] → 2. [Processing] → 3. [Storage]
## Findability & Entry Points
| Entry Point | Why Start Here |
|-------------|----------------|
| `path/to/file.py` | [reason] |
**Search Keywords:** [keyword1], [keyword2], [keyword3]
## Product Context
- **Business Purpose:** [what problem this solves]
- **Primary Users:** [who uses this]
- **Documentation Gaps:** [what's missing]
## How to Modify
1. [Step 1]
2. [Step 2]
## Recommendations
1. [Health improvement]
2. [Findability improvement]
3. [Documentation improvement]Findability Patterns
Improve code discoverability for developers exploring the codebase.
Naming Conventions for Searchability
| Pattern | Example | Searchability |
|---|---|---|
| Domain prefix | auth_login(), auth_logout() | Grep "auth_" finds all |
| Feature suffix | UserService, UserRepository | Grep "User" finds related |
| Action verbs | create_user, delete_order | Grep "create_" finds patterns |
| Consistent pluralization | users/, orders/ | Predictable directory names |
Anti-Patterns:
- Abbreviations:
usr,mgr,svc(hard to search) - Generic names:
utils.py,helpers.js(too broad) - Inconsistent casing:
getUserData,get_user_data
---
Documentation Placement
| Location | Purpose | Findability |
|---|---|---|
README.md in directory | Module overview | First thing developers see |
| Inline docstrings | Function behavior | IDE tooltips, grep |
docs/architecture/ | System design | High-level understanding |
CLAUDE.md / CONTRIBUTING.md | Development guide | Onboarding entry |
Entry Point Strategy: 1. Every directory should have a README or index 2. Complex modules need architecture diagrams 3. Public APIs need usage examples 4. Workflows need sequence diagrams
---
Module Organization
Vertical Slice Architecture
features/
auth/
api.py # Entry point
service.py # Business logic
repository.py # Data access
models.py # Domain models
tests/ # Co-located testsBenefits:
- Related code together
- Easy to find all auth-related files
- Clear boundaries
Horizontal Layer Architecture
api/
auth.py
users.py
services/
auth.py
users.pyBenefits:
- Technical cohesion
- Easier cross-cutting concerns
---
Improving Discoverability
Quick Wins
1. Add index files: Export public API from __init__.py or index.ts 2. Use consistent prefixes: handle_, on_, create_, get_ 3. Create README per directory: Brief purpose + key files 4. Tag with keywords: Add searchable comments for concepts
Search Optimization
# Keywords: authentication, login, JWT, OAuth, session
# See also: user_service.py, token_handler.pyMetadata in Files:
- Related files cross-reference
- Alternative terms for the concept
- Links to documentation
Rule Categories
1. Agent Orchestration -- HIGH -- 2 rules
Patterns for spawning and coordinating parallel exploration agents.
agent-teams-mode.md-- Multi-agent team formation with real-time discovery sharingexploration-agents.md-- Task tool mode with 4 parallel background explorers
2. Code Analysis -- MEDIUM -- 3 rules
Specialized assessment prompts for code quality and architecture analysis.
code-health-assessment.md-- Five-dimension code quality scoring (0-10)dependency-hotspot-analysis.md-- Coupling detection and change impact analysisproduct-perspective.md-- Business context and findability assessment
Agent Teams Mode
In Agent Teams mode, form an exploration team where explorers share discoveries in real-time:
TeamCreate(team_name="explore-{topic}", description="Explore {topic}")
Agent(subagent_type="Explore", name="structure-explorer",
team_name="explore-{topic}",
prompt="""Find all files, classes, and functions related to: {topic}
When you discover key entry points, message data-flow-explorer so they
can trace data paths from those points.
When you find backend patterns, message backend-explorer.
When you find frontend components, message frontend-explorer.""")
Agent(subagent_type="Explore", name="data-flow-explorer",
team_name="explore-{topic}",
prompt="""Trace entry points, processing, and storage for: {topic}
When structure-explorer shares entry points, start tracing from those.
When you discover cross-boundary data flows (frontend→backend or vice versa),
message both backend-explorer and frontend-explorer.""")
Agent(subagent_type="ork:backend-system-architect", name="backend-explorer",
team_name="explore-{topic}",
prompt="""Analyze backend architecture patterns for: {topic}
When structure-explorer or data-flow-explorer share backend findings,
investigate deeper — API design, database schema, service patterns.
Share integration points with frontend-explorer for consistency.""")
Agent(subagent_type="ork:frontend-ui-developer", name="frontend-explorer",
team_name="explore-{topic}",
prompt="""Analyze frontend components, state, and routes for: {topic}
When structure-explorer shares component locations, investigate deeper.
When backend-explorer shares API patterns, verify frontend alignment.
Share component hierarchy with data-flow-explorer.""")Team Teardown
After report generation:
# TeamDelete() shuts down all teammates — no manual shutdown_request needed
TeamDelete()
# Worktree cleanup (CC 2.1.72)
ExitWorktree(action="keep")Fallback: If team formation fails, use standard Task tool spawns. See exploration-agents.md.
Incorrect — Sequential exploration without coordination:
Agent(subagent_type="Explore", prompt="Find auth files")
# Wait for result...
Agent(subagent_type="Explore", prompt="Trace auth data flow")
# Sequential, no sharing between agentsCorrect — Team mode with real-time discovery sharing:
TeamCreate(team_name="explore-auth")
Agent(subagent_type="Explore", name="structure-explorer",
team_name="explore-auth",
prompt="Find auth files. Message data-flow-explorer with entry points.")
Agent(subagent_type="Explore", name="data-flow-explorer",
team_name="explore-auth",
prompt="When structure-explorer shares entry points, trace data flows.")
# Parallel execution, coordinated via messagesCode Health Assessment
Rate found code quality 0-10 with specific dimensions. See code-health-rubric.md for scoring criteria.
Agent(
subagent_type="ork:code-quality-reviewer",
prompt="""CODE HEALTH ASSESSMENT for files related to: $ARGUMENTS
Rate each dimension 0-10:
1. READABILITY (0-10)
- Clear naming conventions?
- Appropriate comments?
- Logical organization?
2. MAINTAINABILITY (0-10)
- Single responsibility?
- Low coupling?
- Easy to modify?
3. TESTABILITY (0-10)
- Pure functions where possible?
- Dependency injection?
- Existing test coverage?
4. COMPLEXITY (0-10, inverted: 10=simple, 0=complex)
- Cyclomatic complexity?
- Nesting depth?
- Function length?
5. DOCUMENTATION (0-10)
- API docs present?
- Usage examples?
- Architecture notes?
Output:
{
"overall_score": N.N,
"dimensions": {
"readability": N,
"maintainability": N,
"testability": N,
"complexity": N,
"documentation": N
},
"hotspots": ["file:line - issue"],
"recommendations": ["improvement suggestion"]
}
SUMMARY: End with: "HEALTH: [N.N]/10 - [best dimension] strong, [worst dimension] needs work"
""",
run_in_background=True,
max_turns=25
)Incorrect — Vague code quality feedback:
Code Review: The code looks okay. Some parts are complex.
Maybe add more tests.Correct — Structured health assessment with scores:
{
"overall_score": 6.2,
"dimensions": {
"readability": 8,
"maintainability": 5,
"testability": 4,
"complexity": 6,
"documentation": 8
},
"hotspots": [
"auth.ts:45 - nested if/else 5 levels deep",
"utils.ts:120 - 200-line function, no SRP"
],
"recommendations": [
"Extract auth.ts:45-80 to separate validation functions",
"Add unit tests for utils.ts edge cases"
]
}Dependency Hotspot Analysis
Identify highly-coupled code and dependency bottlenecks. See dependency-analysis.md for metrics and formulas.
Agent(
subagent_type="ork:backend-system-architect",
prompt="""DEPENDENCY HOTSPOT ANALYSIS for: $ARGUMENTS
Analyze coupling and dependencies:
1. IMPORT ANALYSIS
- Which files import this code?
- What does this code import?
- Circular dependencies?
2. COUPLING SCORE (0-10, 10=highly coupled)
- How many files would break if this changes?
- Fan-in (incoming dependencies)
- Fan-out (outgoing dependencies)
3. CHANGE IMPACT
- Blast radius of modifications
- Files that always change together
4. HOTSPOT VISUALIZATION[Module A] --depends--> [Target] <--depends-- [Module B] | v [Module C]
Output:
{
"coupling_score": N,
"fan_in": N,
"fan_out": N,
"circular_deps": [],
"change_impact": ["file - reason"],
"hotspot_diagram": "ASCII diagram"
}
SUMMARY: End with: "COUPLING: [N]/10 - [N] incoming, [M] outgoing deps - [key concern]"
""",
run_in_background=True,
max_turns=25
)Incorrect — Listing imports without analysis:
auth.ts imports:
- utils.ts
- config.ts
- db.tsCorrect — Hotspot analysis with coupling score:
{
"coupling_score": 8,
"fan_in": 12,
"fan_out": 5,
"circular_deps": ["auth.ts → user.ts → auth.ts"],
"change_impact": [
"auth.ts change breaks 12 files",
"utils.ts and auth.ts always change together"
],
"hotspot_diagram": "
[12 files] --depend on--> [auth.ts]
|
depends on
v
[utils, config, db, user, session]
"
}Exploration Agents (Task Tool Mode)
Launch 4 specialized explorers in ONE message with run_in_background: true:
# PARALLEL - All 4 in ONE message
Agent(
subagent_type="Explore",
prompt="""Code Structure: Find all files, classes, functions related to: $ARGUMENTS
Scope: ONLY read files directly relevant to the topic. Do NOT explore the entire codebase.
SUMMARY: End with: "RESULT: [N] files, [M] classes - [key location, e.g., 'src/auth/']"
""",
run_in_background=True,
max_turns=25
)
Agent(
subagent_type="Explore",
prompt="""Data Flow: Trace entry points, processing, storage for: $ARGUMENTS
Scope: ONLY read files directly relevant to the topic. Do NOT explore the entire codebase.
SUMMARY: End with: "RESULT: [entry] → [processing] → [storage] - [N] hop flow"
""",
run_in_background=True,
max_turns=25
)
Agent(
subagent_type="ork:backend-system-architect",
prompt="""Backend Patterns: Analyze architecture patterns, integrations, dependencies for: $ARGUMENTS
Scope: ONLY read files directly relevant to the topic. Do NOT explore the entire codebase.
SUMMARY: End with: "RESULT: [pattern name] - [N] integrations, [M] dependencies"
""",
run_in_background=True,
max_turns=25
)
Agent(
subagent_type="ork:frontend-ui-developer",
prompt="""Frontend Analysis: Find components, state management, routes for: $ARGUMENTS
Scope: ONLY read files directly relevant to the topic. Do NOT explore the entire codebase.
SUMMARY: End with: "RESULT: [N] components, [state lib] - [key route]"
""",
run_in_background=True,
max_turns=25
)Fork Pattern (CC 2.1.89 — #1227)
These agents are fork-eligible: short prompts (<500 words), no custom model, no worktree isolation. CC automatically shares the parent's cached API prefix across all 4 forks, reducing cost by ~60%.
See chain-patterns/references/fork-pattern.md for full details.Do NOT add model= or isolation="worktree" to these agents — it breaks cache sharing.
Explorer Roles
1. Code Structure Explorer - Files, classes, functions 2. Data Flow Explorer - Entry points, processing, storage 3. Backend Architect - Patterns, integration, dependencies 4. Frontend Developer - Components, state, routes
Incorrect — Sequential exploration:
Agent(subagent_type="Explore", prompt="Find auth files")
# Wait...
Agent(subagent_type="Explore", prompt="Trace auth flow")
# Wait...
Agent(subagent_type="ork:backend-system-architect", prompt="Analyze patterns")
# Slow, sequentialCorrect — Parallel exploration in one message:
# All 4 in ONE message with run_in_background: true
Agent(subagent_type="Explore", prompt="Code Structure: Find all files related to auth",
run_in_background=True, max_turns=25)
Agent(subagent_type="Explore", prompt="Data Flow: Trace auth entry→storage",
run_in_background=True, max_turns=25)
Agent(subagent_type="ork:backend-system-architect", prompt="Backend Patterns: Analyze auth architecture",
run_in_background=True, max_turns=25)
Agent(subagent_type="ork:frontend-ui-developer", prompt="Frontend: Find auth components",
run_in_background=True, max_turns=25)
# Parallel executionProduct Perspective
Add business context and findability suggestions. See findability-patterns.md for discoverability best practices.
Agent(
subagent_type="ork:product-strategist",
prompt="""PRODUCT PERSPECTIVE for: $ARGUMENTS
Analyze from a product/business viewpoint:
1. BUSINESS CONTEXT
- What user problem does this code solve?
- What feature/capability does it enable?
- Who are the users of this code?
2. FINDABILITY SUGGESTIONS
- Better naming for discoverability?
- Missing documentation entry points?
- Where should someone look first?
3. KNOWLEDGE GAPS
- What context is missing for new developers?
- What tribal knowledge exists?
- What should be documented?
4. SEARCH OPTIMIZATION
- Keywords someone might use to find this
- Alternative terms for the same concept
- Related concepts to cross-reference
Output:
{
"business_purpose": "description",
"primary_users": ["user type"],
"findability_issues": ["issue - suggestion"],
"recommended_entry_points": ["file - why start here"],
"search_keywords": ["keyword"],
"documentation_gaps": ["gap"]
}
SUMMARY: End with: "FINDABILITY: [N] issues - start at [recommended entry point]"
""",
run_in_background=True,
max_turns=25)Incorrect — Technical analysis without business context:
Found auth.ts, user.ts, session.ts
Uses JWT tokens, bcrypt hashing
Database: PostgreSQL users tableCorrect — Product perspective with findability:
{
"business_purpose": "Secure user authentication and session management",
"primary_users": ["End users logging in", "Developers integrating auth"],
"findability_issues": [
"auth.ts - generic name, try auth/core.ts",
"Missing README in auth/ - devs don't know where to start"
],
"recommended_entry_points": [
"auth/README.md (missing - create this!)",
"auth/core.ts - main authentication flow"
],
"search_keywords": ["login", "authentication", "session", "JWT", "security"],
"documentation_gaps": [
"No auth flow diagram",
"Token refresh logic undocumented"
]
}#!/usr/bin/env bash
#
# dependency-mapper.sh
# Extract import statements and identify dependency hotspots
#
# Usage: ./dependency-mapper.sh [directory] [--top N]
#
# Supports: Python, TypeScript, JavaScript
#
set -euo pipefail
# Defaults
TARGET_DIR="${1:-.}"
TOP_N=10
# Parse args
while [[ $# -gt 0 ]]; do
case $1 in
--top)
TOP_N="$2"
shift 2
;;
*)
TARGET_DIR="$1"
shift
;;
esac
done
# Temp files
IMPORTS_FILE=$(mktemp)
COUNTS_FILE=$(mktemp)
trap 'rm -f "$IMPORTS_FILE" "$COUNTS_FILE"' EXIT
echo "Scanning: $TARGET_DIR"
echo "======================================"
echo ""
# Extract Python imports
extract_python() {
find "$TARGET_DIR" -name "*.py" -type f 2>/dev/null | while read -r file; do
grep -E "^(import |from .+ import )" "$file" 2>/dev/null | while read -r line; do
# Extract module name
if [[ "$line" =~ ^import[[:space:]]+([a-zA-Z0-9_.]+) ]]; then
echo "${file}:${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^from[[:space:]]+([a-zA-Z0-9_.]+)[[:space:]]+import ]]; then
echo "${file}:${BASH_REMATCH[1]}"
fi
done
done
}
# Extract TypeScript/JavaScript imports
extract_js_ts() {
find "$TARGET_DIR" \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -type f 2>/dev/null | while read -r file; do
grep -E "^import .+ from ['\"]" "$file" 2>/dev/null | while read -r line; do
# Extract module path
if [[ "$line" =~ from[[:space:]]+[\'\"]([@a-zA-Z0-9_./-]+)[\'\"] ]]; then
echo "${file}:${BASH_REMATCH[1]}"
fi
done
# Also check require statements
grep -E "require\(['\"]" "$file" 2>/dev/null | while read -r line; do
if [[ "$line" =~ require\([\'\"]([@a-zA-Z0-9_./-]+)[\'\"]\) ]]; then
echo "${file}:${BASH_REMATCH[1]}"
fi
done
done
}
# Collect all imports
echo "Extracting imports..."
{
extract_python
extract_js_ts
} > "$IMPORTS_FILE"
TOTAL_IMPORTS=$(wc -l < "$IMPORTS_FILE" | tr -d ' ')
echo "Found $TOTAL_IMPORTS import statements"
echo ""
# Count dependencies per file (fan-out)
echo "## Fan-Out (Dependencies per File)"
echo "Files with most outgoing dependencies:"
echo ""
cut -d: -f1 "$IMPORTS_FILE" | sort | uniq -c | sort -rn | head -n "$TOP_N" | while read -r count file; do
printf " %-50s %3d deps\n" "$file" "$count"
done
echo ""
# Count how often each module is imported (fan-in)
echo "## Fan-In (Most Imported Modules)"
echo "Modules that other files depend on most:"
echo ""
cut -d: -f2 "$IMPORTS_FILE" | sort | uniq -c | sort -rn | head -n "$TOP_N" | while read -r count module; do
printf " %-40s %3d imports\n" "$module" "$count"
done
echo ""
# Identify potential hotspots (high fan-in + high fan-out)
echo "## Potential Hotspots"
echo "Files with both high fan-in and fan-out:"
echo ""
# Get fan-out per file
cut -d: -f1 "$IMPORTS_FILE" | sort | uniq -c | sort -rn > "$COUNTS_FILE"
# For each high fan-out file, check if it's also frequently imported
while read -r fanout file; do
# Skip if fan-out is low
[[ "$fanout" -lt 5 ]] && continue
# Get base name for fan-in check
basename=$(basename "$file" | sed 's/\.[^.]*$//')
fanin=$(grep -c "$basename" "$IMPORTS_FILE" 2>/dev/null || echo 0)
if [[ "$fanin" -gt 3 ]]; then
coupling=$((fanout + fanin))
printf " %-45s fan-in: %2d fan-out: %2d coupling: %2d\n" "$file" "$fanin" "$fanout" "$coupling"
fi
done < "$COUNTS_FILE" | sort -t: -k4 -rn | head -n "$TOP_N"
echo ""
echo "======================================"
echo "Scan complete. Total imports: $TOTAL_IMPORTS"
# Generated by OrchestKit Claude Plugin
# Created: 2026-05-20
#!/usr/bin/env python3
"""post_explore_summary.py — auto-emit explore-summary.md from a notebook query.
Closes orchestkit#1893. Signal-fired extra (NOT a numbered phase):
runs after /ork:explore session synthesis when:
len(dirs_scanned) >= 3
Mirrors the brainstorm/assess/memory consumers from PR #1889.
Three exit-0 "skip" paths (none are errors):
- signal absent: fewer than 3 distinct directories scanned
- yg-mcp-core not importable (orchestkit is public, yg-mcp-core is HQ-private)
- hq-content MCP unreachable
Tool stack (yg-mcp-core>=0.3.0):
- mcp_core.client.probe("hq-content")
- mcp_core.client.call_tool("hq-content", "notebook_query",
{"query": <prompt>, "notebook_id": <id>})
Usage:
post_explore_summary.py <session-dir>
<session-dir> must contain:
explore-output.json {dirs_scanned: list[str], synthesis: str, notebook_id: str}
Outputs:
<session-dir>/explore-summary.md on success
<session-dir>/explore-summary.json handoff JSON
Exit codes:
0 success, auto-skip per signals, OR MCP/dep unreachable (WARN only)
1 pre-flight failure (missing args, session dir / required files absent)
2 hq-content MCP returned an error AFTER probe succeeded (real error)
"""
import argparse
import json
import os
import sys
from pathlib import Path
from typing import Any
DIRS_THRESHOLD = 3
# Override at test time via env var.
DEFAULT_MCP_SERVER = os.environ.get("ORK_EXPLORE_MCP_SERVER", "hq-content")
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Auto-emit explore-summary.md from a notebook_query call.",
)
parser.add_argument(
"session_dir",
type=Path,
help="Session directory containing explore-output.json.",
)
return parser.parse_args(argv)
# ---------------------------------------------------------------------------
# signal evaluation (pure)
# ---------------------------------------------------------------------------
def _dirs_scanned(explore: dict[str, Any]) -> list[str]:
dirs = explore.get("dirs_scanned")
if not isinstance(dirs, list):
return []
return [d for d in dirs if isinstance(d, str)]
def evaluate_signal(explore: dict[str, Any]) -> tuple[bool, str]:
"""Return (fires, reason). reason is always populated."""
dirs = _dirs_scanned(explore)
distinct = sorted(set(dirs))
if not dirs:
return False, "no dirs_scanned field on explore-output.json"
if len(distinct) < DIRS_THRESHOLD:
return False, f"dirs_scanned={len(distinct)} < {DIRS_THRESHOLD}"
return True, f"dirs_scanned={len(distinct)} >= {DIRS_THRESHOLD}"
def _notebook_id(explore: dict[str, Any]) -> str | None:
notebook_id = explore.get("notebook_id")
if isinstance(notebook_id, str) and notebook_id.strip():
return notebook_id.strip()
return None
def _synthesis(explore: dict[str, Any]) -> str:
synthesis = explore.get("synthesis")
if isinstance(synthesis, str) and synthesis.strip():
return synthesis.strip()
return ""
# ---------------------------------------------------------------------------
# handoff JSON
# ---------------------------------------------------------------------------
def _write_handoff(handoff_path: Path, payload: dict[str, Any]) -> None:
handoff_path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
def _write_skip(handoff_path: Path, reason: str) -> None:
_write_handoff(handoff_path, {"status": "skipped", "skip_reason": reason})
# ---------------------------------------------------------------------------
# result handling
# ---------------------------------------------------------------------------
def _resolve_text(result: Any) -> str | None:
"""Pull the response text out of a notebook_query result.
Accept any of the common shapes (string, or dict with text/response/result).
"""
if isinstance(result, str):
return result
if isinstance(result, dict):
for key in ("text", "response", "answer", "result"):
candidate = result.get(key)
if isinstance(candidate, str):
return candidate
return None
# ---------------------------------------------------------------------------
# main
# ---------------------------------------------------------------------------
def main(argv: list[str] | None = None) -> int:
args = _parse_args(argv)
session_dir: Path = args.session_dir
if not session_dir.is_dir():
print(f"post_explore_summary: session dir not found: {session_dir}", file=sys.stderr)
return 1
explore_path = session_dir / "explore-output.json"
handoff_path = session_dir / "explore-summary.json"
summary_path = session_dir / "explore-summary.md"
if not explore_path.is_file():
print(
f"post_explore_summary: explore-output.json missing at {explore_path}",
file=sys.stderr,
)
return 1
try:
explore = json.loads(explore_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
print(f"post_explore_summary: cannot parse {explore_path}: {exc}", file=sys.stderr)
return 1
# Signal probe — auto-skip cleanly if predicate fails.
fires, reason = evaluate_signal(explore)
if not fires:
print(f"post_explore_summary: auto-skipped — signal absent ({reason})")
_write_skip(handoff_path, f"signal absent: {reason}")
return 0
print(f"post_explore_summary: signal fired — {reason}")
notebook_id = _notebook_id(explore)
if not notebook_id:
print("post_explore_summary: WARN — notebook_id missing on explore-output.json; skipping")
_write_skip(handoff_path, "notebook_id missing on explore-output.json")
return 0
# MCP preflight — fail-soft on dep miss OR unreachable server.
try:
from mcp_core.client import (
McpToolError,
McpUnreachable,
call_tool,
probe,
)
except ImportError:
print(
"post_explore_summary: WARN — yg-mcp-core not importable; skipping "
"(install yg-mcp-core>=0.3.0 from pypi.yonyon.ai to enable)"
)
_write_skip(handoff_path, "yg-mcp-core not importable")
return 0
server = DEFAULT_MCP_SERVER
if not probe(server):
print(f"post_explore_summary: WARN — {server!r} MCP unreachable; skipping")
_write_skip(handoff_path, f"{server!r} MCP unreachable")
return 0
synthesis = _synthesis(explore)
query = (
f"Summarize this exploration session: {synthesis}"
if synthesis
else ("Summarize this exploration session.")
)
print(
f"post_explore_summary: calling notebook_query on {server!r} (notebook_id={notebook_id!r})"
)
try:
result = call_tool(
server,
"notebook_query",
{"query": query, "notebook_id": notebook_id},
)
except (McpUnreachable, McpToolError) as exc:
print(f"post_explore_summary: ERROR — {type(exc).__name__}: {exc}", file=sys.stderr)
return 2
text = _resolve_text(result)
if not text:
print(
f"post_explore_summary: ERROR — notebook_query response had no text: {result!r}",
file=sys.stderr,
)
return 2
summary_path.write_text(text, encoding="utf-8")
_write_handoff(
handoff_path,
{
"status": "fired",
"summary_path": str(summary_path),
"notebook_id": notebook_id,
"server": server,
"size_bytes": len(text.encode("utf-8")),
},
)
print(f"post_explore_summary: ✓ wrote {summary_path} ({len(text)} chars)")
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env node
// render-spec.mjs — validate + render json-render dashboard specs
//
// Usage:
// node render-spec.mjs <spec.json> # validate, print markdown to stdout
// node render-spec.mjs <spec.json> --check # validate only, exit 0/1
// node render-spec.mjs <spec.json> --json # round-trip parse, print canonical JSON
//
// Zero deps. Used by /ork:explore + /ork:assess for the markdown-fallback path
// and as a structural validator before emission. See references/dashboard-spec.md.
import { readFileSync } from 'node:fs'
import { argv, exit, stdout, stderr } from 'node:process'
const CATALOG = {
Card: { required: [], optional: ['title'], children: 'allowed' },
StatGrid: { required: ['items'], optional: [], children: 'forbidden' },
DataTable: { required: ['columns', 'rows'], optional: [], children: 'forbidden' },
StatusBadge: { required: ['label', 'status'], optional: [], children: 'forbidden' },
BarMeter: { required: ['label', 'value'], optional: ['color'], children: 'forbidden' },
Heatmap: { required: ['xLabels', 'yLabels', 'cells'], optional: [], children: 'forbidden' },
Markdown: { required: ['content'], optional: [], children: 'forbidden' },
}
const STATUS_ENUM = new Set(['success', 'warning', 'error', 'info', 'pending'])
const COLOR_ENUM = new Set(['green', 'red', 'yellow', 'blue', 'gray'])
const TREND_ENUM = new Set(['up', 'down', 'flat'])
function fail(msg) {
stderr.write(`render-spec: ${msg}\n`)
exit(2)
}
function validate(spec) {
const errors = []
if (typeof spec !== 'object' || spec === null) errors.push('spec is not an object')
if (typeof spec.root !== 'string') errors.push('spec.root must be a string id')
if (typeof spec.elements !== 'object' || spec.elements === null) errors.push('spec.elements must be an object')
if (errors.length) return errors
if (!(spec.root in spec.elements)) errors.push(`root id "${spec.root}" not found in elements`)
for (const [id, el] of Object.entries(spec.elements)) {
const where = `elements["${id}"]`
if (!el || typeof el !== 'object') { errors.push(`${where} is not an object`); continue }
if (typeof el.type !== 'string') { errors.push(`${where}.type missing`); continue }
const def = CATALOG[el.type]
if (!def) { errors.push(`${where}.type "${el.type}" not in catalog`); continue }
if (typeof el.props !== 'object' || el.props === null) errors.push(`${where}.props must be an object`)
for (const r of def.required) if (!(r in (el.props || {}))) errors.push(`${where}.props.${r} required`)
if (el.children !== undefined) {
if (def.children === 'forbidden') errors.push(`${where} type "${el.type}" cannot have children`)
if (!Array.isArray(el.children)) errors.push(`${where}.children must be an array`)
else for (const cid of el.children) if (!(cid in spec.elements)) errors.push(`${where}.children references missing id "${cid}"`)
}
if (el.type === 'BarMeter') {
const v = el.props?.value
if (typeof v !== 'number' || v < 0 || v > 10) errors.push(`${where}.props.value must be number in [0,10]`)
if (el.props?.color && !COLOR_ENUM.has(el.props.color)) errors.push(`${where}.props.color invalid`)
}
if (el.type === 'StatusBadge') {
if (!STATUS_ENUM.has(el.props?.status)) errors.push(`${where}.props.status invalid`)
}
if (el.type === 'StatGrid') {
if (!Array.isArray(el.props?.items)) errors.push(`${where}.props.items must be array`)
else for (let i = 0; i < el.props.items.length; i++) {
const it = el.props.items[i]
if (typeof it?.label !== 'string') errors.push(`${where}.props.items[${i}].label required`)
if (typeof it?.value !== 'string') errors.push(`${where}.props.items[${i}].value required`)
if (it?.color && !COLOR_ENUM.has(it.color)) errors.push(`${where}.props.items[${i}].color invalid`)
if (it?.trend && !TREND_ENUM.has(it.trend)) errors.push(`${where}.props.items[${i}].trend invalid`)
}
}
if (el.type === 'DataTable') {
const cols = el.props?.columns
const rows = el.props?.rows
if (!Array.isArray(cols)) errors.push(`${where}.props.columns must be array`)
if (!Array.isArray(rows)) errors.push(`${where}.props.rows must be array`)
if (Array.isArray(cols) && Array.isArray(rows)) {
const keys = new Set(cols.map(c => c.key))
for (let i = 0; i < rows.length; i++) {
for (const k of Object.keys(rows[i])) if (!keys.has(k)) errors.push(`${where}.props.rows[${i}] has unknown key "${k}"`)
}
}
}
}
return errors
}
function renderElement(spec, id, depth = 0) {
const el = spec.elements[id]
if (!el) return ''
const ind = ' '.repeat(depth)
switch (el.type) {
case 'Card': {
const title = el.props?.title ? `${ind}## ${el.props.title}\n\n` : ''
const childTypes = (el.children || []).map(cid => spec.elements[cid]?.type)
const allBars = childTypes.length > 0 && childTypes.every(t => t === 'BarMeter')
const sep = allBars ? '\n' : '\n\n'
const body = (el.children || []).map(cid => renderElement(spec, cid, depth)).join(sep)
return title + body + (allBars ? '\n' : '')
}
case 'StatGrid': {
const cells = el.props.items.map(it => {
const trend = it.trend === 'up' ? ' ↑' : it.trend === 'down' ? ' ↓' : ''
return `**${it.label}:** ${it.value}${trend}`
})
return cells.join(' · ') + '\n'
}
case 'StatusBadge': {
const sym = { success: '✓', warning: '⚠', error: '✗', info: 'ℹ', pending: '…' }[el.props.status] || '•'
return `> ${sym} **${el.props.label}**\n`
}
case 'BarMeter': {
const v = el.props.value
const filled = Math.round(v)
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled)
return `- ${el.props.label.padEnd(16)} ${bar} ${v.toFixed(1)}/10`
}
case 'DataTable': {
const cols = el.props.columns
const head = '| ' + cols.map(c => c.label).join(' | ') + ' |'
const sep = '|' + cols.map(() => '---').join('|') + '|'
const body = el.props.rows.map(r => '| ' + cols.map(c => String(r[c.key] ?? '')).join(' | ') + ' |').join('\n')
return [head, sep, body].join('\n') + '\n'
}
case 'Heatmap': {
const { xLabels, yLabels, cells } = el.props
const head = '| | ' + xLabels.join(' | ') + ' |'
const sep = '|' + xLabels.map(() => '---').join('|') + '|---|'
const body = yLabels.map((y, i) => '| ' + y + ' | ' + (cells[i] || []).map(v => v.toFixed(1)).join(' | ') + ' |').join('\n')
return [head, sep, body].join('\n') + '\n'
}
case 'Markdown':
return el.props.content + '\n'
default:
return ''
}
}
function main() {
const path = argv[2]
const flag = argv[3]
if (!path) fail('usage: render-spec.mjs <spec.json> [--check|--json]')
let spec
try { spec = JSON.parse(readFileSync(path, 'utf8')) }
catch (e) { fail(`parse error: ${e.message}`) }
const errors = validate(spec)
if (errors.length) {
stderr.write(`render-spec: validation failed (${errors.length} error${errors.length > 1 ? 's' : ''})\n`)
for (const e of errors) stderr.write(` - ${e}\n`)
exit(1)
}
if (flag === '--check') { stdout.write('ok\n'); exit(0) }
if (flag === '--json') { stdout.write(JSON.stringify(spec, null, 2) + '\n'); exit(0) }
stdout.write(renderElement(spec, spec.root, 0))
}
main()
{
"skill": "explore",
"version": "2.1.0",
"testCases": [
{
"id": "full-exploration",
"rule": "exploration-agents",
"query": "Explore the authentication system in this codebase",
"expectedBehavior": [
"Launches 4 parallel exploration agents (structure, data flow, backend, frontend)",
"Includes code health assessment with 0-10 scoring across 5 dimensions",
"Spawns backend-system-architect subagent to analyze architecture patterns and integrations",
"Generates structured exploration report with file locations and architecture"
]
},
{
"id": "quick-search",
"rule": "exploration-agents",
"query": "Just find the files related to payment processing",
"expectedBehavior": [
"Skips deep agent analysis for faster lightweight results",
"Uses Grep and Glob for quick file discovery",
"Returns file list without full exploration phases",
"Completes phases 1-2 only without running full exploration"
]
},
{
"id": "data-flow-trace",
"rule": "exploration-agents",
"query": "Trace how user data flows from the API to the database",
"expectedBehavior": [
"Focuses data flow explorer agent on tracing entry points",
"Maps processing pipeline from API to storage",
"Identifies data transformations at each hop",
"Produces entry-to-storage flow diagram showing data transformations at each step"
]
},
{
"id": "architecture-patterns",
"rule": "exploration-agents",
"query": "What design patterns does the backend use?",
"expectedBehavior": [
"Focuses backend-system-architect agent on pattern analysis",
"Identifies architectural patterns and integrations used across the backend codebase",
"Reports on dependencies and integrations found by the backend explorer agent",
"Checks memory for prior architecture decisions"
]
},
{
"id": "agent-teams-mode",
"rule": "agent-teams-mode",
"query": "Explore the payment system using agent teams with coordinated discovery sharing between explorers.",
"expectedBehavior": [
"Creates an exploration team using TeamCreate with a descriptive team name",
"Launches multiple named agents with team_name for coordinated parallel exploration",
"Agents share discoveries in real-time by messaging each other with found entry points",
"Tears down the team with TeamDelete() after report generation (no manual shutdown_request)",
"Falls back to standard Task tool spawns if team formation fails"
]
},
{
"id": "code-health-assessment",
"rule": "code-health-assessment",
"query": "Assess the code health of the authentication module and give me scores across quality dimensions.",
"expectedBehavior": [
"Rates code quality 0-10 across five dimensions: readability, maintainability, testability, complexity, and documentation",
"Produces structured JSON output with overall_score, dimensions, hotspots, and recommendations",
"Identifies specific file and line hotspots like deeply nested conditionals or oversized functions",
"Uses code-quality-reviewer subagent type for the assessment with background execution",
"Ends with a summary line showing overall score and strongest and weakest dimensions"
]
},
{
"id": "dependency-hotspot-analysis",
"rule": "dependency-hotspot-analysis",
"query": "Analyze the coupling and dependency hotspots in the database access layer of this codebase.",
"expectedBehavior": [
"Calculates coupling score 0-10 with fan-in and fan-out dependency counts",
"Identifies circular dependencies between modules and flags them for resolution",
"Maps change impact blast radius showing which files break when the target changes",
"Produces an ASCII hotspot diagram visualizing dependency relationships between modules",
"Uses backend-system-architect subagent type for the dependency analysis"
]
},
{
"id": "product-perspective",
"rule": "product-perspective",
"query": "Analyze the notification system from a product perspective including business context and findability.",
"expectedBehavior": [
"Identifies the business purpose and primary user types for the explored code",
"Suggests better naming and documentation entry points for improved discoverability",
"Lists documentation gaps and tribal knowledge that should be written down",
"Provides search keywords and alternative terms to help developers find the code",
"Uses product-strategist subagent type to analyze from a business viewpoint"
]
}
]
}