diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d9b37a0..c7a58c2 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "understand-anything", "description": "Multi-agent codebase analysis with interactive dashboard, guided tours, and skill commands", - "version": "2.1.0", + "version": "2.2.0", "source": "./understand-anything-plugin" } ] diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index a1229dd..73781f5 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "understand-anything", "description": "AI-powered codebase understanding — analyze, visualize, and explain any project", - "version": "2.1.0", + "version": "2.2.0", "author": { "name": "Lum1104" }, diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 2255fba..e32414b 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "understand-anything", "displayName": "Understand Anything", "description": "AI-powered codebase understanding — analyze, visualize, and explain any project", - "version": "2.1.0", + "version": "2.2.0", "author": { "name": "Lum1104" }, diff --git a/understand-anything-plugin/agents/assemble-reviewer.md b/understand-anything-plugin/agents/assemble-reviewer.md index c4f05fb..ff611e4 100644 --- a/understand-anything-plugin/agents/assemble-reviewer.md +++ b/understand-anything-plugin/agents/assemble-reviewer.md @@ -85,3 +85,13 @@ The merge script combines what each batch produced independently. Batches don't ``` 3. Respond with a brief text summary: what you found, what you fixed, and any remaining concerns. + +## Writing Results + +After completing all steps above: + +1. Apply all fixes directly to `assembled-graph.json` (the file path provided in your dispatch prompt). +2. Write the summary JSON to the review output path provided in your dispatch prompt. +3. Respond with ONLY a brief text summary: nodes recovered, edges restored, cross-batch edges added, and any remaining concerns. + +Do NOT include the full JSON in your text response. diff --git a/understand-anything-plugin/agents/project-scanner.md b/understand-anything-plugin/agents/project-scanner.md index cecb17d..e8814f9 100644 --- a/understand-anything-plugin/agents/project-scanner.md +++ b/understand-anything-plugin/agents/project-scanner.md @@ -265,7 +265,7 @@ Your only task in this phase is to produce the final `description` field: 1. If `rawDescription` is non-empty, use it as the basis. Clean it up if needed (remove marketing fluff, ensure it is 1-2 sentences). 2. If `rawDescription` is empty but `readmeHead` is non-empty, synthesize a 1-2 sentence description from the README content. 3. If both are empty, use: `"No description available"` -4. If `totalFiles` > 200, append a note: `" Note: this project has over 200 source files; consider scoping analysis to a subdirectory for faster results."` +4. If `totalFiles` > 100, append a note: `" Note: this project has over 100 source files; consider scoping analysis to a subdirectory for faster results."` Then assemble the final output JSON: diff --git a/understand-anything-plugin/package.json b/understand-anything-plugin/package.json index 527b319..525c428 100644 --- a/understand-anything-plugin/package.json +++ b/understand-anything-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@understand-anything/skill", - "version": "2.1.0", + "version": "2.2.0", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/understand-anything-plugin/skills/understand/SKILL.md b/understand-anything-plugin/skills/understand/SKILL.md index f10b509..be26f59 100644 --- a/understand-anything-plugin/skills/understand/SKILL.md +++ b/understand-anything-plugin/skills/understand/SKILL.md @@ -39,9 +39,9 @@ Determine whether to run a full analysis or incremental update. - These flags only set the config — analysis proceeds normally regardless. 4. **Check for subdomain knowledge graphs to merge:** - List all `*knowledge-graph*.json` files in `$PROJECT_ROOT/.understand-anything/` **excluding** `knowledge-graph.json` itself (e.g. `frontend-knowledge-graph.json`, `backend-knowledge-graph.json`). If any subdomain graphs exist, run the merge script bundled with this skill: + List all `*knowledge-graph*.json` files in `$PROJECT_ROOT/.understand-anything/` **excluding** `knowledge-graph.json` itself (e.g. `frontend-knowledge-graph.json`, `backend-knowledge-graph.json`). If any subdomain graphs exist, run the merge script bundled with this skill (located next to this SKILL.md file — use the skill directory path, not the project root): ```bash - python ./merge-subdomain-graphs.py $PROJECT_ROOT + python /merge-subdomain-graphs.py $PROJECT_ROOT ``` The script discovers subdomain graphs, loads the existing `knowledge-graph.json` as a base (if present), and merges everything into `knowledge-graph.json` (deduplicating nodes and edges). Report the merge summary to the user, then continue with the merged graph. @@ -164,9 +164,9 @@ Fill in batch-specific parameters below and dispatch: > 2. `` ( lines, fileCategory: ``) > ... -After ALL batches complete, run the merge-and-normalize script bundled with this skill: +After ALL batches complete, run the merge-and-normalize script bundled with this skill (located next to this SKILL.md file — use the skill directory path, not the project root): ```bash -python ./merge-batch-graphs.py $PROJECT_ROOT +python /merge-batch-graphs.py $PROJECT_ROOT ``` This script reads all `batch-*.json` files from `$PROJECT_ROOT/.understand-anything/intermediate/`, then in one pass: @@ -192,7 +192,7 @@ After batches complete: 3. Write the pruned existing nodes/edges as `batch-existing.json` in the intermediate directory 4. Run the same merge script — it will combine `batch-existing.json` with the fresh `batch-*.json` files: ```bash - python ./merge-batch-graphs.py $PROJECT_ROOT + python /merge-batch-graphs.py $PROJECT_ROOT ``` --- diff --git a/understand-anything-plugin/skills/understand/merge-batch-graphs.py b/understand-anything-plugin/skills/understand/merge-batch-graphs.py index 78f9928..1896f50 100644 --- a/understand-anything-plugin/skills/understand/merge-batch-graphs.py +++ b/understand-anything-plugin/skills/understand/merge-batch-graphs.py @@ -32,12 +32,14 @@ VALID_NODE_PREFIXES = { "file", "func", "function", "class", "module", "concept", "config", "document", "service", "table", "endpoint", "pipeline", "schema", "resource", + "domain", "flow", "step", } # node.type → canonical ID prefix TYPE_TO_PREFIX: dict[str, str] = { "file": "file", "function": "function", + "func": "function", "class": "class", "module": "module", "concept": "concept", @@ -49,6 +51,9 @@ TYPE_TO_PREFIX: dict[str, str] = { "pipeline": "pipeline", "schema": "schema", "resource": "resource", + "domain": "domain", + "flow": "flow", + "step": "step", } COMPLEXITY_MAP: dict[str, str] = { @@ -349,8 +354,13 @@ def main() -> None: print(f"Error: {intermediate_dir} does not exist", file=sys.stderr) sys.exit(1) - # Discover batch files - batch_files = sorted(intermediate_dir.glob("batch-*.json")) + # Discover batch files, sorted by numeric index (not lexicographic) + batch_files = sorted( + intermediate_dir.glob("batch-*.json"), + key=lambda p: int(re.search(r"batch-(\d+)", p.stem).group(1)) + if re.search(r"batch-(\d+)", p.stem) + else 0, + ) if not batch_files: print("Error: no batch-*.json files found in intermediate/", file=sys.stderr) sys.exit(1) diff --git a/understand-anything-plugin/skills/understand/merge-subdomain-graphs.py b/understand-anything-plugin/skills/understand/merge-subdomain-graphs.py index 97a8f89..7e6de82 100644 --- a/understand-anything-plugin/skills/understand/merge-subdomain-graphs.py +++ b/understand-anything-plugin/skills/understand/merge-subdomain-graphs.py @@ -116,15 +116,26 @@ def merge_graphs(graphs: list[dict[str, Any]]) -> tuple[dict[str, Any], list[str if diff: dropped_layer_refs += diff - # ── Tour: concatenate, re-number order ──────────────────────────── + # ── Tour: concatenate, merge steps with same title ───────────────── all_tour_steps: list[dict] = [] - seen_titles: set[str] = set() + title_to_step: dict[str, dict] = {} for g in graphs: for step in g.get("tour", []): title = step.get("title", "") - if title not in seen_titles: - seen_titles.add(title) - all_tour_steps.append({**step}) + if title in title_to_step: + # Merge nodeIds from duplicate-titled steps (e.g. both + # subdomains produce a "Project Overview" step 1) + existing = title_to_step[title] + for nid in step.get("nodeIds", []): + if nid not in existing.get("nodeIds", []): + existing.setdefault("nodeIds", []).append(nid) + # Keep the longer description + if len(step.get("description", "")) > len(existing.get("description", "")): + existing["description"] = step["description"] + else: + new_step = {**step} + title_to_step[title] = new_step + all_tour_steps.append(new_step) # Drop dangling tour nodeIds and re-number dropped_tour_refs = 0