From b5eb2e60414495385e93539080ebbd6f61d2d7c6 Mon Sep 17 00:00:00 2001 From: Sreeram Date: Mon, 23 Mar 2026 12:35:09 +0530 Subject: [PATCH] feat: language-agnostic analysis with config-driven registry and framework detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the TypeScript/JavaScript-biased analysis pipeline with a truly language-agnostic system. The core architecture was already language-neutral (graph schema, dashboard, search) — the bias lived in agent prompts, tree-sitter plugin, and language-lesson system. Core changes: - LanguageConfig + FrameworkConfig types with Zod validation - LanguageRegistry (12 languages) and FrameworkRegistry (10 frameworks) - Config-driven TreeSitterPlugin replacing hardcoded TS/JS grammars - PluginRegistry now delegates to LanguageRegistry for extension mapping - Language-lesson system uses config for display names and concepts Prompt system: - SKILL.md generalized: dynamic injection of language snippets and framework addendums instead of hardcoded if/else conditionals - 12 language prompt snippets (languages/*.md) with concepts, patterns, frameworks per language - 10 framework addendums (frameworks/*.md) with canonical file roles, edge patterns, architectural layers — Django/FastAPI/Flask preserved and split, plus React/Next.js/Express/Vue/Spring/Rails/Gin added - Extended entry points, directory patterns, and test patterns across all 12 language ecosystems in base prompts --- CLAUDE.md | 33 +++ .../packages/core/package.json | 4 + .../src/__tests__/framework-registry.test.ts | 99 +++++++++ .../src/__tests__/language-lesson.test.ts | 2 + .../src/__tests__/language-registry.test.ts | 91 ++++++++ .../core/src/analyzer/language-lesson.ts | 58 ++++- .../packages/core/src/index.ts | 14 ++ .../core/src/languages/configs/cpp.ts | 25 +++ .../core/src/languages/configs/csharp.ts | 25 +++ .../packages/core/src/languages/configs/go.ts | 24 ++ .../core/src/languages/configs/index.ts | 43 ++++ .../core/src/languages/configs/java.ts | 29 +++ .../core/src/languages/configs/javascript.ts | 43 ++++ .../core/src/languages/configs/kotlin.ts | 25 +++ .../core/src/languages/configs/php.ts | 25 +++ .../core/src/languages/configs/python.ts | 40 ++++ .../core/src/languages/configs/ruby.ts | 24 ++ .../core/src/languages/configs/rust.ts | 27 +++ .../core/src/languages/configs/swift.ts | 25 +++ .../core/src/languages/configs/typescript.ts | 48 ++++ .../core/src/languages/framework-registry.ts | 80 +++++++ .../core/src/languages/frameworks/django.ts | 36 +++ .../core/src/languages/frameworks/express.ts | 26 +++ .../core/src/languages/frameworks/fastapi.ts | 25 +++ .../core/src/languages/frameworks/flask.ts | 31 +++ .../core/src/languages/frameworks/gin.ts | 19 ++ .../core/src/languages/frameworks/index.ts | 38 ++++ .../core/src/languages/frameworks/nextjs.ts | 23 ++ .../core/src/languages/frameworks/rails.ts | 28 +++ .../core/src/languages/frameworks/react.ts | 19 ++ .../core/src/languages/frameworks/spring.ts | 27 +++ .../core/src/languages/frameworks/vue.ts | 19 ++ .../packages/core/src/languages/index.ts | 22 ++ .../core/src/languages/language-registry.ts | 53 +++++ .../packages/core/src/languages/types.ts | 54 +++++ .../packages/core/src/plugins/registry.ts | 42 ++-- .../core/src/plugins/tree-sitter-plugin.ts | 175 +++++++++++---- .../understand/PYTHON-SUPPORT-CHANGES.md | 208 ------------------ .../skills/understand/SKILL.md | 38 ++-- .../architecture-analyzer-prompt.md | 20 +- .../skills/understand/file-analyzer-prompt.md | 8 +- .../django.md} | 0 .../skills/understand/frameworks/express.md | 57 +++++ .../fastapi.md} | 59 +---- .../skills/understand/frameworks/flask.md | 53 +++++ .../skills/understand/frameworks/gin.md | 59 +++++ .../skills/understand/frameworks/nextjs.md | 59 +++++ .../skills/understand/frameworks/rails.md | 65 ++++++ .../skills/understand/frameworks/react.md | 55 +++++ .../skills/understand/frameworks/spring.md | 59 +++++ .../skills/understand/frameworks/vue.md | 59 +++++ .../skills/understand/languages/cpp.md | 47 ++++ .../skills/understand/languages/csharp.md | 46 ++++ .../skills/understand/languages/go.md | 47 ++++ .../skills/understand/languages/java.md | 45 ++++ .../skills/understand/languages/javascript.md | 46 ++++ .../skills/understand/languages/kotlin.md | 45 ++++ .../skills/understand/languages/php.md | 46 ++++ .../skills/understand/languages/python.md | 48 ++++ .../skills/understand/languages/ruby.md | 46 ++++ .../skills/understand/languages/rust.md | 47 ++++ .../skills/understand/languages/swift.md | 46 ++++ .../skills/understand/languages/typescript.md | 46 ++++ .../understand/project-scanner-prompt.md | 6 +- .../skills/understand/tour-builder-prompt.md | 2 +- 65 files changed, 2421 insertions(+), 362 deletions(-) create mode 100644 understand-anything-plugin/packages/core/src/__tests__/framework-registry.test.ts create mode 100644 understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/cpp.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/csharp.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/go.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/index.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/java.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/javascript.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/kotlin.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/php.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/python.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/ruby.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/rust.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/swift.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/configs/typescript.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/framework-registry.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/django.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/express.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/fastapi.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/flask.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/gin.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/index.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/nextjs.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/rails.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/react.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/spring.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/frameworks/vue.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/index.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/language-registry.ts create mode 100644 understand-anything-plugin/packages/core/src/languages/types.ts delete mode 100644 understand-anything-plugin/skills/understand/PYTHON-SUPPORT-CHANGES.md rename understand-anything-plugin/skills/understand/{django-analyzer-addendum.md => frameworks/django.md} (100%) create mode 100644 understand-anything-plugin/skills/understand/frameworks/express.md rename understand-anything-plugin/skills/understand/{fastapi-analyzer-addendum.md => frameworks/fastapi.md} (55%) create mode 100644 understand-anything-plugin/skills/understand/frameworks/flask.md create mode 100644 understand-anything-plugin/skills/understand/frameworks/gin.md create mode 100644 understand-anything-plugin/skills/understand/frameworks/nextjs.md create mode 100644 understand-anything-plugin/skills/understand/frameworks/rails.md create mode 100644 understand-anything-plugin/skills/understand/frameworks/react.md create mode 100644 understand-anything-plugin/skills/understand/frameworks/spring.md create mode 100644 understand-anything-plugin/skills/understand/frameworks/vue.md create mode 100644 understand-anything-plugin/skills/understand/languages/cpp.md create mode 100644 understand-anything-plugin/skills/understand/languages/csharp.md create mode 100644 understand-anything-plugin/skills/understand/languages/go.md create mode 100644 understand-anything-plugin/skills/understand/languages/java.md create mode 100644 understand-anything-plugin/skills/understand/languages/javascript.md create mode 100644 understand-anything-plugin/skills/understand/languages/kotlin.md create mode 100644 understand-anything-plugin/skills/understand/languages/php.md create mode 100644 understand-anything-plugin/skills/understand/languages/python.md create mode 100644 understand-anything-plugin/skills/understand/languages/ruby.md create mode 100644 understand-anything-plugin/skills/understand/languages/rust.md create mode 100644 understand-anything-plugin/skills/understand/languages/swift.md create mode 100644 understand-anything-plugin/skills/understand/languages/typescript.md diff --git a/CLAUDE.md b/CLAUDE.md index 02ae299..443042e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,3 +46,36 @@ An open-source tool combining LLM intelligence + static analysis to produce inte When pushing to remote, bump the version in **both** of these files (keep them in sync): - `understand-anything-plugin/package.json` → `"version"` field - `.claude-plugin/marketplace.json` → `plugins[0].version` field + +## Testing Local Plugin Changes + +Claude Code caches installed plugins at `~/.claude/plugins/cache/understand-anything/understand-anything//`. Symlinks don't work because Claude's Search/Glob tools can't follow them. To test local changes: + +1. **Build the packages:** + ```bash + pnpm --filter @understand-anything/core build + pnpm --filter @understand-anything/skill build + ``` + +2. **Find the installed version** (must match what the marketplace currently serves): + ```bash + ls ~/.claude/plugins/cache/understand-anything/understand-anything/ + ``` + +3. **Copy your local plugin into the cache**, replacing `` with the version from step 2: + ```bash + rm -rf ~/.claude/plugins/cache/understand-anything/understand-anything/ + cp -R ./understand-anything-plugin ~/.claude/plugins/cache/understand-anything/understand-anything/ + ``` + +4. **Start a fresh Claude Code session** (existing sessions cache the old prompts in context). + +5. **Run `/understand --full`** in the target project to verify. + +**Re-sync after further changes:** +```bash +pnpm --filter @understand-anything/core build && \ +cp -R ./understand-anything-plugin/* ~/.claude/plugins/cache/understand-anything/understand-anything// +``` + +**To revert to upstream:** Uninstall and reinstall the plugin from the marketplace — it repopulates the cache from the upstream repo. diff --git a/understand-anything-plugin/packages/core/package.json b/understand-anything-plugin/packages/core/package.json index f26319b..1609792 100644 --- a/understand-anything-plugin/packages/core/package.json +++ b/understand-anything-plugin/packages/core/package.json @@ -20,6 +20,10 @@ "./schema": { "types": "./dist/schema.d.ts", "default": "./dist/schema.js" + }, + "./languages": { + "types": "./dist/languages/index.d.ts", + "default": "./dist/languages/index.js" } }, "scripts": { diff --git a/understand-anything-plugin/packages/core/src/__tests__/framework-registry.test.ts b/understand-anything-plugin/packages/core/src/__tests__/framework-registry.test.ts new file mode 100644 index 0000000..3fdc588 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/__tests__/framework-registry.test.ts @@ -0,0 +1,99 @@ +import { describe, it, expect } from "vitest"; +import { FrameworkRegistry } from "../languages/framework-registry.js"; +import { djangoConfig } from "../languages/frameworks/django.js"; +import { reactConfig } from "../languages/frameworks/react.js"; + +describe("FrameworkRegistry", () => { + it("registers and retrieves a framework config by id", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + expect(registry.getById("django")?.displayName).toBe("Django"); + }); + + it("retrieves frameworks for a language", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + registry.register(reactConfig); + const pythonFrameworks = registry.getForLanguage("python"); + expect(pythonFrameworks).toHaveLength(1); + expect(pythonFrameworks[0].id).toBe("django"); + }); + + it("returns empty array for unknown language", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + expect(registry.getForLanguage("haskell")).toEqual([]); + }); + + describe("detectFrameworks", () => { + it("detects Django from requirements.txt", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + const detected = registry.detectFrameworks({ + "requirements.txt": "django==4.2\ncelery==5.3\n", + }); + expect(detected).toHaveLength(1); + expect(detected[0].id).toBe("django"); + }); + + it("detects React from package.json", () => { + const registry = new FrameworkRegistry(); + registry.register(reactConfig); + const detected = registry.detectFrameworks({ + "package.json": '{"dependencies": {"react": "^18.2.0", "react-dom": "^18.2.0"}}', + }); + expect(detected).toHaveLength(1); + expect(detected[0].id).toBe("react"); + }); + + it("detection is case-insensitive", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + const detected = registry.detectFrameworks({ + "requirements.txt": "Django==4.2\n", + }); + expect(detected).toHaveLength(1); + }); + + it("returns empty array when no frameworks match", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + const detected = registry.detectFrameworks({ + "requirements.txt": "requests==2.31\n", + }); + expect(detected).toEqual([]); + }); + + it("returns empty array for empty manifests", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + expect(registry.detectFrameworks({})).toEqual([]); + }); + + it("does not duplicate detected frameworks", () => { + const registry = new FrameworkRegistry(); + registry.register(djangoConfig); + const detected = registry.detectFrameworks({ + "requirements.txt": "django==4.2\ndjango==4.2\n", + "pyproject.toml": '[project]\ndependencies = ["django>=4.0"]', + }); + expect(detected).toHaveLength(1); + }); + }); + + describe("createDefault", () => { + it("registers all 10 built-in framework configs", () => { + const registry = FrameworkRegistry.createDefault(); + expect(registry.getAllFrameworks()).toHaveLength(10); + }); + + it("includes frameworks for multiple languages", () => { + const registry = FrameworkRegistry.createDefault(); + expect(registry.getForLanguage("python").length).toBeGreaterThanOrEqual(3); + expect(registry.getForLanguage("typescript").length).toBeGreaterThanOrEqual(2); + expect(registry.getForLanguage("java").length).toBeGreaterThanOrEqual(1); + expect(registry.getForLanguage("ruby").length).toBeGreaterThanOrEqual(1); + expect(registry.getForLanguage("go").length).toBeGreaterThanOrEqual(1); + }); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/__tests__/language-lesson.test.ts b/understand-anything-plugin/packages/core/src/__tests__/language-lesson.test.ts index 7a6d8e7..a8ed5c1 100644 --- a/understand-anything-plugin/packages/core/src/__tests__/language-lesson.test.ts +++ b/understand-anything-plugin/packages/core/src/__tests__/language-lesson.test.ts @@ -5,6 +5,7 @@ import { detectLanguageConcepts, } from "../analyzer/language-lesson.js"; import type { GraphNode, GraphEdge } from "../types.js"; +import { typescriptConfig } from "../languages/configs/typescript.js"; const sampleNode: GraphNode = { id: "func:auth:verifyToken", @@ -51,6 +52,7 @@ describe("language-lesson", () => { sampleNode, sampleEdges, "typescript", + typescriptConfig, ); expect(prompt).toContain("TypeScript"); }); diff --git a/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts b/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts new file mode 100644 index 0000000..c3a8b75 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts @@ -0,0 +1,91 @@ +import { describe, it, expect } from "vitest"; +import { LanguageRegistry } from "../languages/language-registry.js"; +import { typescriptConfig } from "../languages/configs/typescript.js"; +import { pythonConfig } from "../languages/configs/python.js"; + +describe("LanguageRegistry", () => { + it("registers and retrieves a language config by id", () => { + const registry = new LanguageRegistry(); + registry.register(typescriptConfig); + expect(registry.getById("typescript")).toEqual(typescriptConfig); + }); + + it("retrieves config by file extension", () => { + const registry = new LanguageRegistry(); + registry.register(typescriptConfig); + expect(registry.getByExtension(".ts")?.id).toBe("typescript"); + expect(registry.getByExtension(".tsx")?.id).toBe("typescript"); + }); + + it("retrieves config for a file path", () => { + const registry = new LanguageRegistry(); + registry.register(typescriptConfig); + registry.register(pythonConfig); + expect(registry.getForFile("src/index.ts")?.id).toBe("typescript"); + expect(registry.getForFile("app/models.py")?.id).toBe("python"); + }); + + it("returns null for unknown extensions", () => { + const registry = new LanguageRegistry(); + registry.register(typescriptConfig); + expect(registry.getByExtension(".xyz")).toBeNull(); + expect(registry.getForFile("file.unknown")).toBeNull(); + }); + + it("returns null for files without extensions", () => { + const registry = new LanguageRegistry(); + expect(registry.getForFile("Makefile")).toBeNull(); + }); + + it("lists all registered languages", () => { + const registry = new LanguageRegistry(); + registry.register(typescriptConfig); + registry.register(pythonConfig); + const all = registry.getAllLanguages(); + expect(all).toHaveLength(2); + expect(all.map(c => c.id)).toContain("typescript"); + expect(all.map(c => c.id)).toContain("python"); + }); + + describe("createDefault", () => { + it("registers all 12 built-in language configs", () => { + const registry = LanguageRegistry.createDefault(); + const all = registry.getAllLanguages(); + expect(all.length).toBe(12); + }); + + it("maps all expected extensions", () => { + const registry = LanguageRegistry.createDefault(); + expect(registry.getByExtension(".ts")?.id).toBe("typescript"); + expect(registry.getByExtension(".py")?.id).toBe("python"); + expect(registry.getByExtension(".go")?.id).toBe("go"); + expect(registry.getByExtension(".rs")?.id).toBe("rust"); + expect(registry.getByExtension(".java")?.id).toBe("java"); + expect(registry.getByExtension(".rb")?.id).toBe("ruby"); + expect(registry.getByExtension(".php")?.id).toBe("php"); + expect(registry.getByExtension(".swift")?.id).toBe("swift"); + expect(registry.getByExtension(".kt")?.id).toBe("kotlin"); + expect(registry.getByExtension(".cs")?.id).toBe("csharp"); + expect(registry.getByExtension(".cpp")?.id).toBe("cpp"); + expect(registry.getByExtension(".js")?.id).toBe("javascript"); + }); + + it("has no duplicate extension mappings across configs", () => { + const registry = LanguageRegistry.createDefault(); + const all = registry.getAllLanguages(); + const allExtensions: string[] = []; + for (const config of all) { + allExtensions.push(...config.extensions); + } + const unique = new Set(allExtensions); + expect(unique.size).toBe(allExtensions.length); + }); + + it("every config has at least one concept", () => { + const registry = LanguageRegistry.createDefault(); + for (const config of registry.getAllLanguages()) { + expect(config.concepts.length).toBeGreaterThan(0); + } + }); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/analyzer/language-lesson.ts b/understand-anything-plugin/packages/core/src/analyzer/language-lesson.ts index cd1bc56..53fcc01 100644 --- a/understand-anything-plugin/packages/core/src/analyzer/language-lesson.ts +++ b/understand-anything-plugin/packages/core/src/analyzer/language-lesson.ts @@ -1,11 +1,16 @@ import type { GraphNode, GraphEdge } from "../types.js"; +import type { LanguageConfig } from "../languages/types.js"; export interface LanguageLessonResult { languageNotes: string; concepts: Array<{ name: string; explanation: string }>; } -const CONCEPT_PATTERNS: Record = { +/** + * Base concept patterns that apply across all languages. + * These are merged with language-specific concepts from LanguageConfig. + */ +const BASE_CONCEPT_PATTERNS: Record = { "async/await": ["async", "await", "promise", "asynchronous"], "middleware pattern": ["middleware", "interceptor", "pipe"], "generics": ["generic", "type parameter", "template"], @@ -36,12 +41,35 @@ const CONCEPT_PATTERNS: Record = { "concurrency": ["goroutine", "channel", "thread", "worker", "mutex"], }; +/** + * Build the full concept patterns map by merging base patterns with + * language-specific concepts from a LanguageConfig (if provided). + */ +function buildConceptPatterns( + langConfig?: LanguageConfig | null, +): Record { + const patterns = { ...BASE_CONCEPT_PATTERNS }; + + if (langConfig?.concepts) { + for (const concept of langConfig.concepts) { + if (!patterns[concept]) { + // Use the concept name itself as a keyword for detection + patterns[concept] = [concept.toLowerCase()]; + } + } + } + + return patterns; +} + /** * Detects language concepts present in a graph node based on its tags, summary, and languageNotes. + * When a LanguageConfig is provided, language-specific concepts are also detected. */ export function detectLanguageConcepts( node: GraphNode, language: string, + langConfig?: LanguageConfig | null, ): string[] { const text = [ ...node.tags, @@ -49,9 +77,10 @@ export function detectLanguageConcepts( node.languageNotes?.toLowerCase() ?? "", ].join(" "); + const patterns = buildConceptPatterns(langConfig); const detected: string[] = []; - for (const [concept, keywords] of Object.entries(CONCEPT_PATTERNS)) { + for (const [concept, keywords] of Object.entries(patterns)) { const found = keywords.some((keyword) => text.toLowerCase().includes(keyword.toLowerCase()), ); @@ -63,11 +92,19 @@ export function detectLanguageConcepts( return detected; } -const LANGUAGE_DISPLAY_NAMES: Record = { - typescript: "TypeScript", - javascript: "JavaScript", - coffeescript: "CoffeeScript", -}; +/** + * Get the display name for a language. + * Uses LanguageConfig if provided, otherwise falls back to capitalization. + */ +export function getLanguageDisplayName( + language: string, + langConfig?: LanguageConfig | null, +): string { + if (langConfig?.displayName) { + return langConfig.displayName; + } + return language.charAt(0).toUpperCase() + language.slice(1); +} /** * Builds a prompt that asks an LLM to produce a language-specific lesson for a given node. @@ -76,12 +113,11 @@ export function buildLanguageLessonPrompt( node: GraphNode, edges: GraphEdge[], language: string, + langConfig?: LanguageConfig | null, ): string { - const capitalizedLanguage = - LANGUAGE_DISPLAY_NAMES[language.toLowerCase()] ?? - language.charAt(0).toUpperCase() + language.slice(1); + const capitalizedLanguage = getLanguageDisplayName(language, langConfig); - const concepts = detectLanguageConcepts(node, language); + const concepts = detectLanguageConcepts(node, language, langConfig); const relationships = edges .map((edge) => { diff --git a/understand-anything-plugin/packages/core/src/index.ts b/understand-anything-plugin/packages/core/src/index.ts index 7615d6d..6438720 100644 --- a/understand-anything-plugin/packages/core/src/index.ts +++ b/understand-anything-plugin/packages/core/src/index.ts @@ -36,6 +36,20 @@ export { type LanguageLessonResult, } from "./analyzer/language-lesson.js"; export { PluginRegistry } from "./plugins/registry.js"; +export { + LanguageRegistry, + FrameworkRegistry, + builtinLanguageConfigs, + builtinFrameworkConfigs, + LanguageConfigSchema, + FrameworkConfigSchema, +} from "./languages/index.js"; +export type { + LanguageConfig, + FrameworkConfig, + TreeSitterConfig, + FilePatternConfig, +} from "./languages/index.js"; export { parsePluginConfig, serializePluginConfig, diff --git a/understand-anything-plugin/packages/core/src/languages/configs/cpp.ts b/understand-anything-plugin/packages/core/src/languages/configs/cpp.ts new file mode 100644 index 0000000..e4aed53 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/cpp.ts @@ -0,0 +1,25 @@ +import type { LanguageConfig } from "../types.js"; + +export const cppConfig = { + id: "cpp", + displayName: "C/C++", + extensions: [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp", ".hxx"], + concepts: [ + "templates", + "RAII", + "smart pointers", + "move semantics", + "operator overloading", + "virtual functions", + "namespaces", + "constexpr", + "lambda expressions", + "STL containers", + ], + filePatterns: { + entryPoints: ["main.cpp", "main.c", "src/main.cpp"], + barrels: [], + tests: ["*_test.cpp", "*_test.cc", "test_*.cpp"], + config: ["CMakeLists.txt", "Makefile", "meson.build"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/csharp.ts b/understand-anything-plugin/packages/core/src/languages/configs/csharp.ts new file mode 100644 index 0000000..ba2a864 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/csharp.ts @@ -0,0 +1,25 @@ +import type { LanguageConfig } from "../types.js"; + +export const csharpConfig = { + id: "csharp", + displayName: "C#", + extensions: [".cs"], + concepts: [ + "LINQ", + "async/await", + "generics", + "properties", + "delegates and events", + "attributes", + "nullable reference types", + "pattern matching", + "records", + "dependency injection", + ], + filePatterns: { + entryPoints: ["Program.cs", "**/Program.cs"], + barrels: [], + tests: ["*Tests.cs", "*Test.cs"], + config: ["*.csproj", "*.sln"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/go.ts b/understand-anything-plugin/packages/core/src/languages/configs/go.ts new file mode 100644 index 0000000..3d0f2a6 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/go.ts @@ -0,0 +1,24 @@ +import type { LanguageConfig } from "../types.js"; + +export const goConfig = { + id: "go", + displayName: "Go", + extensions: [".go"], + concepts: [ + "goroutines", + "channels", + "interfaces", + "struct embedding", + "error handling patterns", + "defer/panic/recover", + "slices", + "pointers", + "concurrency patterns", + ], + filePatterns: { + entryPoints: ["main.go", "cmd/*/main.go"], + barrels: [], + tests: ["*_test.go"], + config: ["go.mod", "go.sum"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/index.ts b/understand-anything-plugin/packages/core/src/languages/configs/index.ts new file mode 100644 index 0000000..f0a7676 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/index.ts @@ -0,0 +1,43 @@ +import type { LanguageConfig } from "../types.js"; +import { typescriptConfig } from "./typescript.js"; +import { javascriptConfig } from "./javascript.js"; +import { pythonConfig } from "./python.js"; +import { goConfig } from "./go.js"; +import { rustConfig } from "./rust.js"; +import { javaConfig } from "./java.js"; +import { rubyConfig } from "./ruby.js"; +import { phpConfig } from "./php.js"; +import { swiftConfig } from "./swift.js"; +import { kotlinConfig } from "./kotlin.js"; +import { cppConfig } from "./cpp.js"; +import { csharpConfig } from "./csharp.js"; + +export const builtinLanguageConfigs: LanguageConfig[] = [ + typescriptConfig, + javascriptConfig, + pythonConfig, + goConfig, + rustConfig, + javaConfig, + rubyConfig, + phpConfig, + swiftConfig, + kotlinConfig, + cppConfig, + csharpConfig, +]; + +export { + typescriptConfig, + javascriptConfig, + pythonConfig, + goConfig, + rustConfig, + javaConfig, + rubyConfig, + phpConfig, + swiftConfig, + kotlinConfig, + cppConfig, + csharpConfig, +}; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/java.ts b/understand-anything-plugin/packages/core/src/languages/configs/java.ts new file mode 100644 index 0000000..cc62fa4 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/java.ts @@ -0,0 +1,29 @@ +import type { LanguageConfig } from "../types.js"; + +export const javaConfig = { + id: "java", + displayName: "Java", + extensions: [".java"], + concepts: [ + "generics", + "annotations", + "interfaces", + "abstract classes", + "streams API", + "lambdas", + "sealed classes", + "records", + "dependency injection", + "checked exceptions", + ], + filePatterns: { + entryPoints: [ + "**/Application.java", + "**/Main.java", + "src/main/java/**/App.java", + ], + barrels: [], + tests: ["*Test.java", "*Tests.java", "*IT.java"], + config: ["pom.xml", "build.gradle", "build.gradle.kts"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/javascript.ts b/understand-anything-plugin/packages/core/src/languages/configs/javascript.ts new file mode 100644 index 0000000..9d29682 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/javascript.ts @@ -0,0 +1,43 @@ +import type { LanguageConfig } from "../types.js"; + +export const javascriptConfig = { + id: "javascript", + displayName: "JavaScript", + extensions: [".js", ".jsx", ".mjs", ".cjs"], + treeSitter: { + wasmPackage: "tree-sitter-javascript", + wasmFile: "tree-sitter-javascript.wasm", + nodeTypes: { + function: [ + "function_declaration", + "arrow_function", + "function_expression", + "method_definition", + ], + class: ["class_declaration"], + import: ["import_statement"], + export: ["export_statement"], + call: ["call_expression"], + string: ["string", "string_fragment"], + parameter: ["formal_parameters"], + }, + }, + concepts: [ + "closures", + "prototypes", + "promises", + "async/await", + "event loop", + "destructuring", + "spread operator", + "proxies", + "generators", + "modules (ESM/CJS)", + ], + filePatterns: { + entryPoints: ["index.js", "src/index.js", "main.js"], + barrels: ["index.js"], + tests: ["*.test.js", "*.spec.js"], + config: ["package.json", "jsconfig.json"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/kotlin.ts b/understand-anything-plugin/packages/core/src/languages/configs/kotlin.ts new file mode 100644 index 0000000..f02dc79 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/kotlin.ts @@ -0,0 +1,25 @@ +import type { LanguageConfig } from "../types.js"; + +export const kotlinConfig = { + id: "kotlin", + displayName: "Kotlin", + extensions: [".kt", ".kts"], + concepts: [ + "coroutines", + "data classes", + "sealed classes", + "extension functions", + "null safety", + "delegation", + "DSL builders", + "inline functions", + "companion objects", + "flow", + ], + filePatterns: { + entryPoints: ["**/Application.kt", "**/Main.kt"], + barrels: [], + tests: ["*Test.kt", "*Tests.kt"], + config: ["build.gradle.kts", "build.gradle"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/php.ts b/understand-anything-plugin/packages/core/src/languages/configs/php.ts new file mode 100644 index 0000000..2dab065 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/php.ts @@ -0,0 +1,25 @@ +import type { LanguageConfig } from "../types.js"; + +export const phpConfig = { + id: "php", + displayName: "PHP", + extensions: [".php"], + concepts: [ + "namespaces", + "traits", + "type declarations", + "attributes", + "enums", + "fibers", + "closures", + "magic methods", + "dependency injection", + "middleware", + ], + filePatterns: { + entryPoints: ["index.php", "public/index.php", "artisan"], + barrels: [], + tests: ["*Test.php", "tests/**/*.php"], + config: ["composer.json", "php.ini"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/python.ts b/understand-anything-plugin/packages/core/src/languages/configs/python.ts new file mode 100644 index 0000000..f5fa0b0 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/python.ts @@ -0,0 +1,40 @@ +import type { LanguageConfig } from "../types.js"; + +export const pythonConfig = { + id: "python", + displayName: "Python", + extensions: [".py", ".pyi"], + concepts: [ + "decorators", + "list comprehensions", + "generators", + "context managers", + "type hints", + "dunder methods", + "metaclasses", + "dataclasses", + "async/await", + "descriptors", + "protocols", + ], + filePatterns: { + entryPoints: [ + "main.py", + "manage.py", + "app.py", + "wsgi.py", + "asgi.py", + "run.py", + "__main__.py", + ], + barrels: ["__init__.py"], + tests: ["test_*.py", "*_test.py", "conftest.py"], + config: [ + "pyproject.toml", + "setup.py", + "setup.cfg", + "requirements.txt", + "Pipfile", + ], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/ruby.ts b/understand-anything-plugin/packages/core/src/languages/configs/ruby.ts new file mode 100644 index 0000000..f3ed999 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/ruby.ts @@ -0,0 +1,24 @@ +import type { LanguageConfig } from "../types.js"; + +export const rubyConfig = { + id: "ruby", + displayName: "Ruby", + extensions: [".rb", ".rake"], + concepts: [ + "blocks and procs", + "mixins", + "metaprogramming", + "duck typing", + "DSLs", + "monkey patching", + "symbols", + "method_missing", + "open classes", + ], + filePatterns: { + entryPoints: ["config.ru", "app.rb"], + barrels: [], + tests: ["*_test.rb", "*_spec.rb", "spec_helper.rb"], + config: ["Gemfile", "Rakefile"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/rust.ts b/understand-anything-plugin/packages/core/src/languages/configs/rust.ts new file mode 100644 index 0000000..3a6a4bb --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/rust.ts @@ -0,0 +1,27 @@ +import type { LanguageConfig } from "../types.js"; + +export const rustConfig = { + id: "rust", + displayName: "Rust", + extensions: [".rs"], + concepts: [ + "ownership", + "borrowing", + "lifetimes", + "traits", + "pattern matching", + "enums with data", + "error handling (Result/Option)", + "macros", + "async/await", + "unsafe blocks", + "generics", + "closures", + ], + filePatterns: { + entryPoints: ["src/main.rs", "src/lib.rs"], + barrels: ["mod.rs", "lib.rs"], + tests: ["tests/*.rs"], + config: ["Cargo.toml"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/swift.ts b/understand-anything-plugin/packages/core/src/languages/configs/swift.ts new file mode 100644 index 0000000..af0977e --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/swift.ts @@ -0,0 +1,25 @@ +import type { LanguageConfig } from "../types.js"; + +export const swiftConfig = { + id: "swift", + displayName: "Swift", + extensions: [".swift"], + concepts: [ + "optionals", + "protocols", + "extensions", + "generics", + "closures", + "property wrappers", + "result builders", + "actors", + "structured concurrency", + "value types vs reference types", + ], + filePatterns: { + entryPoints: ["Sources/*/main.swift", "App.swift", "AppDelegate.swift"], + barrels: [], + tests: ["*Tests.swift", "Tests/**/*.swift"], + config: ["Package.swift"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/configs/typescript.ts b/understand-anything-plugin/packages/core/src/languages/configs/typescript.ts new file mode 100644 index 0000000..ffb093d --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/typescript.ts @@ -0,0 +1,48 @@ +import type { LanguageConfig } from "../types.js"; + +export const typescriptConfig = { + id: "typescript", + displayName: "TypeScript", + extensions: [".ts", ".tsx"], + treeSitter: { + wasmPackage: "tree-sitter-typescript", + wasmFile: "tree-sitter-typescript.wasm", + nodeTypes: { + function: [ + "function_declaration", + "arrow_function", + "function_expression", + "method_definition", + ], + class: ["class_declaration"], + import: ["import_statement"], + export: ["export_statement"], + call: ["call_expression"], + string: ["string", "string_fragment"], + parameter: [ + "formal_parameters", + "required_parameter", + "optional_parameter", + ], + }, + }, + concepts: [ + "generics", + "type guards", + "discriminated unions", + "utility types", + "decorators", + "enums", + "interfaces", + "type inference", + "mapped types", + "conditional types", + "template literal types", + ], + filePatterns: { + entryPoints: ["src/index.ts", "src/main.ts", "src/App.tsx", "index.ts"], + barrels: ["index.ts"], + tests: ["*.test.ts", "*.spec.ts", "*.test.tsx"], + config: ["tsconfig.json"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/framework-registry.ts b/understand-anything-plugin/packages/core/src/languages/framework-registry.ts new file mode 100644 index 0000000..5ec6d1d --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/framework-registry.ts @@ -0,0 +1,80 @@ +import { FrameworkConfigSchema } from "./types.js"; +import type { FrameworkConfig } from "./types.js"; +import { builtinFrameworkConfigs } from "./frameworks/index.js"; + +/** + * Registry for framework configurations. Provides detection of frameworks + * from manifest file contents and lookup by id or language. + */ +export class FrameworkRegistry { + private byId = new Map(); + private byLanguage = new Map(); + + register(config: FrameworkConfig): void { + const parsed = FrameworkConfigSchema.parse(config); + this.byId.set(parsed.id, parsed); + + const existing = this.byLanguage.get(parsed.language) ?? []; + existing.push(parsed); + this.byLanguage.set(parsed.language, existing); + } + + getById(id: string): FrameworkConfig | null { + return this.byId.get(id) ?? null; + } + + getForLanguage(langId: string): FrameworkConfig[] { + return this.byLanguage.get(langId) ?? []; + } + + getAllFrameworks(): FrameworkConfig[] { + return [...this.byId.values()]; + } + + /** + * Detect frameworks from manifest file contents. + * @param manifests - Map of filename to file content (e.g., { "requirements.txt": "django==4.2\n..." }) + * @returns Array of detected FrameworkConfig objects + */ + detectFrameworks(manifests: Record): FrameworkConfig[] { + const detected = new Set(); + const results: FrameworkConfig[] = []; + + for (const config of this.byId.values()) { + if (detected.has(config.id)) continue; + + for (const manifestFile of config.manifestFiles) { + // Match manifest entries by filename (basename match) + const content = Object.entries(manifests).find( + ([key]) => key === manifestFile || key.endsWith(`/${manifestFile}`), + )?.[1]; + + if (!content) continue; + + const contentLower = content.toLowerCase(); + const found = config.detectionKeywords.some((keyword) => + contentLower.includes(keyword.toLowerCase()), + ); + + if (found) { + detected.add(config.id); + results.push(config); + break; + } + } + } + + return results; + } + + /** + * Create a registry pre-populated with all built-in framework configs. + */ + static createDefault(): FrameworkRegistry { + const registry = new FrameworkRegistry(); + for (const config of builtinFrameworkConfigs) { + registry.register(config); + } + return registry; + } +} diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/django.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/django.ts new file mode 100644 index 0000000..824b02c --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/django.ts @@ -0,0 +1,36 @@ +import type { FrameworkConfig } from "../types.js"; + +export const djangoConfig = { + id: "django", + displayName: "Django", + language: "python", + detectionKeywords: [ + "django", + "djangorestframework", + "django-rest-framework", + "django-cors-headers", + "django-filter", + ], + manifestFiles: [ + "requirements.txt", + "pyproject.toml", + "setup.py", + "setup.cfg", + "Pipfile", + ], + promptSnippetPath: "./frameworks/django.md", + entryPoints: ["manage.py", "wsgi.py", "asgi.py"], + layerHints: { + views: "api", + models: "data", + serializers: "api", + urls: "api", + templates: "ui", + migrations: "data", + management: "config", + signals: "service", + admin: "config", + forms: "ui", + templatetags: "utility", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/express.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/express.ts new file mode 100644 index 0000000..af82dca --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/express.ts @@ -0,0 +1,26 @@ +import type { FrameworkConfig } from "../types.js"; + +export const expressConfig = { + id: "express", + displayName: "Express", + language: "javascript", + detectionKeywords: ["express", "express-validator", "cors", "body-parser"], + manifestFiles: ["package.json"], + promptSnippetPath: "./frameworks/express.md", + entryPoints: [ + "src/index.js", + "src/app.js", + "server.js", + "app.js", + "src/index.ts", + "src/app.ts", + ], + layerHints: { + routes: "api", + controllers: "service", + models: "data", + middleware: "middleware", + services: "service", + db: "data", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/fastapi.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/fastapi.ts new file mode 100644 index 0000000..b62c871 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/fastapi.ts @@ -0,0 +1,25 @@ +import type { FrameworkConfig } from "../types.js"; + +export const fastapiConfig = { + id: "fastapi", + displayName: "FastAPI", + language: "python", + detectionKeywords: ["fastapi", "uvicorn", "starlette"], + manifestFiles: [ + "requirements.txt", + "pyproject.toml", + "setup.py", + "setup.cfg", + "Pipfile", + ], + promptSnippetPath: "./frameworks/fastapi.md", + entryPoints: ["main.py", "app.py"], + layerHints: { + routers: "api", + schemas: "types", + models: "data", + dependencies: "service", + crud: "service", + api: "api", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/flask.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/flask.ts new file mode 100644 index 0000000..e68fcc6 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/flask.ts @@ -0,0 +1,31 @@ +import type { FrameworkConfig } from "../types.js"; + +export const flaskConfig = { + id: "flask", + displayName: "Flask", + language: "python", + detectionKeywords: [ + "flask", + "flask-restful", + "flask-sqlalchemy", + "flask-marshmallow", + "flask-wtf", + ], + manifestFiles: [ + "requirements.txt", + "pyproject.toml", + "setup.py", + "setup.cfg", + "Pipfile", + ], + promptSnippetPath: "./frameworks/flask.md", + entryPoints: ["app.py", "run.py", "wsgi.py"], + layerHints: { + blueprints: "api", + views: "api", + models: "data", + forms: "ui", + templates: "ui", + extensions: "config", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/gin.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/gin.ts new file mode 100644 index 0000000..eae684a --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/gin.ts @@ -0,0 +1,19 @@ +import type { FrameworkConfig } from "../types.js"; + +export const ginConfig = { + id: "gin", + displayName: "Gin", + language: "go", + detectionKeywords: ["github.com/gin-gonic/gin"], + manifestFiles: ["go.mod"], + promptSnippetPath: "./frameworks/gin.md", + entryPoints: ["main.go", "cmd/server/main.go"], + layerHints: { + handlers: "api", + routes: "api", + models: "data", + middleware: "middleware", + services: "service", + repository: "data", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/index.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/index.ts new file mode 100644 index 0000000..0b26b60 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/index.ts @@ -0,0 +1,38 @@ +import type { FrameworkConfig } from "../types.js"; + +import { djangoConfig } from "./django.js"; +import { fastapiConfig } from "./fastapi.js"; +import { flaskConfig } from "./flask.js"; +import { reactConfig } from "./react.js"; +import { nextjsConfig } from "./nextjs.js"; +import { expressConfig } from "./express.js"; +import { vueConfig } from "./vue.js"; +import { springConfig } from "./spring.js"; +import { railsConfig } from "./rails.js"; +import { ginConfig } from "./gin.js"; + +export const builtinFrameworkConfigs: FrameworkConfig[] = [ + djangoConfig, + fastapiConfig, + flaskConfig, + reactConfig, + nextjsConfig, + expressConfig, + vueConfig, + springConfig, + railsConfig, + ginConfig, +]; + +export { + djangoConfig, + fastapiConfig, + flaskConfig, + reactConfig, + nextjsConfig, + expressConfig, + vueConfig, + springConfig, + railsConfig, + ginConfig, +}; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/nextjs.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/nextjs.ts new file mode 100644 index 0000000..97fefad --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/nextjs.ts @@ -0,0 +1,23 @@ +import type { FrameworkConfig } from "../types.js"; + +export const nextjsConfig = { + id: "nextjs", + displayName: "Next.js", + language: "typescript", + detectionKeywords: ["next", "@next/font", "@next/image"], + manifestFiles: ["package.json"], + promptSnippetPath: "./frameworks/nextjs.md", + entryPoints: [ + "src/app/layout.tsx", + "pages/_app.tsx", + "src/pages/_app.tsx", + ], + layerHints: { + app: "ui", + pages: "ui", + api: "api", + components: "ui", + lib: "service", + middleware: "middleware", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/rails.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/rails.ts new file mode 100644 index 0000000..6ba47ab --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/rails.ts @@ -0,0 +1,28 @@ +import type { FrameworkConfig } from "../types.js"; + +export const railsConfig = { + id: "rails", + displayName: "Ruby on Rails", + language: "ruby", + detectionKeywords: [ + "rails", + "railties", + "actionpack", + "activerecord", + "actionview", + ], + manifestFiles: ["Gemfile"], + promptSnippetPath: "./frameworks/rails.md", + entryPoints: ["config.ru", "bin/rails"], + layerHints: { + controllers: "api", + models: "data", + views: "ui", + helpers: "utility", + mailers: "service", + jobs: "service", + channels: "service", + middleware: "middleware", + lib: "service", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/react.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/react.ts new file mode 100644 index 0000000..7429fd3 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/react.ts @@ -0,0 +1,19 @@ +import type { FrameworkConfig } from "../types.js"; + +export const reactConfig = { + id: "react", + displayName: "React", + language: "typescript", + detectionKeywords: ["react", "react-dom", "@types/react"], + manifestFiles: ["package.json"], + promptSnippetPath: "./frameworks/react.md", + entryPoints: ["src/App.tsx", "src/App.jsx", "src/index.tsx", "src/main.tsx"], + layerHints: { + components: "ui", + hooks: "service", + pages: "ui", + contexts: "service", + utils: "utility", + lib: "service", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/spring.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/spring.ts new file mode 100644 index 0000000..7b7b56d --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/spring.ts @@ -0,0 +1,27 @@ +import type { FrameworkConfig } from "../types.js"; + +export const springConfig = { + id: "spring", + displayName: "Spring Boot", + language: "java", + detectionKeywords: [ + "spring-boot", + "spring-boot-starter", + "spring-web", + "spring-data", + "org.springframework", + ], + manifestFiles: ["pom.xml", "build.gradle", "build.gradle.kts"], + promptSnippetPath: "./frameworks/spring.md", + entryPoints: ["**/Application.java", "**/App.java"], + layerHints: { + controller: "api", + service: "service", + repository: "data", + model: "data", + entity: "data", + config: "config", + dto: "types", + security: "middleware", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/frameworks/vue.ts b/understand-anything-plugin/packages/core/src/languages/frameworks/vue.ts new file mode 100644 index 0000000..94a8f01 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/frameworks/vue.ts @@ -0,0 +1,19 @@ +import type { FrameworkConfig } from "../types.js"; + +export const vueConfig = { + id: "vue", + displayName: "Vue", + language: "typescript", + detectionKeywords: ["vue", "@vue/cli-service", "nuxt", "vite-plugin-vue"], + manifestFiles: ["package.json"], + promptSnippetPath: "./frameworks/vue.md", + entryPoints: ["src/main.ts", "src/App.vue", "src/main.js"], + layerHints: { + components: "ui", + views: "ui", + store: "service", + composables: "service", + router: "config", + plugins: "config", + }, +} satisfies FrameworkConfig; diff --git a/understand-anything-plugin/packages/core/src/languages/index.ts b/understand-anything-plugin/packages/core/src/languages/index.ts new file mode 100644 index 0000000..7fab61b --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/index.ts @@ -0,0 +1,22 @@ +// Types +export type { + LanguageConfig, + TreeSitterConfig, + FilePatternConfig, + FrameworkConfig, +} from "./types.js"; + +export { + LanguageConfigSchema, + TreeSitterConfigSchema, + FilePatternConfigSchema, + FrameworkConfigSchema, +} from "./types.js"; + +// Registries +export { LanguageRegistry } from "./language-registry.js"; +export { FrameworkRegistry } from "./framework-registry.js"; + +// Built-in configs +export { builtinLanguageConfigs } from "./configs/index.js"; +export { builtinFrameworkConfigs } from "./frameworks/index.js"; diff --git a/understand-anything-plugin/packages/core/src/languages/language-registry.ts b/understand-anything-plugin/packages/core/src/languages/language-registry.ts new file mode 100644 index 0000000..0afef19 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/language-registry.ts @@ -0,0 +1,53 @@ +import { LanguageConfigSchema } from "./types.js"; +import type { LanguageConfig } from "./types.js"; +import { builtinLanguageConfigs } from "./configs/index.js"; + +/** + * Registry for language configurations. Maps language ids and file extensions + * to their corresponding LanguageConfig objects. + */ +export class LanguageRegistry { + private byId = new Map(); + private byExtension = new Map(); + + register(config: LanguageConfig): void { + const parsed = LanguageConfigSchema.parse(config); + this.byId.set(parsed.id, parsed); + for (const ext of parsed.extensions) { + // Normalize: strip leading dot if present for lookup consistency + const key = ext.startsWith(".") ? ext : `.${ext}`; + this.byExtension.set(key, parsed); + } + } + + getById(id: string): LanguageConfig | null { + return this.byId.get(id) ?? null; + } + + getByExtension(ext: string): LanguageConfig | null { + const key = ext.startsWith(".") ? ext : `.${ext}`; + return this.byExtension.get(key) ?? null; + } + + getForFile(filePath: string): LanguageConfig | null { + const lastDot = filePath.lastIndexOf("."); + if (lastDot === -1) return null; + const ext = filePath.slice(lastDot).toLowerCase(); + return this.getByExtension(ext); + } + + getAllLanguages(): LanguageConfig[] { + return [...this.byId.values()]; + } + + /** + * Create a registry pre-populated with all built-in language configs. + */ + static createDefault(): LanguageRegistry { + const registry = new LanguageRegistry(); + for (const config of builtinLanguageConfigs) { + registry.register(config); + } + return registry; + } +} diff --git a/understand-anything-plugin/packages/core/src/languages/types.ts b/understand-anything-plugin/packages/core/src/languages/types.ts new file mode 100644 index 0000000..03953a2 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/types.ts @@ -0,0 +1,54 @@ +import { z } from "zod"; + +// Tree-sitter node type mappings for a language +export const TreeSitterConfigSchema = z.object({ + wasmPackage: 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()), + call: z.array(z.string()), + string: z.array(z.string()), + parameter: z.array(z.string()), + }), +}); + +export type TreeSitterConfig = z.infer; + +// File pattern conventions for a language +export const FilePatternConfigSchema = z.object({ + entryPoints: z.array(z.string()), + barrels: z.array(z.string()), + tests: z.array(z.string()), + config: z.array(z.string()), +}); + +export type FilePatternConfig = z.infer; + +// Complete language configuration +export const LanguageConfigSchema = z.object({ + id: z.string().min(1), + displayName: z.string().min(1), + extensions: z.array(z.string()).min(1), + treeSitter: TreeSitterConfigSchema.optional(), + concepts: z.array(z.string()), + filePatterns: FilePatternConfigSchema, +}); + +export type LanguageConfig = z.infer; + +// Framework configuration +export const FrameworkConfigSchema = z.object({ + id: z.string().min(1), + displayName: z.string().min(1), + language: z.string().min(1), + detectionKeywords: z.array(z.string()).min(1), + manifestFiles: z.array(z.string()).min(1), + promptSnippetPath: z.string().min(1), + entryPoints: z.array(z.string()).optional(), + layerHints: z.record(z.string(), z.string()).optional(), +}); + +export type FrameworkConfig = z.infer; diff --git a/understand-anything-plugin/packages/core/src/plugins/registry.ts b/understand-anything-plugin/packages/core/src/plugins/registry.ts index 324a802..71233fe 100644 --- a/understand-anything-plugin/packages/core/src/plugins/registry.ts +++ b/understand-anything-plugin/packages/core/src/plugins/registry.ts @@ -1,30 +1,21 @@ import type { AnalyzerPlugin, StructuralAnalysis, ImportResolution } from "../types.js"; - -const EXTENSION_TO_LANGUAGE: Record = { - ts: "typescript", - tsx: "typescript", - js: "javascript", - jsx: "javascript", - py: "python", - go: "go", - rs: "rust", - rb: "ruby", - java: "java", - kt: "kotlin", - cs: "csharp", - cpp: "cpp", - c: "c", - swift: "swift", - php: "php", -}; +import { LanguageRegistry } from "../languages/language-registry.js"; /** * Registry for analyzer plugins. Maps languages to plugins and provides * a unified interface for analyzing files across languages. + * + * Uses LanguageRegistry for extension-to-language mapping instead of + * a hardcoded lookup table. */ export class PluginRegistry { private plugins: AnalyzerPlugin[] = []; private languageMap = new Map(); + private languageRegistry: LanguageRegistry; + + constructor(languageRegistry?: LanguageRegistry) { + this.languageRegistry = languageRegistry ?? LanguageRegistry.createDefault(); + } register(plugin: AnalyzerPlugin): void { this.plugins.push(plugin); @@ -50,11 +41,16 @@ export class PluginRegistry { } getPluginForFile(filePath: string): AnalyzerPlugin | null { - const ext = filePath.split(".").pop()?.toLowerCase(); - if (!ext) return null; - const language = EXTENSION_TO_LANGUAGE[ext]; - if (!language) return null; - return this.getPluginForLanguage(language); + const langConfig = this.languageRegistry.getForFile(filePath); + if (!langConfig) return null; + return this.getPluginForLanguage(langConfig.id); + } + + /** + * Get the language id for a file path using the language registry. + */ + getLanguageForFile(filePath: string): string | null { + return this.languageRegistry.getForFile(filePath)?.id ?? null; } analyzeFile(filePath: string, content: string): StructuralAnalysis | null { diff --git a/understand-anything-plugin/packages/core/src/plugins/tree-sitter-plugin.ts b/understand-anything-plugin/packages/core/src/plugins/tree-sitter-plugin.ts index 81897c7..cf774f9 100644 --- a/understand-anything-plugin/packages/core/src/plugins/tree-sitter-plugin.ts +++ b/understand-anything-plugin/packages/core/src/plugins/tree-sitter-plugin.ts @@ -6,6 +6,7 @@ import type { ImportResolution, CallGraphEntry, } from "../types.js"; +import type { LanguageConfig } from "../languages/types.js"; // web-tree-sitter uses CJS internally; we need createRequire for .wasm resolution const require = createRequire(import.meta.url); @@ -14,23 +15,6 @@ type TreeSitterParser = import("web-tree-sitter").Parser; type TreeSitterLanguage = import("web-tree-sitter").Language; type TreeSitterNode = import("web-tree-sitter").Node; -function languageKeyFromPath(filePath: string): string { - const ext = extname(filePath).toLowerCase(); - switch (ext) { - case ".ts": - return "typescript"; - case ".tsx": - return "tsx"; - case ".js": - case ".mjs": - case ".cjs": - case ".jsx": - return "javascript"; - default: - throw new Error(`Unsupported file extension: ${ext}`); - } -} - /** * Recursively traverse an AST tree, calling the visitor for each node. */ @@ -155,17 +139,81 @@ function extractImportSpecifiers( return specifiers; } +/** + * Config-driven tree-sitter plugin. + * + * Accepts LanguageConfig objects to determine which languages to support + * and how to load their WASM grammars. Currently provides deep structural + * analysis for TypeScript/JavaScript; other languages with tree-sitter configs + * get basic function/class/import extraction. + * + * Languages without tree-sitter configs are gracefully skipped (the LLM + * agent handles analysis for those). + */ export class TreeSitterPlugin implements AnalyzerPlugin { readonly name = "tree-sitter"; - readonly languages = ["typescript", "javascript"]; + readonly languages: string[]; + + private configs: LanguageConfig[]; // Pre-loaded parser constructor and languages (set by init()) private _ParserClass: | (new () => TreeSitterParser) | null = null; private _languages = new Map(); + private _extensionToLang = new Map(); private _initialized = false; + /** + * Create a TreeSitterPlugin with the given language configs. + * Only configs that have a `treeSitter` field will be loaded. + * If no configs are provided, defaults to TypeScript and JavaScript. + */ + constructor(configs?: LanguageConfig[]) { + if (configs) { + this.configs = configs.filter((c) => c.treeSitter); + } else { + // Default: TS/JS for backward compatibility + this.configs = []; + } + + // Derive supported languages and extension map from configs + const langs: string[] = []; + for (const config of this.configs) { + langs.push(config.id); + for (const ext of config.extensions) { + const key = ext.startsWith(".") ? ext : `.${ext}`; + this._extensionToLang.set(key, config.id); + } + } + + // Fallback for backward compat when no configs provided + if (langs.length === 0) { + langs.push("typescript", "javascript"); + this._extensionToLang.set(".ts", "typescript"); + this._extensionToLang.set(".tsx", "typescript"); + this._extensionToLang.set(".js", "javascript"); + this._extensionToLang.set(".mjs", "javascript"); + this._extensionToLang.set(".cjs", "javascript"); + this._extensionToLang.set(".jsx", "javascript"); + } + + this.languages = langs; + } + + private languageKeyFromPath(filePath: string): string { + const ext = extname(filePath).toLowerCase(); + + // Special case: .tsx needs its own grammar + if (ext === ".tsx") return "tsx"; + + const lang = this._extensionToLang.get(ext); + if (!lang) { + throw new Error(`Unsupported file extension: ${ext}`); + } + return lang; + } + /** * Initialize the plugin by loading the WASM module and all language grammars. * Must be called (and awaited) before any synchronous methods. @@ -180,26 +228,68 @@ export class TreeSitterPlugin implements AnalyzerPlugin { await ParserCls.init(); this._ParserClass = ParserCls as unknown as new () => TreeSitterParser; - // Pre-load all supported language grammars - const tsWasm = require.resolve( - "tree-sitter-typescript/tree-sitter-typescript.wasm", - ); - const tsxWasm = require.resolve( - "tree-sitter-typescript/tree-sitter-tsx.wasm", - ); - const jsWasm = require.resolve( - "tree-sitter-javascript/tree-sitter-javascript.wasm", - ); + if (this.configs.length > 0) { + // Load grammars from configs + const loadPromises: Promise[] = []; - const [tsLang, tsxLang, jsLang] = await Promise.all([ - LanguageCls.load(tsWasm), - LanguageCls.load(tsxWasm), - LanguageCls.load(jsWasm), - ]); + for (const config of this.configs) { + if (!config.treeSitter) continue; + + const loadGrammar = async () => { + try { + const wasmPath = require.resolve( + `${config.treeSitter!.wasmPackage}/${config.treeSitter!.wasmFile}`, + ); + const lang = await LanguageCls.load(wasmPath); + this._languages.set(config.id, lang); + + // Special handling for TypeScript: also load TSX grammar + if (config.id === "typescript") { + try { + const tsxWasm = require.resolve( + `${config.treeSitter!.wasmPackage}/tree-sitter-tsx.wasm`, + ); + const tsxLang = await LanguageCls.load(tsxWasm); + this._languages.set("tsx", tsxLang); + } catch { + // TSX grammar not available; .tsx files will fall back to TS grammar + } + } + } catch { + // Grammar not available — this language will be skipped gracefully + console.debug?.( + `tree-sitter: Could not load grammar for ${config.id}, skipping structural analysis`, + ); + } + }; + + loadPromises.push(loadGrammar()); + } + + await Promise.all(loadPromises); + } else { + // Legacy fallback: load TS/JS grammars directly + const tsWasm = require.resolve( + "tree-sitter-typescript/tree-sitter-typescript.wasm", + ); + const tsxWasm = require.resolve( + "tree-sitter-typescript/tree-sitter-tsx.wasm", + ); + const jsWasm = require.resolve( + "tree-sitter-javascript/tree-sitter-javascript.wasm", + ); + + const [tsLang, tsxLang, jsLang] = await Promise.all([ + LanguageCls.load(tsWasm), + LanguageCls.load(tsxWasm), + LanguageCls.load(jsWasm), + ]); + + this._languages.set("typescript", tsLang); + this._languages.set("tsx", tsxLang); + this._languages.set("javascript", jsLang); + } - this._languages.set("typescript", tsLang); - this._languages.set("tsx", tsxLang); - this._languages.set("javascript", jsLang); this._initialized = true; } @@ -207,16 +297,17 @@ export class TreeSitterPlugin implements AnalyzerPlugin { * Create a parser set to the appropriate language for the given file. * This is synchronous because all languages are pre-loaded during init(). */ - private getParser(filePath: string): TreeSitterParser { + private getParser(filePath: string): TreeSitterParser | null { if (!this._initialized || !this._ParserClass) { throw new Error( "TreeSitterPlugin.init() must be called before use", ); } - const langKey = languageKeyFromPath(filePath); + const langKey = this.languageKeyFromPath(filePath); const lang = this._languages.get(langKey); if (!lang) { - throw new Error(`Language not loaded: ${langKey}`); + // Language grammar not loaded — graceful degradation + return null; } const parser = new this._ParserClass(); parser.setLanguage(lang); @@ -228,6 +319,10 @@ export class TreeSitterPlugin implements AnalyzerPlugin { content: string, ): StructuralAnalysis { const parser = this.getParser(filePath); + if (!parser) { + return { functions: [], classes: [], imports: [], exports: [] }; + } + const tree = parser.parse(content); if (!tree) { parser.delete(); @@ -290,6 +385,8 @@ export class TreeSitterPlugin implements AnalyzerPlugin { content: string, ): CallGraphEntry[] { const parser = this.getParser(filePath); + if (!parser) return []; + const tree = parser.parse(content); if (!tree) { parser.delete(); diff --git a/understand-anything-plugin/skills/understand/PYTHON-SUPPORT-CHANGES.md b/understand-anything-plugin/skills/understand/PYTHON-SUPPORT-CHANGES.md deleted file mode 100644 index 39939a1..0000000 --- a/understand-anything-plugin/skills/understand/PYTHON-SUPPORT-CHANGES.md +++ /dev/null @@ -1,208 +0,0 @@ -# Python Codebase Support — Change Review Guide - -This document explains every change made to add Python and Python framework support. It is written for a reviewer who wants to verify correctness, spot regressions, and understand the rationale for each decision. - ---- - -## Background: What Was Wrong - -The tool worked well for TypeScript/JavaScript codebases. For Python codebases it would: -- Fail to detect frameworks (Django, FastAPI, Flask) — `frameworks: []` always -- Miss common Python entry points (`manage.py`, `app.py`, `wsgi.py`) — defaulting to no entry point -- Score Python entry points much lower than TS equivalents in the tour builder (4 Python patterns vs 8 JS/TS patterns) -- Miss Python's `__init__.py` as a barrel/entry-point equivalent (only `index.ts`/`index.js` were recognized) -- Have no layer guidance for FastAPI or Flask (only Django had a brief mention) -- Ignore `pyproject.toml` for project name extraction - -The bias was **only in agent prompts** (markdown files). The graph schema, dashboard, search engine, and core plugin architecture are language-agnostic and required no changes. - ---- - -## Files Changed - -### 1. `project-scanner-prompt.md` - -**What changed:** - -**Step 5 (Framework Detection)** — Extended the Python manifest reading from "confirms Python project" to actually detecting frameworks: -- `requirements.txt`: now reads line-by-line, strips version specifiers, and matches against a Python framework keyword list: `django`, `djangorestframework`, `fastapi`, `flask`, `sqlalchemy`, `alembic`, `celery`, `pydantic`, `uvicorn`, `gunicorn`, `aiohttp`, `tornado`, `starlette`, `pytest`, `hypothesis`, `channels` -- `pyproject.toml`: now parses `[project].dependencies` and `[tool.poetry.dependencies]`, applies the same keyword matching, and also checks for `[tool.pytest.ini_options]` (pytest) and `[tool.django]` (Django) -- `setup.py`, `setup.cfg`, `Pipfile`: now apply the same Python framework keyword matching - -**Step 7 (Project Name)** — Added `pyproject.toml` to the priority order between `go.mod` and directory name. Checks `[project].name` first, then `[tool.poetry].name`. - -**Why:** Without framework detection, `frameworks: []` is passed to every downstream agent. The framework-specific guidance injected in Phase 2 and Phase 4 of SKILL.md is only useful when `frameworks` is non-empty. - -**Regression risk:** None. The JS framework detection in `package.json` is unchanged. The Python additions are additive. - -**How to verify:** Run `/understand` on a Django project with `requirements.txt` containing `django`. Check that `scan-result.json` has `frameworks: ["Django"]`. - ---- - -### 2. `SKILL.md` - -**What changed:** - -**Phase 0 (entry point detection, line 57)** — Added Python entry points to the pattern list: -- Before: `src/index.ts`, `src/main.ts`, `src/App.tsx`, `main.py`, `main.go`, `src/main.rs`, `index.js` -- After: added `manage.py`, `app.py`, `wsgi.py`, `asgi.py`, `run.py`, `__main__.py` - -**Why:** A Django project's real entry point is `manage.py`. A FastAPI/Flask project uses `app.py` or `run.py`. Without these, `$ENTRY_POINT` is empty for most Python projects, and the tour builder gets no starting hint. - -**Phase 2 (file-analyzer framework guidance)** — Extended the inline framework hints: -- Django: added `serializers.py`, `signals.py`, `admin.py`, `migrations/` descriptions -- Added FastAPI: describes `@router` decorator files, Pydantic schemas, `Depends()` providers -- Added Flask: describes `@blueprint.route`, `blueprints/`, SQLAlchemy `models.py` -- Added addendum injection: if `Django` detected, reads `./django-analyzer-addendum.md` and appends to the file-analyzer prompt. If `FastAPI` or `Flask` detected, reads `./fastapi-analyzer-addendum.md` and appends. - -**Phase 4 (architecture-analyzer framework hints)** — Extended the inline layer hints: -- Django: added `serializers.py`, `signals.py`, `migrations/` → specific layers -- Added FastAPI: router files → API, Pydantic schemas → Types, `dependencies.py` → Service, DB files → Data -- Added Flask: blueprint route files → API, `models.py` → Data, `forms.py` → UI, `extensions.py` → Config -- Added addendum injection: same logic as Phase 2 - -**Regression risk:** Low. The addendum injection only triggers when those frameworks are in the detected list. The inline guidance additions are additive strings — they don't change the structure of the injected context. - -**How to verify:** -- Run on a FastAPI project: check `layers.json` has a `layer:types` or `layer:api` with Pydantic schema files assigned correctly -- Run on a TS project: check that no Django/FastAPI addendum content appears in the analysis (it shouldn't, since `frameworks` won't contain those values) - ---- - -### 3. `architecture-analyzer-prompt.md` - -**What changed:** - -**Directory pattern table** — Added Python-specific directory names: - -| Added | Pattern Label | Why | -|-------|---------------|-----| -| `migrations` | `data` | Django/Alembic migration directories hold schema history — Data Layer | -| `management`, `commands` | `config` | Django management command directories | -| `templatetags` | `utility` | Django custom template tag directories | -| `signals` | `service` | Signal handler modules — cross-cutting service logic | -| `serializers` | `api` | DRF serializer directories | - -**File-level pattern matching** — Three changes: -1. Added `test_*.py` to the test pattern (Python's `pytest` naming convention) -2. Added `__init__.py` at a directory root → `entry` pattern (Python package barrel equivalent of `index.ts`) -3. Added `manage.py` → `entry` and `wsgi.py`/`asgi.py` → `config` -4. Clarified `*.d.ts → types` with "(TypeScript declaration files only)" — making it explicit this is TS-specific so an LLM doesn't misapply it to Python - -**Why:** Without `__init__.py → entry`, the architecture analyzer would never recognize any Python file as an entry point via file-level patterns. The `hooks` pattern label was left as-is (it won't trigger on Python projects since they don't conventionally have a `hooks/` directory). - -**Note on Node.js script:** The architecture analyzer's structural analysis script is hardcoded to `node`. This is correct to leave as-is — the script processes the JSON graph structure (file nodes, import edges), not the source language of the codebase being analyzed. The script's input/output is always JSON regardless of whether the project is Python or TypeScript. - -**Regression risk:** Very low. Added rows to the directory pattern table and clarified file-level pattern descriptions. No existing patterns were removed or modified. - -**How to verify:** Run on a Django project. Check that `migrations/` directory files land in `layer:data` and that `manage.py` gets tagged `entry`. - ---- - -### 4. `tour-builder-prompt.md` - -**What changed:** - -**Entry point candidate scoring (Section C)** — Added Python entry points to the +3 filename list: -- Added: `manage.py`, `app.py`, `wsgi.py`, `asgi.py`, `run.py`, `__main__.py` - -Before this change, the entry point scoring had 8 TS/JS patterns vs 4 for all other languages. After: 8 TS/JS + 6 Python + 4 others. - -**Why:** The tour builder uses entry point scores to decide Step 1 of the tour. For a Django project where `manage.py` exists, it should score highly. Without this, the tour might start from a random high-fan-in utility file instead of the actual entry point. - -**The `languageLesson` example** was left as-is (TypeScript barrel files). This is just an illustrative example in the output format section — it does not affect how Python tours are generated. The language lessons section already lists Python-specific patterns (decorators, generators, context managers, metaclasses, protocols). - -**Regression risk:** None. The scoring list is additive. TS/JS entry points retain their +3 scores. - -**How to verify:** Run on a Django project. Check that `tour.json` starts from `manage.py` or `apps.py`/`wsgi.py` rather than a utility file. - ---- - -### 5. `file-analyzer-prompt.md` - -**What changed:** - -**Tags indicators — barrel/entry-point detection** — Extended the `index.ts` rule: -- Before: `Named index.ts at a directory root with re-exports = entry-point` -- After: Added `__init__.py` at a package root with imports or re-exports = `entry-point`, and `manage.py` = `entry-point` - -**Script execution example** — Added the Python equivalent command alongside the Node.js example. The base prompt already says "Choose the best language for this task — Node.js is recommended for TypeScript/JavaScript projects, Python for Python projects" (line 15), but the execution example only showed `node`. This created a contradiction. Now both are shown. - -**Regression risk:** None. Additive changes only. - -**How to verify:** Run on a Python project with a package structure. Check that `__init__.py` files at package roots get the `entry-point` or `barrel` tag rather than being treated as empty boilerplate files. - ---- - -## New Files Created - -### `django-analyzer-addendum.md` - -A detailed reference injected into the file-analyzer and architecture-analyzer when Django is detected. Contains: -- Canonical file roles table (15+ Django file types with appropriate tags) -- Edge patterns to look for (URL routing graph, signal wiring, ORM relationships, serializer→model binding) -- Layer assignment guide (7 layers: api, data, service, ui, middleware, config, test) -- Notable `languageLesson` patterns (fat models, ORM lazy evaluation, CBV mixins, signal anti-patterns, app isolation) - -**How it's injected:** SKILL.md reads this file and appends it to the base `file-analyzer-prompt.md` and `architecture-analyzer-prompt.md` content when `Django` appears in the detected frameworks list. - -### `fastapi-analyzer-addendum.md` - -A detailed reference for FastAPI and Flask projects, injected when either framework is detected. Contains two sections: - -**FastAPI section:** -- Canonical file roles (router files, Pydantic schemas, CRUD, dependencies, database session) -- Edge patterns (router inclusion chain, DI tree, Pydantic inheritance, CRUD→model binding) -- Layer assignment guide (7 layers) -- Notable `languageLesson` patterns (DI as composition, Pydantic validation, async vs sync, route order) - -**Flask section:** -- Canonical file roles (blueprints, application factory, WTForms, Marshmallow) -- Edge patterns (blueprint registration, extension coupling, before/after request hooks) -- Layer assignment guide -- Notable `languageLesson` patterns (factory pattern, blueprint modularity, extension `init_app` protocol) - -**How it's injected:** Same mechanism as the Django addendum — SKILL.md appends it when `FastAPI` or `Flask` is in the detected frameworks list. - ---- - -## What Was NOT Changed (And Why) - -| Component | Rationale | -|-----------|-----------| -| Graph schema (`types.ts`, `schema.ts`) | Already language-agnostic. All 18 edge types work for Python patterns. | -| `packages/core/src/plugins/tree-sitter-plugin.ts` | Still TS/JS only. Adding Python tree-sitter support is Phase 3 (separate PR). | -| `packages/core/src/plugins/registry.ts` | Extension map already has `.py → python`. A Python plugin will register here in Phase 3. | -| `packages/core/src/analyzer/language-lesson.ts` | Concept detection patterns. Phase 3 work. | -| Dashboard, search engine, skills | Already language-agnostic. | -| `tour-builder-prompt.md` language lessons example | The TypeScript barrel file example is illustrative only. Python tours will produce Python-specific `languageLesson` strings based on the language-lessons list (which already includes Python patterns). | -| Architecture analyzer `node` script execution | The script analyzes the graph JSON, not the source language. `node` is always correct here. | -| `hooks` directory pattern label | React-specific but harmless — Python projects don't conventionally have `hooks/` directories, so this label will never trigger on Python codebases. | - ---- - -## Testing Checklist for Reviewer - -For a Django project (e.g., a real Django app with `requirements.txt`): -- [ ] `scan-result.json` has `frameworks: ["Django"]` (or similar) -- [ ] `manage.py` is detected as `$ENTRY_POINT` in SKILL.md Phase 0 -- [ ] `manage.py` node gets tags including `entry-point` -- [ ] `urls.py` files get `api-handler`, `routing` tags -- [ ] `models.py` files get `data-model` tag -- [ ] `migrations/` directory files land in `layer:data` -- [ ] Tour Step 1 starts from `manage.py` or `wsgi.py` -- [ ] No TypeScript-specific guidance appears in the analysis output - -For a FastAPI project: -- [ ] `scan-result.json` has `frameworks: ["FastAPI"]` -- [ ] Router files get `api-handler`, `routing` tags -- [ ] Pydantic schema files get `type-definition`, `serialization` tags -- [ ] `dependencies.py` or `deps.py` gets `service` tag -- [ ] `depends_on` edges appear between router files and their dependencies -- [ ] `layer:types` exists with schema files - -For an existing TypeScript project (regression check): -- [ ] No Django/FastAPI addendum content appears in analysis -- [ ] `frameworks: ["React"]` (or whatever was there before) unchanged -- [ ] `src/index.ts` still detected as entry point -- [ ] All existing layer assignments and tour steps unchanged diff --git a/understand-anything-plugin/skills/understand/SKILL.md b/understand-anything-plugin/skills/understand/SKILL.md index fd89b92..35b28d3 100644 --- a/understand-anything-plugin/skills/understand/SKILL.md +++ b/understand-anything-plugin/skills/understand/SKILL.md @@ -54,7 +54,7 @@ Determine whether to run a full analysis or incremental update. find $PROJECT_ROOT -maxdepth 2 -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | head -100 ``` Store as `$DIR_TREE`. - - Detect the project entry point by checking for common patterns (in order): `src/index.ts`, `src/main.ts`, `src/App.tsx`, `index.js`, `main.py`, `manage.py`, `app.py`, `wsgi.py`, `asgi.py`, `run.py`, `__main__.py`, `main.go`, `src/main.rs`. Store first match as `$ENTRY_POINT`. + - Detect the project entry point by checking for common patterns (in order): `src/index.ts`, `src/main.ts`, `src/App.tsx`, `index.js`, `main.py`, `manage.py`, `app.py`, `wsgi.py`, `asgi.py`, `run.py`, `__main__.py`, `main.go`, `cmd/*/main.go`, `src/main.rs`, `src/lib.rs`, `src/main/java/**/Application.java`, `Program.cs`, `config.ru`, `index.php`. Store first match as `$ENTRY_POINT`. --- @@ -98,7 +98,14 @@ After the subagent completes, read `$PROJECT_ROOT/.understand-anything/intermedi Batch the file list from Phase 1 into groups of **5-10 files each** (aim for balanced batch sizes). -For each batch, dispatch a subagent using the prompt template at `./file-analyzer-prompt.md`. Run up to **3 subagents concurrently** using parallel dispatch. Read the template once. If any detected framework is `Django`, also read `./django-analyzer-addendum.md` and append its full content after the base template. If any detected framework is `FastAPI` or `Flask`, also read `./fastapi-analyzer-addendum.md` and append its full content after the base template. Then for each batch pass the combined template content as the subagent's prompt, appending the following additional context: +For each batch, dispatch a subagent using the prompt template at `./file-analyzer-prompt.md`. Run up to **3 subagents concurrently** using parallel dispatch. + +**Build the combined prompt template:** +1. Read the base template at `./file-analyzer-prompt.md`. +2. **Language context injection:** For each language detected in Phase 1, check if `./languages/.md` exists. If it does, read it and append its content after the base template under a `## Language Context` header. +3. **Framework addendum injection:** For each framework detected in Phase 1, check if `./frameworks/.md` exists. If it does, read it and append its full content after the language context. This replaces any hardcoded framework-specific conditionals. + +Then for each batch pass the combined template content as the subagent's prompt, appending the following additional context: > **Additional context from main session:** > @@ -106,15 +113,7 @@ For each batch, dispatch a subagent using the prompt template at `./file-analyze > Frameworks detected: `` > Languages: `` > -> Framework-specific guidance: -> - If React/Next.js: files in `app/` or `pages/` are routes, `components/` are UI, `lib/` or `utils/` are utilities -> - If Express/Fastify: files in `routes/` are API endpoints, `middleware/` is middleware, `models/` or `db/` is data -> - If Python Django: `views.py` are controllers, `models.py` is data, `urls.py` is routing, `templates/` is UI, `serializers.py` is API serialization, `signals.py` is event wiring, `admin.py` is admin registration, `migrations/` is schema history -> - If Python FastAPI: files with `@router.get/post/...` decorators are endpoints, `schemas.py` or `models.py` with Pydantic classes are request/response types, `dependencies.py` or `deps.py` holds `Depends()` providers, `routers/` or `api/` groups route modules -> - If Python Flask: files with `@app.route` or `@blueprint.route` are endpoints, `blueprints/` or `views/` groups route modules, `models.py` with SQLAlchemy classes is data -> - If Go: `cmd/` is entry points, `internal/` is private packages, `pkg/` is public packages -> -> Use this context to produce more accurate summaries and better classify file roles. +> Use the language context and framework addendums (appended above) to produce more accurate summaries and better classify file roles. Fill in batch-specific parameters below and dispatch: @@ -160,7 +159,12 @@ Merge all file-analyzer results into a single set of nodes and edges. Then perfo ## Phase 4 — ARCHITECTURE -Dispatch a subagent using the prompt template at `./architecture-analyzer-prompt.md`. If any detected framework is `Django`, also read `./django-analyzer-addendum.md` and append its full content after the base template. If any detected framework is `FastAPI` or `Flask`, also read `./fastapi-analyzer-addendum.md` and append its full content after the base template. Pass the combined content as the subagent's prompt, appending the following additional context: +**Build the combined prompt template:** +1. Read the base template at `./architecture-analyzer-prompt.md`. +2. **Language context injection:** For each language detected in Phase 1, check if `./languages/.md` exists. If it does, read it and append its content after the base template under a `## Language Context` header. +3. **Framework addendum injection:** For each framework detected in Phase 1, check if `./frameworks/.md` exists. If it does, read it and append its full content after the language context. + +Pass the combined content as the subagent's prompt, appending the following additional context: > **Additional context from main session:** > @@ -171,15 +175,7 @@ Dispatch a subagent using the prompt template at `./architecture-analyzer-prompt > $DIR_TREE > ``` > -> Framework-specific layer hints: -> - If React/Next.js: `app/` or `pages/` → UI Layer, `api/` → API Layer, `lib/` → Service Layer, `components/` → UI Layer -> - If Express: `routes/` → API Layer, `controllers/` → Service Layer, `models/` → Data Layer, `middleware/` → Middleware Layer -> - If Python Django: `views/` or `views.py` → API Layer, `models/` or `models.py` → Data Layer, `templates/` → UI Layer, `management/` → CLI Layer, `serializers.py` → API Layer, `signals.py` → Event Layer, `migrations/` → Data Layer -> - If Python FastAPI: files with router decorators → API Layer, Pydantic schema files → Types Layer, `dependencies.py` or `deps.py` → Service Layer, `routers/` or `api/` → API Layer, database session/engine files → Data Layer -> - If Python Flask: files with `@blueprint.route` → API Layer, `models.py` → Data Layer, `forms.py` → UI Layer, `extensions.py` → Config Layer -> - If Go: `cmd/` → Entry Points, `internal/` → Service Layer, `pkg/` → Shared Library, `api/` → API Layer -> -> Use the directory tree and framework hints to inform layer assignments. Directory structure is strong evidence for layer boundaries. +> Use the directory tree, language context, and framework addendums (appended above) to inform layer assignments. Directory structure is strong evidence for layer boundaries. Pass these parameters in the dispatch prompt: diff --git a/understand-anything-plugin/skills/understand/architecture-analyzer-prompt.md b/understand-anything-plugin/skills/understand/architecture-analyzer-prompt.md index 9728db3..e7b1158 100644 --- a/understand-anything-plugin/skills/understand/architecture-analyzer-prompt.md +++ b/understand-anything-plugin/skills/understand/architecture-analyzer-prompt.md @@ -87,13 +87,31 @@ Classify each directory name against known architectural patterns: | `templatetags` | `utility` | | `signals` | `service` | | `serializers` | `api` | +| `cmd` | `entry` | +| `internal` | `service` | +| `pkg` | `utility` | +| `src/main/java` | `service` | +| `src/test/java` | `test` | +| `dto`, `request`, `response` | `types` | +| `entity` | `data` | +| `controller` | `api` | +| `routers` | `api` | +| `composables` | `service` | +| `blueprints` | `api` | +| `mailers`, `jobs`, `channels` | `service` | +| `bin` | `entry` | Also check file-level patterns: -- Files matching `*.test.*` or `*.spec.*` or `test_*.py` -> `test` +- Files matching `*.test.*` or `*.spec.*` or `test_*.py` or `*_test.go` or `*Test.java` or `*_spec.rb` or `*Test.php` or `*Tests.cs` -> `test` - Files matching `*.d.ts` -> `types` (TypeScript declaration files only) - Files named `index.ts`, `index.js`, or `__init__.py` at a package/directory root -> `entry` - Files named `manage.py` at the project root -> `entry` (Django management entry point) - Files named `wsgi.py` or `asgi.py` -> `config` (Python WSGI/ASGI server config) +- Files named `main.go` at `cmd/*/` -> `entry` (Go binary entry points) +- Files named `main.rs` or `lib.rs` at `src/` -> `entry` (Rust crate roots) +- Files named `Application.java` or `Program.cs` -> `entry` (JVM / .NET entry points) +- Files named `config.ru` -> `entry` (Ruby Rack entry point) +- Files named `Cargo.toml`, `go.mod`, `Gemfile`, `pom.xml`, `build.gradle`, `composer.json` -> `config` (language-level project config) **F. Dependency Direction** diff --git a/understand-anything-plugin/skills/understand/file-analyzer-prompt.md b/understand-anything-plugin/skills/understand/file-analyzer-prompt.md index 0ab0cf1..c948b1f 100644 --- a/understand-anything-plugin/skills/understand/file-analyzer-prompt.md +++ b/understand-anything-plugin/skills/understand/file-analyzer-prompt.md @@ -12,7 +12,7 @@ For each file in the batch provided to you, extract structural data via a script ## Phase 1 -- Structural Extraction Script -Write a script that reads each source file in your batch and extracts deterministic structural information. Choose the best language for this task -- Node.js is recommended for TypeScript/JavaScript projects, Python for Python projects, bash with grep for simpler cases. +Write a script that reads each source file in your batch and extracts deterministic structural information. Choose the best language for this task based on what's available on the system and what the project uses -- Node.js, Python, or bash with grep are all valid choices. ### Script Requirements @@ -172,6 +172,12 @@ Indicators from script data: - Named `index.ts` or `index.js` at a directory root with re-exports = `entry-point` (JavaScript/TypeScript barrel) - Named `__init__.py` at a package root with imports or re-exports = `entry-point` (Python package barrel) - Named `manage.py` = `entry-point` (Django management script) +- Named `main.go` in `cmd/` directory = `entry-point` (Go binary) +- Named `main.rs` or `lib.rs` in `src/` = `entry-point` (Rust crate root) +- Named `Application.java` or `Main.java` = `entry-point` (Java application) +- Named `Program.cs` = `entry-point` (.NET application) +- Named `config.ru` = `entry-point` (Ruby Rack server) +- Named `mod.rs` in a directory = `barrel` (Rust module barrel) **Language Notes** (optional, your expert judgment): If the structural data reveals notable language-specific patterns (e.g., many generic type parameters, decorator usage, complex trait bounds), add a brief `languageNotes` string. Only add this when genuinely educational. diff --git a/understand-anything-plugin/skills/understand/django-analyzer-addendum.md b/understand-anything-plugin/skills/understand/frameworks/django.md similarity index 100% rename from understand-anything-plugin/skills/understand/django-analyzer-addendum.md rename to understand-anything-plugin/skills/understand/frameworks/django.md diff --git a/understand-anything-plugin/skills/understand/frameworks/express.md b/understand-anything-plugin/skills/understand/frameworks/express.md new file mode 100644 index 0000000..2970354 --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/express.md @@ -0,0 +1,57 @@ +# Express Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when Express is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## Express Project Structure + +When analyzing an Express project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `app.js`, `app.ts` | Application entry point — creates Express app, mounts middleware and routes | `entry-point`, `config` | +| `server.js`, `server.ts`, `index.js`, `index.ts` | Server bootstrap — starts HTTP listener, may import app | `entry-point`, `config` | +| `routes/*.js`, `routes/*.ts` | Route definitions — map HTTP methods and paths to handlers | `api-handler`, `routing` | +| `controllers/*.js`, `controllers/*.ts` | Request handlers — process requests, orchestrate services, return responses | `api-handler`, `service` | +| `models/*.js`, `models/*.ts` | Data models — Mongoose schemas, Sequelize models, or plain data definitions | `data-model` | +| `middleware/*.js`, `middleware/*.ts` | Middleware functions — authentication, logging, validation, error handling | `middleware` | +| `services/*.js`, `services/*.ts` | Business logic — domain operations decoupled from HTTP layer | `service` | +| `db/*.js`, `db/*.ts`, `database/*.js` | Database connection and configuration | `data-model`, `config` | +| `config/*.js`, `config/*.ts` | Application configuration — environment variables, feature flags | `config` | +| `validators/*.js`, `validators/*.ts` | Request validation schemas (Joi, Zod, express-validator) | `validation`, `utility` | +| `utils/*.js`, `utils/*.ts` | Shared utility functions | `utility` | +| `tests/*.js`, `test/*.js`, `__tests__/*.js` | Unit and integration tests | `test` | + +### Edge Patterns to Look For + +**Route mounting** — When `app.use('/api/users', usersRouter)` mounts a router, create `depends_on` edges from the main app to the router module. These edges represent the HTTP routing tree. + +**Middleware chain** — When `app.use(cors())`, `app.use(authMiddleware)`, or `router.use(validate)` registers middleware, create middleware edges from the app or router to the middleware function. Order matters — middleware executes in registration order. + +**Controller-to-service calls** — When a controller imports and calls a service function, create `depends_on` edges from the controller to the service. This represents the separation between HTTP handling and business logic. + +**Model relationships** — When models reference each other (Mongoose `ref`, Sequelize associations), create `depends_on` edges between model files with descriptions indicating the relationship type. + +### Architectural Layers for Express + +Assign nodes to these layers when detected: + +| Layer ID | Layer Name | What Goes Here | +|---|---|---| +| `layer:api` | API Layer | `routes/`, `controllers/`, request validators | +| `layer:data` | Data Layer | `models/`, `db/`, migration files, seeders | +| `layer:service` | Service Layer | `services/`, business logic modules | +| `layer:middleware` | Middleware Layer | `middleware/`, error handlers, authentication, logging | +| `layer:config` | Config Layer | `app.js`, `config/`, environment setup, `server.js` | +| `layer:utility` | Utility Layer | `utils/`, `helpers/`, shared pure functions | +| `layer:test` | Test Layer | `tests/`, `__tests__/`, `*.test.js`, `*.spec.js` | + +### Notable Patterns to Capture in languageLesson + +- **Middleware chain (req, res, next)**: Express processes requests through a pipeline of middleware functions — each receives the request, response, and a `next()` callback to pass control forward +- **Error-handling middleware (4 params)**: Middleware with signature `(err, req, res, next)` catches errors — must be registered after all routes to act as a global error handler +- **Router modularity**: `express.Router()` creates modular, mountable route handlers that can be composed into the main app at different path prefixes +- **MVC pattern**: Express apps commonly separate concerns into Models (data), Views (response formatting), and Controllers (request handling) +- **Body parsing and validation**: Request body parsing (`express.json()`, `express.urlencoded()`) and validation (Joi, Zod, express-validator) are middleware concerns applied before route handlers diff --git a/understand-anything-plugin/skills/understand/fastapi-analyzer-addendum.md b/understand-anything-plugin/skills/understand/frameworks/fastapi.md similarity index 55% rename from understand-anything-plugin/skills/understand/fastapi-analyzer-addendum.md rename to understand-anything-plugin/skills/understand/frameworks/fastapi.md index d8a71c6..79431a2 100644 --- a/understand-anything-plugin/skills/understand/fastapi-analyzer-addendum.md +++ b/understand-anything-plugin/skills/understand/frameworks/fastapi.md @@ -1,13 +1,13 @@ -# FastAPI / Flask Framework Addendum +# FastAPI Framework Addendum -> Injected into file-analyzer and architecture-analyzer prompts when FastAPI or Flask is detected. +> Injected into file-analyzer and architecture-analyzer prompts when FastAPI is detected. > Do NOT use as a standalone prompt — always appended to the base prompt template. ## FastAPI Project Structure When analyzing a FastAPI project, apply these additional conventions on top of the base analysis rules. -### Canonical File Roles — FastAPI +### Canonical File Roles | File / Pattern | Role | Tags | |---|---|---| @@ -26,7 +26,7 @@ When analyzing a FastAPI project, apply these additional conventions on top of t | `*/tests/*.py`, `test_*.py` | pytest test files | `test` | | `conftest.py` | pytest fixtures and test configuration | `test`, `config` | -### Edge Patterns to Look For — FastAPI +### Edge Patterns to Look For **Router inclusion chain** — When `app.include_router(some_router, prefix="/api")` appears in `main.py` or a router aggregator, create `imports` + `depends_on` edges from the main app file to each router module. This builds the URL hierarchy graph. @@ -56,54 +56,3 @@ When analyzing a FastAPI project, apply these additional conventions on top of t - **Pydantic for validation**: Request bodies, query params, and path params are automatically validated by Pydantic — invalid input raises `422 Unprocessable Entity` before your code runs - **Async endpoints**: `async def` routes run in the event loop; `def` routes run in a threadpool — mixing them incorrectly can cause performance issues - **Path operation order**: FastAPI matches routes in declaration order; a catch-all route before a specific one will shadow it - ---- - -## Flask Project Structure - -When analyzing a Flask project, apply these additional conventions on top of the base analysis rules. - -### Canonical File Roles — Flask - -| File / Pattern | Role | Tags | -|---|---|---| -| `app.py`, `__init__.py` (in app package) | Application factory (`create_app()`) or direct `Flask(__name__)` instance | `entry-point`, `config` | -| `run.py`, `wsgi.py` | Production/dev server entry point | `entry-point`, `config` | -| `*/views.py`, `*/routes.py` | Route handler functions with `@app.route` or `@blueprint.route` | `api-handler`, `routing` | -| `*/blueprints/*.py`, `*/api/*.py` | Blueprint modules — group routes by feature | `api-handler`, `routing` | -| `*/models.py` | SQLAlchemy models or other ORM models | `data-model` | -| `*/forms.py` | WTForms form classes | `validation`, `ui` | -| `*/schemas.py` | Marshmallow serialization schemas | `serialization`, `type-definition` | -| `*/config.py` | Config classes (`DevelopmentConfig`, `ProductionConfig`) | `config` | -| `*/extensions.py` | Flask extension initialization (`db = SQLAlchemy()`, `login_manager = LoginManager()`) | `config`, `singleton` | -| `*/decorators.py` | Custom route decorators (auth guards, rate limiting) | `middleware`, `utility` | -| `*/utils.py`, `*/helpers.py` | Shared utility functions | `utility` | -| `*/templates/**/*.html` | Jinja2 templates | `ui` | -| `*/static/` | CSS, JS, and asset files | `assets` | -| `*/tests/*.py`, `test_*.py` | pytest or unittest test files | `test` | - -### Edge Patterns to Look For — Flask - -**Blueprint registration** — When `app.register_blueprint(bp, url_prefix='/api')` appears in the application factory, create `depends_on` edges from the app factory to each blueprint module. - -**Extension coupling** — When a view imports from `extensions.py` (e.g., `from .extensions import db, login_manager`), create `imports` edges to show which views depend on which extensions. - -**Before/after request hooks** — When `@app.before_request` or `@blueprint.before_request` decorates a function, create `middleware` edges from those functions to the app/blueprint they attach to. - -### Architectural Layers for Flask - -| Layer ID | Layer Name | What Goes Here | -|---|---|---| -| `layer:api` | API Layer | Blueprint route files, view functions | -| `layer:data` | Data Layer | `models.py`, database migration files | -| `layer:service` | Service Layer | Business logic modules, `schemas.py`, service classes | -| `layer:ui` | UI Layer | `templates/`, `forms.py`, `static/` | -| `layer:config` | Config Layer | `app.py` factory, `config.py`, `extensions.py` | -| `layer:middleware` | Middleware Layer | `decorators.py`, before/after request hooks | -| `layer:test` | Test Layer | Test files, `conftest.py` | - -### Notable Patterns to Capture in languageLesson - -- **Application factory pattern**: `create_app()` functions allow multiple app instances (e.g., for testing) and delay extension initialization — avoids circular imports -- **Blueprint modularity**: Blueprints group related routes, templates, and static files; they are registered on the app with a URL prefix, making them independently testable -- **Flask extension protocol**: Extensions follow `init_app(app)` for lazy initialization — the extension object is created globally but bound to an app instance later diff --git a/understand-anything-plugin/skills/understand/frameworks/flask.md b/understand-anything-plugin/skills/understand/frameworks/flask.md new file mode 100644 index 0000000..b1df89f --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/flask.md @@ -0,0 +1,53 @@ +# Flask Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when Flask is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## Flask Project Structure + +When analyzing a Flask project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `app.py`, `__init__.py` (in app package) | Application factory (`create_app()`) or direct `Flask(__name__)` instance | `entry-point`, `config` | +| `run.py`, `wsgi.py` | Production/dev server entry point | `entry-point`, `config` | +| `*/views.py`, `*/routes.py` | Route handler functions with `@app.route` or `@blueprint.route` | `api-handler`, `routing` | +| `*/blueprints/*.py`, `*/api/*.py` | Blueprint modules — group routes by feature | `api-handler`, `routing` | +| `*/models.py` | SQLAlchemy models or other ORM models | `data-model` | +| `*/forms.py` | WTForms form classes | `validation`, `ui` | +| `*/schemas.py` | Marshmallow serialization schemas | `serialization`, `type-definition` | +| `*/config.py` | Config classes (`DevelopmentConfig`, `ProductionConfig`) | `config` | +| `*/extensions.py` | Flask extension initialization (`db = SQLAlchemy()`, `login_manager = LoginManager()`) | `config`, `singleton` | +| `*/decorators.py` | Custom route decorators (auth guards, rate limiting) | `middleware`, `utility` | +| `*/utils.py`, `*/helpers.py` | Shared utility functions | `utility` | +| `*/templates/**/*.html` | Jinja2 templates | `ui` | +| `*/static/` | CSS, JS, and asset files | `assets` | +| `*/tests/*.py`, `test_*.py` | pytest or unittest test files | `test` | + +### Edge Patterns to Look For + +**Blueprint registration** — When `app.register_blueprint(bp, url_prefix='/api')` appears in the application factory, create `depends_on` edges from the app factory to each blueprint module. + +**Extension coupling** — When a view imports from `extensions.py` (e.g., `from .extensions import db, login_manager`), create `imports` edges to show which views depend on which extensions. + +**Before/after request hooks** — When `@app.before_request` or `@blueprint.before_request` decorates a function, create `middleware` edges from those functions to the app/blueprint they attach to. + +### Architectural Layers for Flask + +| Layer ID | Layer Name | What Goes Here | +|---|---|---| +| `layer:api` | API Layer | Blueprint route files, view functions | +| `layer:data` | Data Layer | `models.py`, database migration files | +| `layer:service` | Service Layer | Business logic modules, `schemas.py`, service classes | +| `layer:ui` | UI Layer | `templates/`, `forms.py`, `static/` | +| `layer:config` | Config Layer | `app.py` factory, `config.py`, `extensions.py` | +| `layer:middleware` | Middleware Layer | `decorators.py`, before/after request hooks | +| `layer:test` | Test Layer | Test files, `conftest.py` | + +### Notable Patterns to Capture in languageLesson + +- **Application factory pattern**: `create_app()` functions allow multiple app instances (e.g., for testing) and delay extension initialization — avoids circular imports +- **Blueprint modularity**: Blueprints group related routes, templates, and static files; they are registered on the app with a URL prefix, making them independently testable +- **Flask extension protocol**: Extensions follow `init_app(app)` for lazy initialization — the extension object is created globally but bound to an app instance later diff --git a/understand-anything-plugin/skills/understand/frameworks/gin.md b/understand-anything-plugin/skills/understand/frameworks/gin.md new file mode 100644 index 0000000..494c27d --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/gin.md @@ -0,0 +1,59 @@ +# Gin (Go) Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when Gin is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## Gin Project Structure + +When analyzing a Gin project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `main.go` | Application entry point — initializes the Gin engine, registers routes, starts the server | `entry-point`, `config` | +| `cmd/*.go`, `cmd/**/*.go` | CLI entry points — multiple binaries in a multi-command project | `entry-point`, `config` | +| `handlers/*.go`, `handler/*.go` | HTTP handlers — process requests with `gin.Context` | `api-handler` | +| `controllers/*.go`, `controller/*.go` | Controllers — alternative naming for HTTP handlers | `api-handler` | +| `routes/*.go`, `router/*.go` | Route definitions — register endpoints and route groups | `routing`, `config` | +| `models/*.go`, `model/*.go` | Data models — struct definitions mapped to database tables | `data-model` | +| `middleware/*.go` | Middleware functions — authentication, logging, CORS, rate limiting | `middleware` | +| `services/*.go`, `service/*.go` | Business logic — domain operations decoupled from HTTP layer | `service` | +| `repository/*.go`, `repo/*.go` | Data access layer — database queries and persistence logic | `data-model`, `service` | +| `config/*.go`, `config.go` | Application configuration — environment loading, struct-based config | `config` | +| `dto/*.go` | Data transfer objects — request and response structs | `type-definition` | +| `utils/*.go`, `pkg/*.go` | Shared utility packages | `utility` | +| `*_test.go` | Unit and integration tests | `test` | + +### Edge Patterns to Look For + +**Route group registration** — When `r.Group("/api")` creates a route group and registers handlers, create `configures` edges from the route definition file to each handler. Route groups organize endpoints by prefix and shared middleware. + +**Handler-to-service calls** — When a handler function calls a service method, create `depends_on` edges from the handler to the service. This represents the separation between HTTP handling and business logic. + +**Service-to-repository calls** — When a service calls a repository method for data access, create `depends_on` edges from the service to the repository. This represents the data access abstraction. + +**Middleware chaining** — When `r.Use(middleware)` or a route group applies middleware, create middleware edges from the router or group to the middleware function. Middleware executes in registration order. + +### Architectural Layers for Gin + +Assign nodes to these layers when detected: + +| Layer ID | Layer Name | What Goes Here | +|---|---|---| +| `layer:api` | API Layer | `handlers/`, `controllers/`, HTTP handler functions | +| `layer:data` | Data Layer | `models/`, `repository/`, database access, migrations | +| `layer:service` | Service Layer | `services/`, business logic | +| `layer:middleware` | Middleware Layer | `middleware/`, authentication, logging, rate limiting | +| `layer:config` | Config Layer | `main.go`, `routes/`, `config/`, environment setup | +| `layer:utility` | Utility Layer | `utils/`, `pkg/`, shared helper packages | +| `layer:test` | Test Layer | `*_test.go`, test fixtures, test helpers | + +### Notable Patterns to Capture in languageLesson + +- **Handler functions with gin.Context**: Every Gin handler receives a `*gin.Context` parameter — it provides request parsing (`c.Bind`, `c.Param`, `c.Query`), response writing (`c.JSON`, `c.HTML`), and control flow (`c.Abort`, `c.Next`) +- **Middleware chain with c.Next()**: Middleware calls `c.Next()` to pass control to the next handler in the chain — code before `c.Next()` runs pre-handler, code after runs post-handler +- **Route grouping for modular APIs**: `r.Group("/v1")` creates modular sub-routers that can have their own middleware stack — enables versioning and access control at the group level +- **Dependency injection via constructors (no framework DI)**: Go has no DI framework — dependencies are passed as constructor parameters (e.g., `NewUserHandler(userService)`) and stored as struct fields +- **Interface-driven design for testability**: Services and repositories are defined as interfaces — handlers depend on the interface, enabling mock implementations in tests +- **Error handling with gin.Error**: Gin collects errors via `c.Error(err)` — middleware can inspect `c.Errors` after handler execution to implement centralized error logging and response formatting diff --git a/understand-anything-plugin/skills/understand/frameworks/nextjs.md b/understand-anything-plugin/skills/understand/frameworks/nextjs.md new file mode 100644 index 0000000..6b9a93c --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/nextjs.md @@ -0,0 +1,59 @@ +# Next.js Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when Next.js is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## Next.js Project Structure + +When analyzing a Next.js project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `app/layout.tsx` | Root layout — wraps all pages, defines HTML shell and global providers | `entry-point`, `config`, `ui` | +| `app/page.tsx` | Root page component — renders at `/` | `ui`, `routing` | +| `app/**/page.tsx` | Route page components — file path determines URL | `ui`, `routing` | +| `app/**/layout.tsx` | Nested layouts — wrap child routes with shared UI | `ui`, `config` | +| `app/**/loading.tsx` | Loading UI — shown as Suspense fallback during route transitions | `ui` | +| `app/**/error.tsx` | Error boundary — catches errors in the route segment | `ui` | +| `app/**/not-found.tsx` | 404 UI — shown when `notFound()` is called | `ui` | +| `app/api/**/route.ts` | API route handlers — serverless endpoint functions (GET, POST, etc.) | `api-handler` | +| `middleware.ts` | Edge middleware — intercepts requests before they reach routes | `middleware` | +| `lib/*.ts`, `lib/**/*.ts` | Shared server-side utilities, data access, and business logic | `service` | +| `components/*.tsx`, `components/**/*.tsx` | Reusable UI components | `ui` | +| `next.config.js`, `next.config.mjs`, `next.config.ts` | Next.js configuration — redirects, rewrites, env, webpack overrides | `config` | +| `actions/*.ts`, `app/**/actions.ts` | Server Actions — server-side mutation functions callable from client | `service`, `api-handler` | + +### Edge Patterns to Look For + +**Layout nesting** — When `app/foo/layout.tsx` wraps `app/foo/page.tsx` and `app/foo/bar/page.tsx`, create `contains` edges from the layout to the pages it wraps. Layouts compose via the file-system hierarchy. + +**API route handlers** — When a `route.ts` file exports named functions (GET, POST, PUT, DELETE), create edges from consuming components or server actions to the route handler based on fetch calls. + +**Server/Client component boundary** — Files with `"use client"` directive at the top are Client Components. All other components in the `app/` directory are Server Components by default. Create `depends_on` edges that cross this boundary and note the boundary in the edge description. + +**Parallel routes** — When `app/@slot/page.tsx` patterns appear, create `contains` edges from the parent layout to each parallel slot. These render simultaneously in the same layout. + +**Route groups** — Directories wrapped in parentheses `(group)` organize routes without affecting the URL path. Note these in node descriptions. + +### Architectural Layers for Next.js + +Assign nodes to these layers when detected: + +| Layer ID | Layer Name | What Goes Here | +|---|---|---| +| `layer:ui` | UI Layer | `app/**/page.tsx`, `app/**/layout.tsx`, `components/`, loading/error boundaries | +| `layer:api` | API Layer | `app/api/**/route.ts`, API route handlers | +| `layer:service` | Service Layer | `lib/`, server actions, data-fetching utilities | +| `layer:middleware` | Middleware Layer | `middleware.ts`, edge functions | +| `layer:config` | Config Layer | `next.config.*`, root layout, `tailwind.config.*`, environment setup | +| `layer:test` | Test Layer | `__tests__/`, `*.test.tsx`, `*.spec.tsx`, `e2e/` | + +### Notable Patterns to Capture in languageLesson + +- **Server Components by default**: Components in the `app/` directory are Server Components — no JavaScript is sent to the client unless `"use client"` is declared +- **Server Actions for mutations**: Functions marked with `"use server"` can be called directly from client components, replacing traditional API routes for form submissions and mutations +- **App Router file conventions**: Special files (`page`, `layout`, `loading`, `error`, `not-found`, `route`) define behavior by naming convention within the file-system router +- **ISR and static generation**: `generateStaticParams` pre-renders pages at build time; revalidation strategies control cache freshness +- **Parallel and intercepting routes**: `@slot` directories enable parallel rendering; `(.)` prefix directories enable route interception for modal patterns diff --git a/understand-anything-plugin/skills/understand/frameworks/rails.md b/understand-anything-plugin/skills/understand/frameworks/rails.md new file mode 100644 index 0000000..570ef10 --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/rails.md @@ -0,0 +1,65 @@ +# Ruby on Rails Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when Rails is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## Rails Project Structure + +When analyzing a Ruby on Rails project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `config.ru` | Rack entry point — boots the Rails application for the web server | `entry-point` | +| `config/application.rb` | Application configuration — sets up Rails, loads gems, configures middleware | `entry-point`, `config` | +| `app/controllers/*_controller.rb` | Controllers — handle HTTP requests, orchestrate models, render responses | `api-handler` | +| `app/controllers/concerns/*.rb` | Controller concerns — shared controller behavior via mixins | `middleware`, `utility` | +| `app/models/*.rb` | ActiveRecord models — map to database tables, contain validations and associations | `data-model` | +| `app/models/concerns/*.rb` | Model concerns — shared model behavior via mixins | `utility` | +| `app/views/**/*.erb`, `app/views/**/*.haml` | View templates — HTML rendering with embedded Ruby | `ui` | +| `app/helpers/*_helper.rb` | View helpers — utility methods available in templates | `utility` | +| `app/mailers/*_mailer.rb` | Action Mailer classes — send email notifications | `service` | +| `app/jobs/*_job.rb` | Active Job classes — background job processing | `service` | +| `app/channels/*_channel.rb` | Action Cable channels — WebSocket communication | `service` | +| `app/serializers/*_serializer.rb` | API serializers — JSON response formatting (ActiveModelSerializers, Blueprinter) | `api-handler`, `utility` | +| `app/services/*.rb` | Service objects — encapsulate complex business logic | `service` | +| `db/migrate/*.rb` | Database migrations — schema changes versioned by timestamp | `config`, `data-model` | +| `db/schema.rb`, `db/structure.sql` | Generated schema snapshot — current database structure | `data-model`, `config` | +| `config/routes.rb` | Route definitions — maps URLs to controller actions | `routing`, `config` | +| `config/initializers/*.rb` | Initializers — run once at boot to configure gems and services | `config` | +| `lib/**/*.rb` | Library code — custom classes, Rake tasks, extensions | `utility`, `service` | +| `spec/**/*_spec.rb`, `test/**/*_test.rb` | RSpec or Minitest test files | `test` | + +### Edge Patterns to Look For + +**Route-to-controller mapping** — When `config/routes.rb` defines `resources :users` or `get '/foo', to: 'bar#baz'`, create `configures` edges from the routes file to the corresponding controller. RESTful resources generate a full set of action mappings. + +**ActiveRecord associations** — When models define `has_many`, `belongs_to`, `has_one`, or `has_and_belongs_to_many`, create `depends_on` edges between model files with descriptions indicating the association type and direction. + +**Controller-to-model** — When a controller calls model methods (`User.find`, `@post.save`), create `depends_on` edges from the controller to the model. Controllers are the primary consumers of model data. + +**Callbacks** — When models or controllers use `before_action`, `after_save`, `before_validation`, or similar callbacks, note these as middleware-like edges. Callbacks create implicit execution paths that are not visible from the call site. + +### Architectural Layers for Rails + +Assign nodes to these layers when detected: + +| Layer ID | Layer Name | What Goes Here | +|---|---|---| +| `layer:api` | API Layer | `app/controllers/`, `app/serializers/`, API-specific controllers | +| `layer:data` | Data Layer | `app/models/`, `db/migrate/`, `db/schema.rb` | +| `layer:ui` | UI Layer | `app/views/`, `app/helpers/`, `app/assets/`, `app/javascript/` | +| `layer:service` | Service Layer | `app/mailers/`, `app/jobs/`, `app/channels/`, `app/services/`, `lib/` | +| `layer:config` | Config Layer | `config/routes.rb`, `config/initializers/`, `config/application.rb`, `config.ru` | +| `layer:middleware` | Middleware Layer | `app/middleware/`, controller concerns, Rack middleware | +| `layer:test` | Test Layer | `spec/`, `test/`, `*.spec.rb`, `*_test.rb` | + +### Notable Patterns to Capture in languageLesson + +- **Convention over configuration**: Rails derives routing, table names, and file locations from naming conventions — `UsersController` maps to `users_controller.rb`, handles `/users`, and queries the `users` table +- **ActiveRecord pattern**: Models are database wrappers — each model class maps to a table, instances map to rows, and attributes map to columns with automatic type coercion +- **Concerns for shared behavior**: `ActiveSupport::Concern` modules are mixins included in models or controllers to share validations, scopes, callbacks, and methods across classes +- **Strong parameters for mass-assignment protection**: `params.require(:user).permit(:name, :email)` whitelists attributes — controllers must explicitly declare which fields can be set from user input +- **RESTful resource routing**: `resources :posts` generates seven standard CRUD routes — Rails strongly encourages RESTful design where each controller maps to a resource +- **Callbacks and observers**: `before_save`, `after_create`, and similar callbacks inject logic into the object lifecycle — they create invisible execution paths that can be difficult to trace diff --git a/understand-anything-plugin/skills/understand/frameworks/react.md b/understand-anything-plugin/skills/understand/frameworks/react.md new file mode 100644 index 0000000..d36eb39 --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/react.md @@ -0,0 +1,55 @@ +# React Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when React is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## React Project Structure + +When analyzing a React project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `src/App.tsx` | Root application component — mounts providers, router, and top-level layout | `entry-point`, `ui` | +| `components/*.tsx`, `components/**/*.tsx` | Reusable UI components | `ui` | +| `hooks/*.ts`, `hooks/*.tsx` | Custom React hooks — encapsulate reusable stateful logic | `service`, `utility` | +| `contexts/*.tsx`, `context/*.tsx` | React Context providers and consumers — shared state across component tree | `service`, `state` | +| `pages/*.tsx`, `views/*.tsx` | Page-level components mapped to routes | `ui`, `routing` | +| `utils/*.ts`, `helpers/*.ts` | Pure utility functions — formatting, validation, transformations | `utility` | +| `types/*.ts`, `types/*.d.ts` | TypeScript type definitions and interfaces | `type-definition` | +| `services/*.ts`, `api/*.ts` | API client functions and data-fetching logic | `service` | +| `store/*.ts`, `slices/*.ts` | State management (Redux, Zustand, etc.) | `service`, `state` | +| `constants/*.ts` | Application-wide constants and enums | `config` | +| `__tests__/*.tsx`, `*.test.tsx`, `*.spec.tsx` | Unit and integration tests | `test` | + +### Edge Patterns to Look For + +**Component composition** — When a parent component renders a child component in its JSX return, create `contains` edges from the parent to the child. These edges represent the component tree hierarchy. + +**Hook usage** — When a component or hook imports and calls a custom hook (`useX`), create `depends_on` edges from the consumer to the hook module. Hooks are the primary mechanism for shared logic in React. + +**Context provider/consumer** — When a Context provider wraps components, create `publishes` edges from the provider to the context definition. When components call `useContext` or use a custom context hook, create `subscribes` edges from the consumer to the context. + +**Props drilling chains** — When props are passed through multiple component layers without being used, create `depends_on` edges along the chain to surface the coupling depth. + +### Architectural Layers for React + +Assign nodes to these layers when detected: + +| Layer ID | Layer Name | What Goes Here | +|---|---|---| +| `layer:ui` | UI Layer | `components/`, `pages/`, `views/`, layout components | +| `layer:service` | Service Layer | `hooks/`, `contexts/`, `services/`, `api/`, `store/` | +| `layer:types` | Types Layer | `types/`, shared TypeScript interfaces and type definitions | +| `layer:utility` | Utility Layer | `utils/`, `helpers/`, pure functions | +| `layer:config` | Config Layer | `App.tsx`, router configuration, provider setup, constants | +| `layer:test` | Test Layer | `__tests__/`, `*.test.tsx`, `*.spec.tsx` | + +### Notable Patterns to Capture in languageLesson + +- **Component composition over inheritance**: React favors composing components via props and children rather than class inheritance hierarchies +- **Custom hooks for reusable logic**: Hooks prefixed with `use` extract stateful logic into shareable modules without changing the component tree +- **React.memo for performance**: Components wrapped in `React.memo` skip re-renders when props are unchanged — indicates performance-sensitive paths +- **Controlled vs. uncontrolled components**: Controlled components derive state from props; uncontrolled components manage internal state via refs +- **Render props pattern**: Components that accept a function as children or a render prop to delegate rendering decisions to the consumer diff --git a/understand-anything-plugin/skills/understand/frameworks/spring.md b/understand-anything-plugin/skills/understand/frameworks/spring.md new file mode 100644 index 0000000..0c5bac4 --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/spring.md @@ -0,0 +1,59 @@ +# Spring Boot Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when Spring Boot is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## Spring Boot Project Structure + +When analyzing a Spring Boot project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `*Application.java`, `*Application.kt` | Application entry point — `@SpringBootApplication` class with `main()` method | `entry-point`, `config` | +| `*Controller.java`, `*RestController.java` | REST controllers — handle HTTP requests, delegate to services | `api-handler` | +| `*Service.java` | Service interfaces — define business operation contracts | `service` | +| `*ServiceImpl.java` | Service implementations — contain business logic | `service` | +| `*Repository.java` | Spring Data repositories — data access interfaces extending JpaRepository/CrudRepository | `data-model` | +| `*Entity.java` | JPA entities — map to database tables via `@Entity` annotation | `data-model` | +| `*DTO.java`, `*Request.java`, `*Response.java` | Data transfer objects — request/response payloads | `type-definition` | +| `*Config.java`, `*Configuration.java` | Configuration classes — `@Configuration` beans, security config, web config | `config` | +| `*Filter.java` | Servlet filters — intercept requests before they reach controllers | `middleware` | +| `*Interceptor.java` | Handler interceptors — pre/post processing around controller methods | `middleware` | +| `*Advice.java`, `*ExceptionHandler.java` | Controller advice — global exception handling and response wrapping | `middleware` | +| `*Mapper.java` | Object mappers — convert between entities and DTOs (MapStruct, ModelMapper) | `utility` | +| `application.yml`, `application.properties` | Application configuration — profiles, datasource, server settings | `config` | +| `*Test.java`, `*Tests.java`, `*IT.java` | Unit tests, integration tests | `test` | + +### Edge Patterns to Look For + +**@Autowired injection** — When a class injects a dependency via `@Autowired`, constructor injection, or `@Inject`, create `depends_on` edges from the consumer to the injected bean. Constructor injection is preferred and most common in modern Spring. + +**Controller-Service-Repository chain** — The canonical call chain is `@RestController` -> `@Service` -> `@Repository`. Create `depends_on` edges along this chain to show the layered architecture. + +**@Entity relationships** — When entities define `@OneToMany`, `@ManyToOne`, `@OneToOne`, or `@ManyToMany` annotations, create `depends_on` edges between entity classes with descriptions indicating the relationship type and direction. + +**@Configuration bean definitions** — When a `@Configuration` class defines `@Bean` methods, create `configures` edges from the configuration class to the types it produces. These beans become available for injection throughout the application. + +### Architectural Layers for Spring Boot + +Assign nodes to these layers when detected: + +| Layer ID | Layer Name | What Goes Here | +|---|---|---| +| `layer:api` | API Layer | `*Controller.java`, REST endpoints, API documentation | +| `layer:service` | Service Layer | `*Service.java`, `*ServiceImpl.java`, business logic | +| `layer:data` | Data Layer | `*Repository.java`, `*Entity.java`, JPA mappings, database migrations | +| `layer:types` | Types Layer | `*DTO.java`, `*Request.java`, `*Response.java`, shared value objects | +| `layer:config` | Config Layer | `*Configuration.java`, `application.yml`, security config, `*Application.java` | +| `layer:middleware` | Middleware Layer | `*Filter.java`, `*Interceptor.java`, `*Advice.java`, security filters | +| `layer:test` | Test Layer | `*Test.java`, `*Tests.java`, `*IT.java`, test configuration | + +### Notable Patterns to Capture in languageLesson + +- **Dependency injection via constructor injection**: Spring favors constructor injection over field injection (`@Autowired` on fields) — it makes dependencies explicit, supports immutability, and simplifies testing +- **Layered architecture (Controller -> Service -> Repository)**: Spring Boot applications follow a strict layered pattern where controllers handle HTTP, services contain business logic, and repositories manage persistence +- **Spring Security filter chain**: Security is implemented as a chain of servlet filters — `SecurityFilterChain` beans configure authentication, authorization, CORS, and CSRF protection +- **JPA entity lifecycle**: Entities transition through states (transient, managed, detached, removed) — understanding this lifecycle is essential for tracing data flow through the persistence layer +- **AOP for cross-cutting concerns**: `@Aspect` classes with `@Before`, `@After`, and `@Around` advice inject behavior at join points — used for logging, transactions (`@Transactional`), and caching (`@Cacheable`) diff --git a/understand-anything-plugin/skills/understand/frameworks/vue.md b/understand-anything-plugin/skills/understand/frameworks/vue.md new file mode 100644 index 0000000..fdd3419 --- /dev/null +++ b/understand-anything-plugin/skills/understand/frameworks/vue.md @@ -0,0 +1,59 @@ +# Vue Framework Addendum + +> Injected into file-analyzer and architecture-analyzer prompts when Vue is detected. +> Do NOT use as a standalone prompt — always appended to the base prompt template. + +## Vue Project Structure + +When analyzing a Vue project, apply these additional conventions on top of the base analysis rules. + +### Canonical File Roles + +| File / Pattern | Role | Tags | +|---|---|---| +| `src/App.vue` | Root application component — mounts the top-level layout and router view | `entry-point`, `ui` | +| `src/main.ts`, `src/main.js` | Application bootstrap — creates Vue app instance, registers plugins, mounts to DOM | `entry-point`, `config` | +| `components/*.vue`, `components/**/*.vue` | Reusable UI components | `ui` | +| `views/*.vue`, `pages/*.vue` | Page-level components mapped to routes | `ui`, `routing` | +| `composables/*.ts`, `composables/*.js` | Composable functions — reusable stateful logic using Composition API | `service`, `utility` | +| `store/*.ts`, `stores/*.ts` | State management modules (Pinia stores or Vuex modules) | `service`, `state` | +| `router/*.ts`, `router/index.ts` | Vue Router configuration — route definitions, navigation guards | `config`, `routing` | +| `plugins/*.ts`, `plugins/*.js` | Vue plugin registrations — extend app functionality (i18n, auth, etc.) | `config` | +| `utils/*.ts`, `helpers/*.ts` | Pure utility functions | `utility` | +| `types/*.ts`, `types/*.d.ts` | TypeScript type definitions and interfaces | `type-definition` | +| `api/*.ts`, `services/*.ts` | API client functions and data-fetching logic | `service` | +| `directives/*.ts` | Custom Vue directives | `utility` | +| `tests/*.spec.ts`, `__tests__/*.spec.ts` | Unit and integration tests | `test` | + +### Edge Patterns to Look For + +**Component parent-child** — When a parent component uses a child component in its `