1393 lines
44 KiB
Markdown
1393 lines
44 KiB
Markdown
# 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<typeof LanguageConfigSchema>;
|
|
export type TreeSitterConfig = z.infer<typeof TreeSitterConfigSchema>;
|
|
```
|
|
|
|
**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<string, LanguageConfig>();
|
|
private extensionMap = new Map<string, string>();
|
|
|
|
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/<id>.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"
|
|
```
|