From d7d39254632fee2d17ff8599347f8cf2c4e60454 Mon Sep 17 00:00:00 2001 From: Lum1104 Date: Sat, 21 Mar 2026 23:30:47 +0800 Subject: [PATCH 1/2] docs: add language-agnostic support design document Design for making Understand-Anything language-agnostic instead of TypeScript-heavy. Covers LanguageConfig registry, GenericTreeSitterPlugin, language-aware prompts, and support for 12 languages. Co-Authored-By: Claude Opus 4.6 --- .../2026-03-21-language-agnostic-design.md | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 docs/plans/2026-03-21-language-agnostic-design.md diff --git a/docs/plans/2026-03-21-language-agnostic-design.md b/docs/plans/2026-03-21-language-agnostic-design.md new file mode 100644 index 0000000..1d0ec36 --- /dev/null +++ b/docs/plans/2026-03-21-language-agnostic-design.md @@ -0,0 +1,249 @@ +# Language-Agnostic Support Design + +**Date:** 2026-03-21 +**Status:** Approved +**Issue:** Make Understand-Anything codebase-aware and language-agnostic instead of TypeScript-heavy + +## Problem + +The tool's agent prompts, tree-sitter plugin, and language lesson system are heavily biased toward TypeScript/JavaScript. Non-TS codebases get degraded analysis because: + +1. Agent prompts use TS-specific examples and concepts (e.g., "barrel files", "type guards", "generics") +2. Tree-sitter plugin only ships TS/JS grammar support — structural analysis silently fails for other languages +3. Language lesson detection hardcodes TS-specific concept patterns and display names + +The architecture (PluginRegistry, GraphBuilder, dashboard, search) is already language-neutral. The bias is in shipped content, not the framework. + +## Decisions + +- **Scope:** All three layers — prompts, tree-sitter plugins, language framework +- **Languages (v1):** TypeScript, JavaScript, Python, Go, Java, Rust, C/C++, C#, Ruby, PHP, Swift, Kotlin +- **Architecture:** Config-first with code escape hatch (hybrid) +- **Prompt strategy:** Base prompt + per-language markdown snippet files in a `languages/` folder +- **Config location:** Prompt snippets in `skills/understand/languages/`, tree-sitter configs in `packages/core/src/languages/` +- **Multi-language projects:** Per-file language analysis + project-level multi-language summary +- **Language detection:** Auto-detect from file extensions only (no manual override for v1) + +## Design + +### 1. LanguageConfig Type & Registry + +#### LanguageConfig Interface + +```typescript +// packages/core/src/languages/types.ts +interface LanguageConfig { + id: string; // e.g., "python" + displayName: string; // e.g., "Python" + extensions: string[]; // e.g., [".py", ".pyi"] + treeSitter: { + grammarPackage: string; // npm package name + nodeTypes: { + function: string[]; // e.g., ["function_definition"] + class: string[]; // e.g., ["class_definition"] + import: string[]; // e.g., ["import_statement", "import_from_statement"] + export: string[]; // e.g., ["export_statement"] or [] for languages without exports + typeAnnotation: string[]; // e.g., ["type"] for Python type hints + }; + }; + concepts: string[]; // e.g., ["decorators", "list comprehensions", "generators"] + filePatterns?: Record; // special files, e.g., {"config": "pyproject.toml"} + customAnalyzer?: (node: SyntaxNode) => AnalysisResult; // escape hatch for unusual AST shapes +} +``` + +#### Language Registry + +```typescript +// packages/core/src/languages/registry.ts +class LanguageRegistry { + private configs: Map; + + register(config: LanguageConfig): void; + getByExtension(ext: string): LanguageConfig | null; + getById(id: string): LanguageConfig; + getAll(): LanguageConfig[]; +} +``` + +#### File Structure + +``` +packages/core/src/languages/ +├── types.ts +├── registry.ts +├── index.ts +├── configs/ +│ ├── typescript.ts +│ ├── javascript.ts +│ ├── python.ts +│ ├── go.ts +│ ├── java.ts +│ ├── rust.ts +│ ├── cpp.ts +│ ├── csharp.ts +│ ├── ruby.ts +│ ├── php.ts +│ ├── swift.ts +│ └── kotlin.ts +``` + +All built-in configs auto-registered on import. + +### 2. GenericTreeSitterPlugin + +Replaces the current TS-only `TreeSitterPlugin` with a config-driven version. + +```typescript +// packages/core/src/plugins/generic-tree-sitter-plugin.ts +class GenericTreeSitterPlugin implements AnalyzerPlugin { + private registry: LanguageRegistry; + + canAnalyze(filePath: string): boolean { + return this.registry.getByExtension(path.extname(filePath)) !== null; + } + + async analyzeFile(filePath: string, content: string): Promise { + const config = this.registry.getByExtension(path.extname(filePath)); + + // Custom analyzer escape hatch + if (config.customAnalyzer) { + return config.customAnalyzer(tree.rootNode); + } + + // Generic extraction driven by config.treeSitter.nodeTypes + const functions = this.extractNodes(tree, config.treeSitter.nodeTypes.function); + const classes = this.extractNodes(tree, config.treeSitter.nodeTypes.class); + const imports = this.extractNodes(tree, config.treeSitter.nodeTypes.import); + const exports = this.extractNodes(tree, config.treeSitter.nodeTypes.export); + // ... + } + + private extractNodes(tree: Tree, nodeTypes: string[]): NodeInfo[] { + // Walk AST, collect all nodes matching any of the given types + } +} +``` + +#### Migration + +- Current `TreeSitterPlugin` deleted, replaced by `GenericTreeSitterPlugin` + TS/JS configs +- `PluginRegistry` unchanged +- Existing tests updated to use new plugin + +#### WASM Grammar Loading + +- Each grammar loaded lazily on first use and cached +- WASM files bundled in `packages/core/src/languages/grammars/` or fetched from tree-sitter's official WASM builds + +### 3. Language-Aware Prompts + +#### File Structure + +``` +skills/understand/ +├── file-analyzer-prompt.md # Base prompt (language-neutral) +├── tour-builder-prompt.md +├── project-scanner-prompt.md +├── languages/ +│ ├── typescript.md +│ ├── javascript.md +│ ├── python.md +│ ├── go.md +│ ├── java.md +│ ├── rust.md +│ ├── cpp.md +│ ├── csharp.md +│ ├── ruby.md +│ ├── php.md +│ ├── swift.md +│ └── kotlin.md +``` + +#### Base Prompt Changes + +All TS-specific examples removed from base prompts. Replaced with injection point: + +```markdown +## Language-Specific Guidance + +{{LANGUAGE_CONTEXT}} +``` + +#### Language Markdown Format + +Each language file contains: + +```markdown +# Python + +## Key Concepts +- Decorators, comprehensions, generators, context managers, type hints, dunder methods + +## Import Patterns +- `import module`, `from module import name`, relative imports + +## Notable File Patterns +- `__init__.py` (package initializer), `conftest.py` (pytest), `pyproject.toml` (config) + +## Example Summary Style +> "FastAPI route handler that accepts a Pydantic model, validates input..." +``` + +#### Injection Logic + +1. Project scanner detects languages present in the codebase +2. File-analyzer: inject matching language `.md` for that file's language +3. Tour-builder: inject all detected languages' `.md` files +4. Project-scanner: inject all detected languages' key concepts for project-level summary + +#### Multi-Language Projects + +Project-scanner prompt gets a combined section listing all detected languages with their key concepts. + +### 4. Language Lesson Updates + +- Delete `LANGUAGE_DISPLAY_NAMES` — use `LanguageRegistry.getById(id).displayName` +- Delete hardcoded concept patterns — use `LanguageConfig.concepts` from registry +- Language lesson generation becomes config-driven + +### 5. Testing Strategy + +#### Unit Tests + +1. **LanguageConfig validation** — Each config has all required fields, non-empty nodeTypes +2. **LanguageRegistry** — Registration, lookup by extension/id, duplicate handling +3. **GenericTreeSitterPlugin per language** — Small fixture file per language verifying function/class/import extraction +4. **Language lesson generation** — Concepts sourced from config + +#### Integration Tests + +5. **Multi-language project** — Mixed TS + Python fixture, verify graph contains nodes from both languages +6. **Prompt injection** — Correct language `.md` injected based on detected language + +#### Migration Tests + +- Current tree-sitter-plugin tests rewritten for GenericTreeSitterPlugin with TS config +- Must produce identical results to validate non-breaking migration + +### 6. Error Handling & Graceful Degradation + +#### Key Principle + +**Every file always gets analyzed.** Tree-sitter is an enhancement, not a gate. The LLM is the primary analyzer; structural analysis enriches it. + +#### Unknown Language + +- Tree-sitter skipped (returns `null`) +- LLM analysis still runs — file gets summary, tags, graph node +- Debug log: `"No language config for .xyz, skipping structural analysis"` + +#### Missing WASM Grammar + +- Warning logged, that language degrades to LLM-only +- Other languages unaffected + +#### Malformed Language Config + +- Validated at registration time via Zod schema +- Invalid config throws at startup — fail fast From 87c07a61d88afe9c9a9add2d74b1ae7420d1c0a6 Mon Sep 17 00:00:00 2001 From: Lum1104 Date: Sat, 21 Mar 2026 23:37:38 +0800 Subject: [PATCH 2/2] docs: add language-agnostic implementation plan 15-task TDD plan covering LanguageConfig types, LanguageRegistry, 12 language configs, GenericTreeSitterPlugin, prompt snippets, and migration from TS-only TreeSitterPlugin. Co-Authored-By: Claude Opus 4.6 --- .../2026-03-21-language-agnostic-plan.md | 1392 +++++++++++++++++ 1 file changed, 1392 insertions(+) create mode 100644 docs/plans/2026-03-21-language-agnostic-plan.md diff --git a/docs/plans/2026-03-21-language-agnostic-plan.md b/docs/plans/2026-03-21-language-agnostic-plan.md new file mode 100644 index 0000000..16e14c4 --- /dev/null +++ b/docs/plans/2026-03-21-language-agnostic-plan.md @@ -0,0 +1,1392 @@ +# Language-Agnostic Support Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Make Understand-Anything language-agnostic by introducing a config-driven language framework, replacing the TS-only tree-sitter plugin, and creating language-aware prompts for 12 languages. + +**Architecture:** Config-first hybrid approach — each language defined by a `LanguageConfig` object (tree-sitter node mappings, concepts, extensions) plus a prompt snippet markdown file. A single `GenericTreeSitterPlugin` replaces the hardcoded TS-only plugin, driven by whichever config matches the file extension. + +**Tech Stack:** TypeScript, web-tree-sitter (WASM), Zod v4, Vitest + +--- + +### Task 1: Create LanguageConfig types and Zod schema + +**Files:** +- Create: `understand-anything-plugin/packages/core/src/languages/types.ts` + +**Step 1: Write the failing test** + +Create: `understand-anything-plugin/packages/core/src/languages/__tests__/types.test.ts` + +```typescript +import { describe, it, expect } from "vitest"; +import { LanguageConfigSchema } from "../types.js"; + +describe("LanguageConfigSchema", () => { + it("validates a complete language config", () => { + const config = { + id: "python", + displayName: "Python", + extensions: [".py", ".pyi"], + treeSitter: { + grammarPackage: "tree-sitter-python", + wasmFile: "tree-sitter-python.wasm", + nodeTypes: { + function: ["function_definition"], + class: ["class_definition"], + import: ["import_statement", "import_from_statement"], + export: [], + typeAnnotation: ["type"], + }, + }, + concepts: ["decorators", "list comprehensions", "generators"], + }; + const result = LanguageConfigSchema.safeParse(config); + expect(result.success).toBe(true); + }); + + it("rejects config missing required fields", () => { + const result = LanguageConfigSchema.safeParse({ id: "python" }); + expect(result.success).toBe(false); + }); + + it("accepts optional filePatterns", () => { + const config = { + id: "python", + displayName: "Python", + extensions: [".py"], + treeSitter: { + grammarPackage: "tree-sitter-python", + wasmFile: "tree-sitter-python.wasm", + nodeTypes: { + function: ["function_definition"], + class: ["class_definition"], + import: ["import_statement"], + export: [], + typeAnnotation: [], + }, + }, + concepts: ["decorators"], + filePatterns: { config: "pyproject.toml" }, + }; + const result = LanguageConfigSchema.safeParse(config); + expect(result.success).toBe(true); + }); +}); +``` + +**Step 2: Run test to verify it fails** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/languages/__tests__/types.test.ts` +Expected: FAIL — module `../types.js` not found + +**Step 3: Write minimal implementation** + +Create: `understand-anything-plugin/packages/core/src/languages/types.ts` + +```typescript +import { z } from "zod/v4"; + +export const TreeSitterConfigSchema = z.object({ + grammarPackage: z.string(), + wasmFile: z.string(), + nodeTypes: z.object({ + function: z.array(z.string()), + class: z.array(z.string()), + import: z.array(z.string()), + export: z.array(z.string()), + typeAnnotation: z.array(z.string()), + }), +}); + +export const LanguageConfigSchema = z.object({ + id: z.string(), + displayName: z.string(), + extensions: z.array(z.string()), + treeSitter: TreeSitterConfigSchema, + concepts: z.array(z.string()), + filePatterns: z.record(z.string(), z.string()).optional(), +}); + +export type LanguageConfig = z.infer; +export type TreeSitterConfig = z.infer; +``` + +**Step 4: Run test to verify it passes** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/languages/__tests__/types.test.ts` +Expected: PASS + +**Step 5: Commit** + +```bash +git add understand-anything-plugin/packages/core/src/languages/ +git commit -m "feat: add LanguageConfig types and Zod schema" +``` + +--- + +### Task 2: Create LanguageRegistry + +**Files:** +- Create: `understand-anything-plugin/packages/core/src/languages/registry.ts` + +**Step 1: Write the failing test** + +Create: `understand-anything-plugin/packages/core/src/languages/__tests__/registry.test.ts` + +```typescript +import { describe, it, expect } from "vitest"; +import { LanguageRegistry } from "../registry.js"; +import type { LanguageConfig } from "../types.js"; + +const pythonConfig: LanguageConfig = { + id: "python", + displayName: "Python", + extensions: [".py", ".pyi"], + treeSitter: { + grammarPackage: "tree-sitter-python", + wasmFile: "tree-sitter-python.wasm", + nodeTypes: { + function: ["function_definition"], + class: ["class_definition"], + import: ["import_statement", "import_from_statement"], + export: [], + typeAnnotation: ["type"], + }, + }, + concepts: ["decorators", "generators"], +}; + +const tsConfig: LanguageConfig = { + id: "typescript", + displayName: "TypeScript", + extensions: [".ts", ".tsx"], + treeSitter: { + grammarPackage: "tree-sitter-typescript", + wasmFile: "tree-sitter-typescript.wasm", + nodeTypes: { + function: ["function_declaration"], + class: ["class_declaration"], + import: ["import_statement"], + export: ["export_statement"], + typeAnnotation: ["type_annotation"], + }, + }, + concepts: ["generics", "type guards", "decorators"], +}; + +describe("LanguageRegistry", () => { + it("registers and retrieves a config by id", () => { + const registry = new LanguageRegistry(); + registry.register(pythonConfig); + expect(registry.getById("python")).toBe(pythonConfig); + }); + + it("retrieves config by file extension", () => { + const registry = new LanguageRegistry(); + registry.register(pythonConfig); + expect(registry.getByExtension(".py")).toBe(pythonConfig); + expect(registry.getByExtension(".pyi")).toBe(pythonConfig); + }); + + it("returns null for unknown extension", () => { + const registry = new LanguageRegistry(); + registry.register(pythonConfig); + expect(registry.getByExtension(".rs")).toBeNull(); + }); + + it("returns all registered configs", () => { + const registry = new LanguageRegistry(); + registry.register(pythonConfig); + registry.register(tsConfig); + expect(registry.getAll()).toHaveLength(2); + }); + + it("later registration overrides same id", () => { + const registry = new LanguageRegistry(); + const updated = { ...pythonConfig, displayName: "Python 3" }; + registry.register(pythonConfig); + registry.register(updated); + expect(registry.getById("python")?.displayName).toBe("Python 3"); + }); + + it("throws on invalid config", () => { + const registry = new LanguageRegistry(); + expect(() => registry.register({ id: "bad" } as LanguageConfig)).toThrow(); + }); +}); +``` + +**Step 2: Run test to verify it fails** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/languages/__tests__/registry.test.ts` +Expected: FAIL — module `../registry.js` not found + +**Step 3: Write minimal implementation** + +```typescript +// understand-anything-plugin/packages/core/src/languages/registry.ts +import { LanguageConfigSchema } from "./types.js"; +import type { LanguageConfig } from "./types.js"; + +export class LanguageRegistry { + private configs = new Map(); + private extensionMap = new Map(); + + register(config: LanguageConfig): void { + const result = LanguageConfigSchema.safeParse(config); + if (!result.success) { + throw new Error(`Invalid LanguageConfig for "${config.id}": ${result.error.message}`); + } + this.configs.set(config.id, config); + for (const ext of config.extensions) { + this.extensionMap.set(ext, config.id); + } + } + + getById(id: string): LanguageConfig | null { + return this.configs.get(id) ?? null; + } + + getByExtension(ext: string): LanguageConfig | null { + const id = this.extensionMap.get(ext); + if (!id) return null; + return this.configs.get(id) ?? null; + } + + getAll(): LanguageConfig[] { + return [...this.configs.values()]; + } +} +``` + +**Step 4: Run test to verify it passes** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/languages/__tests__/registry.test.ts` +Expected: PASS + +**Step 5: Commit** + +```bash +git add understand-anything-plugin/packages/core/src/languages/ +git commit -m "feat: add LanguageRegistry with Zod validation" +``` + +--- + +### Task 3: Create all 12 language configs + +**Files:** +- Create: `understand-anything-plugin/packages/core/src/languages/configs/typescript.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/javascript.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/python.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/go.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/java.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/rust.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/cpp.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/csharp.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/ruby.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/php.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/swift.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/kotlin.ts` +- Create: `understand-anything-plugin/packages/core/src/languages/configs/index.ts` + +**Step 1: Write the failing test** + +Create: `understand-anything-plugin/packages/core/src/languages/__tests__/configs.test.ts` + +```typescript +import { describe, it, expect } from "vitest"; +import { LanguageConfigSchema } from "../types.js"; +import { builtinConfigs } from "../configs/index.js"; + +describe("builtin language configs", () => { + it("has 12 language configs", () => { + expect(builtinConfigs).toHaveLength(12); + }); + + it("all configs pass Zod validation", () => { + for (const config of builtinConfigs) { + const result = LanguageConfigSchema.safeParse(config); + expect(result.success, `${config.id} failed validation: ${result.error?.message}`).toBe(true); + } + }); + + it("all configs have unique ids", () => { + const ids = builtinConfigs.map((c) => c.id); + expect(new Set(ids).size).toBe(ids.length); + }); + + it("no duplicate extensions across configs", () => { + const allExts: string[] = []; + for (const config of builtinConfigs) { + allExts.push(...config.extensions); + } + expect(new Set(allExts).size).toBe(allExts.length); + }); + + it("all configs have non-empty function and class node types", () => { + for (const config of builtinConfigs) { + expect(config.treeSitter.nodeTypes.function.length, `${config.id} missing function types`).toBeGreaterThan(0); + expect(config.treeSitter.nodeTypes.class.length, `${config.id} missing class types`).toBeGreaterThanOrEqual(0); + } + }); + + it("all configs have at least one concept", () => { + for (const config of builtinConfigs) { + expect(config.concepts.length, `${config.id} has no concepts`).toBeGreaterThan(0); + } + }); +}); +``` + +**Step 2: Run test to verify it fails** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/languages/__tests__/configs.test.ts` +Expected: FAIL — module not found + +**Step 3: Write all config files** + +Each config file exports a `LanguageConfig`. Here are the key ones (the rest follow the same pattern): + +**typescript.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const typescriptConfig: LanguageConfig = { + id: "typescript", + displayName: "TypeScript", + extensions: [".ts", ".tsx"], + treeSitter: { + grammarPackage: "tree-sitter-typescript", + wasmFile: "tree-sitter-typescript.wasm", + nodeTypes: { + function: ["function_declaration"], + class: ["class_declaration"], + import: ["import_statement"], + export: ["export_statement"], + typeAnnotation: ["type_annotation"], + }, + }, + concepts: [ + "generics", "type guards", "discriminated unions", "utility types", + "decorators", "enums", "interfaces", "type inference", + "mapped types", "conditional types", "template literal types", + ], + filePatterns: { config: "tsconfig.json", manifest: "package.json" }, +}; +``` + +**python.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const pythonConfig: LanguageConfig = { + id: "python", + displayName: "Python", + extensions: [".py", ".pyi"], + treeSitter: { + grammarPackage: "tree-sitter-python", + wasmFile: "tree-sitter-python.wasm", + nodeTypes: { + function: ["function_definition"], + class: ["class_definition"], + import: ["import_statement", "import_from_statement"], + export: [], + typeAnnotation: ["type"], + }, + }, + concepts: [ + "decorators", "list comprehensions", "generators", "context managers", + "type hints", "dunder methods", "metaclasses", "dataclasses", + "async/await", "descriptors", + ], + filePatterns: { config: "pyproject.toml", manifest: "setup.py" }, +}; +``` + +**go.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const goConfig: LanguageConfig = { + id: "go", + displayName: "Go", + extensions: [".go"], + treeSitter: { + grammarPackage: "tree-sitter-go", + wasmFile: "tree-sitter-go.wasm", + nodeTypes: { + function: ["function_declaration", "method_declaration"], + class: ["type_declaration"], + import: ["import_declaration"], + export: [], + typeAnnotation: [], + }, + }, + concepts: [ + "goroutines", "channels", "interfaces", "struct embedding", + "error handling patterns", "defer/panic/recover", "slices", + "pointers", "concurrency patterns", + ], + filePatterns: { config: "go.mod" }, +}; +``` + +**java.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const javaConfig: LanguageConfig = { + id: "java", + displayName: "Java", + extensions: [".java"], + treeSitter: { + grammarPackage: "tree-sitter-java", + wasmFile: "tree-sitter-java.wasm", + nodeTypes: { + function: ["method_declaration", "constructor_declaration"], + class: ["class_declaration", "interface_declaration", "enum_declaration"], + import: ["import_declaration"], + export: [], + typeAnnotation: ["type_identifier"], + }, + }, + concepts: [ + "generics", "annotations", "interfaces", "abstract classes", + "streams API", "lambdas", "sealed classes", "records", + "dependency injection", "checked exceptions", + ], + filePatterns: { config: "pom.xml", manifest: "build.gradle" }, +}; +``` + +**rust.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const rustConfig: LanguageConfig = { + id: "rust", + displayName: "Rust", + extensions: [".rs"], + treeSitter: { + grammarPackage: "tree-sitter-rust", + wasmFile: "tree-sitter-rust.wasm", + nodeTypes: { + function: ["function_item"], + class: ["struct_item", "enum_item", "impl_item", "trait_item"], + import: ["use_declaration"], + export: [], + typeAnnotation: ["type_identifier"], + }, + }, + concepts: [ + "ownership", "borrowing", "lifetimes", "traits", "pattern matching", + "enums with data", "error handling (Result/Option)", "macros", + "async/await", "unsafe blocks", "generics", "closures", + ], + filePatterns: { config: "Cargo.toml" }, +}; +``` + +**cpp.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const cppConfig: LanguageConfig = { + id: "cpp", + displayName: "C/C++", + extensions: [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp", ".hxx"], + treeSitter: { + grammarPackage: "tree-sitter-cpp", + wasmFile: "tree-sitter-cpp.wasm", + nodeTypes: { + function: ["function_definition"], + class: ["class_specifier", "struct_specifier"], + import: ["preproc_include"], + export: [], + typeAnnotation: [], + }, + }, + concepts: [ + "templates", "RAII", "smart pointers", "move semantics", + "operator overloading", "virtual functions", "namespaces", + "constexpr", "lambda expressions", "STL containers", + ], + filePatterns: { config: "CMakeLists.txt", manifest: "Makefile" }, +}; +``` + +**csharp.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const csharpConfig: LanguageConfig = { + id: "csharp", + displayName: "C#", + extensions: [".cs"], + treeSitter: { + grammarPackage: "tree-sitter-c-sharp", + wasmFile: "tree-sitter-c_sharp.wasm", + nodeTypes: { + function: ["method_declaration", "constructor_declaration"], + class: ["class_declaration", "interface_declaration", "struct_declaration", "enum_declaration", "record_declaration"], + import: ["using_directive"], + export: [], + typeAnnotation: ["type_identifier"], + }, + }, + concepts: [ + "LINQ", "async/await", "generics", "properties", + "delegates and events", "attributes", "nullable reference types", + "pattern matching", "records", "dependency injection", + ], + filePatterns: { config: "*.csproj" }, +}; +``` + +**ruby.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const rubyConfig: LanguageConfig = { + id: "ruby", + displayName: "Ruby", + extensions: [".rb", ".rake"], + treeSitter: { + grammarPackage: "tree-sitter-ruby", + wasmFile: "tree-sitter-ruby.wasm", + nodeTypes: { + function: ["method"], + class: ["class", "module"], + import: ["call"], + export: [], + typeAnnotation: [], + }, + }, + concepts: [ + "blocks and procs", "mixins", "metaprogramming", "duck typing", + "DSLs", "monkey patching", "gems", "symbols", + "method_missing", "open classes", + ], + filePatterns: { config: "Gemfile" }, +}; +``` + +**php.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const phpConfig: LanguageConfig = { + id: "php", + displayName: "PHP", + extensions: [".php"], + treeSitter: { + grammarPackage: "tree-sitter-php", + wasmFile: "tree-sitter-php.wasm", + nodeTypes: { + function: ["function_definition", "method_declaration"], + class: ["class_declaration", "interface_declaration", "trait_declaration"], + import: ["namespace_use_declaration"], + export: [], + typeAnnotation: ["type_list", "named_type"], + }, + }, + concepts: [ + "namespaces", "traits", "type declarations", "attributes", + "enums", "fibers", "closures", "magic methods", + "dependency injection", "middleware", + ], + filePatterns: { config: "composer.json" }, +}; +``` + +**swift.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const swiftConfig: LanguageConfig = { + id: "swift", + displayName: "Swift", + extensions: [".swift"], + treeSitter: { + grammarPackage: "tree-sitter-swift", + wasmFile: "tree-sitter-swift.wasm", + nodeTypes: { + function: ["function_declaration", "init_declaration"], + class: ["class_declaration", "struct_declaration", "protocol_declaration", "enum_declaration"], + import: ["import_declaration"], + export: [], + typeAnnotation: ["type_annotation"], + }, + }, + concepts: [ + "optionals", "protocols", "extensions", "generics", + "closures", "property wrappers", "result builders", + "actors", "structured concurrency", "value types vs reference types", + ], + filePatterns: { config: "Package.swift" }, +}; +``` + +**kotlin.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const kotlinConfig: LanguageConfig = { + id: "kotlin", + displayName: "Kotlin", + extensions: [".kt", ".kts"], + treeSitter: { + grammarPackage: "tree-sitter-kotlin", + wasmFile: "tree-sitter-kotlin.wasm", + nodeTypes: { + function: ["function_declaration"], + class: ["class_declaration", "object_declaration", "interface_declaration"], + import: ["import_header"], + export: [], + typeAnnotation: ["type_identifier"], + }, + }, + concepts: [ + "coroutines", "data classes", "sealed classes", "extension functions", + "null safety", "delegation", "DSL builders", "inline functions", + "companion objects", "flow", + ], + filePatterns: { config: "build.gradle.kts" }, +}; +``` + +**javascript.ts:** +```typescript +import type { LanguageConfig } from "../types.js"; + +export const javascriptConfig: LanguageConfig = { + id: "javascript", + displayName: "JavaScript", + extensions: [".js", ".mjs", ".cjs", ".jsx"], + treeSitter: { + grammarPackage: "tree-sitter-javascript", + wasmFile: "tree-sitter-javascript.wasm", + nodeTypes: { + function: ["function_declaration"], + class: ["class_declaration"], + import: ["import_statement"], + export: ["export_statement"], + typeAnnotation: [], + }, + }, + concepts: [ + "closures", "prototypes", "promises", "async/await", + "event loop", "destructuring", "spread operator", + "proxies", "generators", "modules (ESM/CJS)", + ], + filePatterns: { config: "package.json" }, +}; +``` + +**configs/index.ts:** +```typescript +import { typescriptConfig } from "./typescript.js"; +import { javascriptConfig } from "./javascript.js"; +import { pythonConfig } from "./python.js"; +import { goConfig } from "./go.js"; +import { javaConfig } from "./java.js"; +import { rustConfig } from "./rust.js"; +import { cppConfig } from "./cpp.js"; +import { csharpConfig } from "./csharp.js"; +import { rubyConfig } from "./ruby.js"; +import { phpConfig } from "./php.js"; +import { swiftConfig } from "./swift.js"; +import { kotlinConfig } from "./kotlin.js"; +import type { LanguageConfig } from "../types.js"; + +export const builtinConfigs: LanguageConfig[] = [ + typescriptConfig, + javascriptConfig, + pythonConfig, + goConfig, + javaConfig, + rustConfig, + cppConfig, + csharpConfig, + rubyConfig, + phpConfig, + swiftConfig, + kotlinConfig, +]; +``` + +**Step 4: Run test to verify it passes** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/languages/__tests__/configs.test.ts` +Expected: PASS + +**Step 5: Commit** + +```bash +git add understand-anything-plugin/packages/core/src/languages/configs/ +git commit -m "feat: add 12 builtin language configs" +``` + +--- + +### Task 4: Create languages/index.ts barrel and export from core + +**Files:** +- Create: `understand-anything-plugin/packages/core/src/languages/index.ts` +- Modify: `understand-anything-plugin/packages/core/src/index.ts` + +**Step 1: Create barrel export** + +```typescript +// understand-anything-plugin/packages/core/src/languages/index.ts +export { LanguageRegistry } from "./registry.js"; +export { LanguageConfigSchema } from "./types.js"; +export type { LanguageConfig, TreeSitterConfig } from "./types.js"; +export { builtinConfigs } from "./configs/index.js"; +``` + +**Step 2: Add export to core index.ts** + +Add to `understand-anything-plugin/packages/core/src/index.ts`: + +```typescript +// Languages +export { LanguageRegistry, builtinConfigs, LanguageConfigSchema } from "./languages/index.js"; +export type { LanguageConfig, TreeSitterConfig } from "./languages/index.js"; +``` + +**Step 3: Build and verify** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core build` +Expected: Build succeeds with no errors + +**Step 4: Commit** + +```bash +git add understand-anything-plugin/packages/core/src/languages/index.ts understand-anything-plugin/packages/core/src/index.ts +git commit -m "feat: export language types and registry from core" +``` + +--- + +### Task 5: Install tree-sitter WASM grammar packages + +**Files:** +- Modify: `understand-anything-plugin/packages/core/package.json` + +**Step 1: Install new grammar packages** + +Run: +```bash +cd understand-anything-plugin && pnpm --filter @understand-anything/core add \ + tree-sitter-python \ + tree-sitter-go \ + tree-sitter-java \ + tree-sitter-rust \ + tree-sitter-cpp \ + tree-sitter-c-sharp \ + tree-sitter-ruby \ + tree-sitter-php \ + tree-sitter-swift \ + tree-sitter-kotlin +``` + +Note: Some grammar packages may not ship `.wasm` files. For those, we need to check availability and potentially build from source or use the `tree-sitter` CLI to generate WASM. Verify each package after install: + +```bash +cd understand-anything-plugin && for lang in python go java rust cpp c-sharp ruby php swift kotlin; do + echo "=== tree-sitter-$lang ===" + ls node_modules/tree-sitter-$lang/*.wasm 2>/dev/null || echo "NO WASM FOUND" +done +``` + +For packages without pre-built WASM, use `tree-sitter build --wasm` to compile them, or find alternative npm packages that ship WASM builds. Document which packages needed manual WASM generation. + +**Step 2: Verify build still passes** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core build` +Expected: PASS + +**Step 3: Commit** + +```bash +git add understand-anything-plugin/packages/core/package.json understand-anything-plugin/pnpm-lock.yaml +git commit -m "feat: add tree-sitter grammar packages for 10 new languages" +``` + +--- + +### Task 6: Build GenericTreeSitterPlugin + +**Files:** +- Create: `understand-anything-plugin/packages/core/src/plugins/generic-tree-sitter-plugin.ts` + +**Step 1: Write the failing test** + +Create: `understand-anything-plugin/packages/core/src/plugins/generic-tree-sitter-plugin.test.ts` + +```typescript +import { describe, it, expect, beforeAll } from "vitest"; +import { GenericTreeSitterPlugin } from "./generic-tree-sitter-plugin.js"; +import { LanguageRegistry } from "../languages/registry.js"; +import { typescriptConfig } from "../languages/configs/typescript.js"; +import { javascriptConfig } from "../languages/configs/javascript.js"; +import { pythonConfig } from "../languages/configs/python.js"; + +describe("GenericTreeSitterPlugin", () => { + let plugin: GenericTreeSitterPlugin; + + beforeAll(async () => { + const registry = new LanguageRegistry(); + registry.register(typescriptConfig); + registry.register(javascriptConfig); + registry.register(pythonConfig); + plugin = new GenericTreeSitterPlugin(registry); + await plugin.init(); + }); + + describe("TypeScript (migration parity)", () => { + it("extracts function declarations", () => { + const code = ` +function greet(name: string): string { + return "Hello " + name; +} +`; + const result = plugin.analyzeFile("test.ts", code); + expect(result.functions).toHaveLength(1); + expect(result.functions[0].name).toBe("greet"); + }); + + it("extracts class declarations", () => { + const code = ` +class UserService { + getName(): string { return "test"; } +} +`; + const result = plugin.analyzeFile("test.ts", code); + expect(result.classes).toHaveLength(1); + expect(result.classes[0].name).toBe("UserService"); + }); + + it("extracts imports", () => { + const code = `import { readFile } from "fs";`; + const result = plugin.analyzeFile("test.ts", code); + expect(result.imports).toHaveLength(1); + expect(result.imports[0].source).toBe("fs"); + }); + + it("extracts exports", () => { + const code = `export function hello() {}`; + const result = plugin.analyzeFile("test.ts", code); + expect(result.exports.length).toBeGreaterThanOrEqual(1); + }); + + it("extracts arrow functions", () => { + const code = `const add = (a: number, b: number): number => a + b;`; + const result = plugin.analyzeFile("test.ts", code); + expect(result.functions).toHaveLength(1); + expect(result.functions[0].name).toBe("add"); + }); + }); + + describe("Python", () => { + it("extracts function definitions", () => { + const code = ` +def greet(name): + return f"Hello {name}" + +def add(a, b): + return a + b +`; + const result = plugin.analyzeFile("test.py", code); + expect(result.functions).toHaveLength(2); + expect(result.functions[0].name).toBe("greet"); + expect(result.functions[1].name).toBe("add"); + }); + + it("extracts class definitions", () => { + const code = ` +class UserService: + def get_name(self): + return "test" +`; + const result = plugin.analyzeFile("test.py", code); + expect(result.classes).toHaveLength(1); + expect(result.classes[0].name).toBe("UserService"); + }); + + it("extracts import statements", () => { + const code = ` +import os +from pathlib import Path +from typing import Optional +`; + const result = plugin.analyzeFile("test.py", code); + expect(result.imports).toHaveLength(3); + }); + }); + + it("returns null for unsupported file extension", () => { + expect(plugin.canAnalyze("test.unknown")).toBe(false); + }); + + it("reports all registered languages", () => { + const langs = plugin.supportedLanguages(); + expect(langs).toContain("typescript"); + expect(langs).toContain("python"); + }); +}); +``` + +**Step 2: Run test to verify it fails** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/plugins/generic-tree-sitter-plugin.test.ts` +Expected: FAIL — module not found + +**Step 3: Write implementation** + +Create `understand-anything-plugin/packages/core/src/plugins/generic-tree-sitter-plugin.ts`: + +This file implements a `GenericTreeSitterPlugin` that: +- Takes a `LanguageRegistry` in the constructor +- In `init()`, lazily loads WASM grammars per language using `require.resolve(config.treeSitter.grammarPackage + '/' + config.treeSitter.wasmFile)` +- In `analyzeFile()`, determines language from extension via registry, then walks the AST using `config.treeSitter.nodeTypes` to extract functions/classes/imports/exports +- Reuses the same helper patterns from the old `TreeSitterPlugin` (traverse, getStringValue, extractParams) but driven by config instead of hardcoded node types +- Implements `resolveImports()` and `extractCallGraph()` with the same logic as before + +Key implementation notes: +- The `extractNodes()` method walks the AST and matches nodes against `nodeTypes.function`, `nodeTypes.class`, etc. +- For TS/JS, also handle `lexical_declaration`/`variable_declaration` with arrow function values (existing behavior) +- For import extraction, use the same `getStringValue()` approach but match against language-specific import node types +- For export extraction, same pattern matching against export node types +- Grammar loading: try `require.resolve()` first; if WASM not found, log warning and skip that language + +**Step 4: Run test to verify it passes** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/plugins/generic-tree-sitter-plugin.test.ts` +Expected: PASS + +**Step 5: Run old TreeSitterPlugin tests with new plugin to verify migration parity** + +Ensure the existing `tree-sitter-plugin.test.ts` test cases also pass with `GenericTreeSitterPlugin` + TS/JS configs. + +**Step 6: Commit** + +```bash +git add understand-anything-plugin/packages/core/src/plugins/generic-tree-sitter-plugin.ts +git add understand-anything-plugin/packages/core/src/plugins/generic-tree-sitter-plugin.test.ts +git commit -m "feat: add GenericTreeSitterPlugin driven by LanguageConfig" +``` + +--- + +### Task 7: Add per-language test fixtures for remaining languages + +**Files:** +- Modify: `understand-anything-plugin/packages/core/src/plugins/generic-tree-sitter-plugin.test.ts` + +**Step 1: Add test cases for Go, Java, Rust, C++, C#, Ruby, PHP, Swift, Kotlin** + +For each language, add a `describe` block with a small fixture testing function/class/import extraction. Example for Go: + +```typescript +describe("Go", () => { + it("extracts function declarations", () => { + const code = ` +package main + +func greet(name string) string { + return "Hello " + name +} +`; + const result = plugin.analyzeFile("test.go", code); + expect(result.functions).toHaveLength(1); + expect(result.functions[0].name).toBe("greet"); + }); + + it("extracts type declarations", () => { + const code = ` +package main + +type UserService struct { + Name string +} +`; + const result = plugin.analyzeFile("test.go", code); + expect(result.classes).toHaveLength(1); + }); + + it("extracts imports", () => { + const code = ` +package main + +import ( + "fmt" + "os" +) +`; + const result = plugin.analyzeFile("test.go", code); + expect(result.imports).toHaveLength(2); + }); +}); +``` + +Follow same pattern for each language with appropriate syntax. Each test uses ~10-20 lines of idiomatic code. + +Note: Some WASM grammars may not be available. For languages where the grammar fails to load, register them in the `beforeAll` with a try/catch and use `it.skipIf()` to conditionally skip tests. This prevents CI failures while still testing what's available. + +**Step 2: Run all tests** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/plugins/generic-tree-sitter-plugin.test.ts` +Expected: PASS for all languages with available grammars + +**Step 3: Commit** + +```bash +git add understand-anything-plugin/packages/core/src/plugins/generic-tree-sitter-plugin.test.ts +git commit -m "test: add per-language fixtures for GenericTreeSitterPlugin" +``` + +--- + +### Task 8: Replace TreeSitterPlugin with GenericTreeSitterPlugin + +**Files:** +- Modify: `understand-anything-plugin/packages/core/src/index.ts` +- Modify: `understand-anything-plugin/packages/core/src/plugins/registry.ts` +- Delete: `understand-anything-plugin/packages/core/src/plugins/tree-sitter-plugin.ts` (after confirming no other imports) + +**Step 1: Update core exports** + +In `understand-anything-plugin/packages/core/src/index.ts`: +- Replace `export { TreeSitterPlugin }` with `export { GenericTreeSitterPlugin }` +- Also export `GenericTreeSitterPlugin` as `TreeSitterPlugin` for backward compat if needed (check consumers) + +**Step 2: Update PluginRegistry extension map** + +In `understand-anything-plugin/packages/core/src/plugins/registry.ts`: +- The `EXTENSION_TO_LANGUAGE` map is already comprehensive (has py, go, rs, etc.) +- No changes needed here — the registry just dispatches to whatever plugin is registered + +**Step 3: Update all imports in skill source** + +Search for all imports of `TreeSitterPlugin` across the codebase: + +Run: `grep -r "TreeSitterPlugin" understand-anything-plugin/` + +Update each import to use `GenericTreeSitterPlugin`. The main consumers are: +- `understand-anything-plugin/packages/core/src/index.ts` +- Any skill source files that instantiate the plugin + +**Step 4: Delete old TreeSitterPlugin** + +Once all imports are updated and tests pass: + +Run: `rm understand-anything-plugin/packages/core/src/plugins/tree-sitter-plugin.ts` + +Keep the old test file temporarily — rename it to verify parity. + +**Step 5: Run full test suite** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test` +Expected: ALL PASS + +**Step 6: Commit** + +```bash +git add -A +git commit -m "refactor: replace TreeSitterPlugin with GenericTreeSitterPlugin" +``` + +--- + +### Task 9: Update language-lesson.ts to use LanguageRegistry + +**Files:** +- Modify: `understand-anything-plugin/packages/core/src/analyzer/language-lesson.ts` +- Modify: `understand-anything-plugin/packages/core/src/__tests__/language-lesson.test.ts` + +**Step 1: Update the test** + +Update `language-lesson.test.ts` to verify concepts come from the registry: + +```typescript +it("detects concepts from language config", () => { + const node = { + ...sampleNode, + summary: "Uses decorators and async/await with generators", + tags: ["decorators"], + }; + const concepts = detectLanguageConcepts(node, "python"); + expect(concepts).toContain("decorators"); + expect(concepts).toContain("async/await"); +}); +``` + +**Step 2: Run test to verify it fails (or passes with old behavior)** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/__tests__/language-lesson.test.ts` + +**Step 3: Update implementation** + +In `language-lesson.ts`: +- Import `LanguageRegistry` and `builtinConfigs` +- Create a module-level registry instance, pre-populated with builtinConfigs +- Replace `LANGUAGE_DISPLAY_NAMES` lookups with `registry.getById(lang)?.displayName` +- Replace hardcoded `CONCEPT_PATTERNS` with `registry.getById(lang)?.concepts` merged with generic patterns (async/await, error handling, etc. that apply to all languages) +- Keep the detection logic (search tags/summary for concept keywords) but source keywords from the config + +**Step 4: Run test to verify it passes** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test -- --run src/__tests__/language-lesson.test.ts` +Expected: PASS + +**Step 5: Run full test suite** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test` +Expected: ALL PASS + +**Step 6: Commit** + +```bash +git add understand-anything-plugin/packages/core/src/analyzer/language-lesson.ts +git add understand-anything-plugin/packages/core/src/__tests__/language-lesson.test.ts +git commit -m "refactor: source language concepts from LanguageRegistry" +``` + +--- + +### Task 10: Create language prompt snippet files + +**Files:** +- Create: `understand-anything-plugin/skills/understand/languages/typescript.md` +- Create: `understand-anything-plugin/skills/understand/languages/javascript.md` +- Create: `understand-anything-plugin/skills/understand/languages/python.md` +- Create: `understand-anything-plugin/skills/understand/languages/go.md` +- Create: `understand-anything-plugin/skills/understand/languages/java.md` +- Create: `understand-anything-plugin/skills/understand/languages/rust.md` +- Create: `understand-anything-plugin/skills/understand/languages/cpp.md` +- Create: `understand-anything-plugin/skills/understand/languages/csharp.md` +- Create: `understand-anything-plugin/skills/understand/languages/ruby.md` +- Create: `understand-anything-plugin/skills/understand/languages/php.md` +- Create: `understand-anything-plugin/skills/understand/languages/swift.md` +- Create: `understand-anything-plugin/skills/understand/languages/kotlin.md` + +**Step 1: Create all 12 language markdown files** + +Each file follows this structure: + +```markdown +# [Language Name] + +## Key Concepts +- [5-10 language-specific concepts with brief explanations] + +## Import Patterns +- [All common import syntax patterns for this language] + +## Notable File Patterns +- [Special files like __init__.py, go.mod, Cargo.toml, etc.] + +## Common Frameworks +- [Top 3-5 frameworks/libraries in this ecosystem] + +## Example Summary Style +> "[Example of how to summarize a function/class in this language's idiom]" +``` + +Each file should be 30-50 lines, with content specific to that language's ecosystem and idioms. The content should help the LLM produce better analysis by understanding language-specific patterns. + +**Step 2: Verify files are well-formed** + +Manually review each file for accuracy and completeness. + +**Step 3: Commit** + +```bash +git add understand-anything-plugin/skills/understand/languages/ +git commit -m "feat: add language-specific prompt snippet files for 12 languages" +``` + +--- + +### Task 11: Make base prompts language-neutral with injection points + +**Files:** +- Modify: `understand-anything-plugin/skills/understand/file-analyzer-prompt.md` +- Modify: `understand-anything-plugin/skills/understand/tour-builder-prompt.md` +- Modify: `understand-anything-plugin/skills/understand/project-scanner-prompt.md` + +**Step 1: Update file-analyzer-prompt.md** + +- Remove all TypeScript-specific examples (e.g., "TypeScript barrel file", type guard references) +- Replace TS-specific concept lists with generic placeholders +- Add injection point: + +```markdown +## Language-Specific Guidance + +{{LANGUAGE_CONTEXT}} +``` + +- Make the Phase 1 script detection language-aware (not just "Node.js recommended") + +**Step 2: Update tour-builder-prompt.md** + +- Remove TS-specific language lesson examples ("generics, discriminated unions, utility types") +- Replace with injection point for detected languages: + +```markdown +## Language-Specific Concepts + +{{LANGUAGE_CONTEXT}} +``` + +**Step 3: Update project-scanner-prompt.md** + +- Remove `tsconfig.json` hardcoded check +- Make framework detection generic (inject detected languages' framework lists) +- Add multi-language section: + +```markdown +## Detected Languages + +{{LANGUAGE_CONTEXT}} +``` + +**Step 4: Verify prompts are well-formed** + +Read each modified prompt to ensure it's coherent with injection points and no residual TS bias. + +**Step 5: Commit** + +```bash +git add understand-anything-plugin/skills/understand/file-analyzer-prompt.md +git add understand-anything-plugin/skills/understand/tour-builder-prompt.md +git add understand-anything-plugin/skills/understand/project-scanner-prompt.md +git commit -m "refactor: make agent prompts language-neutral with injection points" +``` + +--- + +### Task 12: Implement prompt injection logic in skill source + +**Files:** +- Modify: `understand-anything-plugin/skills/understand/SKILL.md` (the `/understand` skill definition) + +**Step 1: Update the skill orchestration** + +In the `/understand` skill (SKILL.md), update the agent dispatch logic: + +- **Phase 0 (Pre-flight):** After scanning files, detect languages present and load corresponding `languages/*.md` files +- **Phase 2 (File Analyzer dispatch):** For each file batch, inject the matching language's `.md` content into the file-analyzer prompt's `{{LANGUAGE_CONTEXT}}` placeholder +- **Phase 4 (Architecture Analyzer):** Inject all detected languages' concepts +- **Phase 5 (Tour Builder):** Inject all detected languages' `.md` content into the `{{LANGUAGE_CONTEXT}}` placeholder +- **Phase 1 (Project Scanner):** Inject all detected languages' `.md` content + +The injection logic: +1. Map file extensions to language IDs (reuse `LanguageRegistry.getByExtension()`) +2. Read the corresponding `languages/.md` file +3. Replace `{{LANGUAGE_CONTEXT}}` in the base prompt with the file contents + +For multi-language projects, concatenate all detected language files. + +**Step 2: Verify by reading modified SKILL.md** + +Ensure the orchestration flow includes language detection and prompt injection steps. + +**Step 3: Commit** + +```bash +git add understand-anything-plugin/skills/understand/SKILL.md +git commit -m "feat: add language detection and prompt injection to /understand skill" +``` + +--- + +### Task 13: Update old tree-sitter-plugin test to use GenericTreeSitterPlugin + +**Files:** +- Modify or Delete: `understand-anything-plugin/packages/core/src/plugins/tree-sitter-plugin.test.ts` + +**Step 1: Migrate or delete** + +If the old `tree-sitter-plugin.test.ts` still exists: +- Either update it to import `GenericTreeSitterPlugin` and instantiate with a `LanguageRegistry` containing TS/JS configs +- Or delete it if all its test cases are covered in `generic-tree-sitter-plugin.test.ts` + +Prefer deleting to avoid duplication. + +**Step 2: Run full test suite** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test` +Expected: ALL PASS + +**Step 3: Commit** + +```bash +git add -A +git commit -m "test: migrate old tree-sitter-plugin tests to generic plugin" +``` + +--- + +### Task 14: Build and lint verification + +**Step 1: Build core** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core build` +Expected: PASS + +**Step 2: Build skill package** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/skill build` +Expected: PASS + +**Step 3: Build dashboard** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/dashboard build` +Expected: PASS (dashboard doesn't import language modules directly) + +**Step 4: Run lint** + +Run: `cd understand-anything-plugin && pnpm lint` +Expected: PASS (or fix any lint issues) + +**Step 5: Run all tests** + +Run: `cd understand-anything-plugin && pnpm --filter @understand-anything/core test && pnpm --filter @understand-anything/skill test` +Expected: ALL PASS + +**Step 6: Commit any fixes** + +```bash +git add -A +git commit -m "fix: resolve build and lint issues from language-agnostic refactor" +``` + +--- + +### Task 15: Update CLAUDE.md and documentation + +**Files:** +- Modify: `CLAUDE.md` +- Modify: `README.md` (if it exists and mentions TS-only support) + +**Step 1: Update CLAUDE.md** + +Add to the Architecture section: +- Mention the `languages/` directories (both in core and skills) +- Document how to add a new language (create config + prompt snippet) +- List supported languages + +**Step 2: Commit** + +```bash +git add CLAUDE.md +git commit -m "docs: update CLAUDE.md with language-agnostic architecture" +```