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 index c3a8b75..289f3db 100644 --- a/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts +++ b/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts @@ -32,9 +32,9 @@ describe("LanguageRegistry", () => { expect(registry.getForFile("file.unknown")).toBeNull(); }); - it("returns null for files without extensions", () => { + it("returns null for files without extensions and no filename match", () => { const registry = new LanguageRegistry(); - expect(registry.getForFile("Makefile")).toBeNull(); + expect(registry.getForFile("SOMEFILE")).toBeNull(); }); it("lists all registered languages", () => { @@ -48,10 +48,10 @@ describe("LanguageRegistry", () => { }); describe("createDefault", () => { - it("registers all 12 built-in language configs", () => { + it("registers all 38 built-in language configs", () => { const registry = LanguageRegistry.createDefault(); const all = registry.getAllLanguages(); - expect(all.length).toBe(12); + expect(all.length).toBe(38); }); it("maps all expected extensions", () => { @@ -88,4 +88,56 @@ describe("LanguageRegistry", () => { } }); }); + + describe("Non-code language configs", () => { + it("detects all non-code file types via extension", () => { + const registry = LanguageRegistry.createDefault(); + const expectations: [string, string][] = [ + ["README.md", "markdown"], + ["config.yaml", "yaml"], + ["package.json", "json"], + ["config.toml", "toml"], + [".env", "env"], + ["pom.xml", "xml"], + ["Dockerfile", "dockerfile"], + ["schema.sql", "sql"], + ["schema.graphql", "graphql"], + ["types.proto", "protobuf"], + ["main.tf", "terraform"], + ["Makefile", "makefile"], + ["deploy.sh", "shell"], + ["index.html", "html"], + ["styles.css", "css"], + ["data.csv", "csv"], + ["deploy.ps1", "powershell"], + ]; + for (const [file, expectedId] of expectations) { + const config = registry.getForFile(file); + expect(config?.id, `${file} should be detected as ${expectedId}`).toBe(expectedId); + } + }); + + it("detects filename-based configs (Dockerfile, Makefile, Jenkinsfile)", () => { + const registry = LanguageRegistry.createDefault(); + expect(registry.getForFile("Dockerfile")?.id).toBe("dockerfile"); + expect(registry.getForFile("Makefile")?.id).toBe("makefile"); + expect(registry.getForFile("Jenkinsfile")?.id).toBe("jenkinsfile"); + expect(registry.getForFile("src/Dockerfile")?.id).toBe("dockerfile"); + expect(registry.getForFile("build/Makefile")?.id).toBe("makefile"); + }); + + it("detects filename-based configs for docker-compose", () => { + const registry = LanguageRegistry.createDefault(); + expect(registry.getForFile("docker-compose.yml")?.id).toBe("docker-compose"); + expect(registry.getForFile("docker-compose.yaml")?.id).toBe("docker-compose"); + expect(registry.getForFile("compose.yml")?.id).toBe("docker-compose"); + }); + + it("detects .env file variants", () => { + const registry = LanguageRegistry.createDefault(); + expect(registry.getForFile(".env")?.id).toBe("env"); + expect(registry.getForFile(".env.local")?.id).toBe("env"); + expect(registry.getForFile(".env.production")?.id).toBe("env"); + }); + }); }); diff --git a/understand-anything-plugin/packages/core/src/languages/configs/json-schema.ts b/understand-anything-plugin/packages/core/src/languages/configs/json-schema.ts index 8a132c8..4e0d9d3 100644 --- a/understand-anything-plugin/packages/core/src/languages/configs/json-schema.ts +++ b/understand-anything-plugin/packages/core/src/languages/configs/json-schema.ts @@ -1,5 +1,9 @@ import type { LanguageConfig } from "../types.js"; +// TODO: JSON Schema files have no unique extension — *.schema.json files will match +// `jsonConfigConfig` by the `.json` extension. Detection requires content-based +// heuristics (e.g., checking for `"$schema"` or `"type"` keys at the root level). +// A future content-based detection pass could re-classify them as JSON Schema. export const jsonSchemaConfig = { id: "json-schema", displayName: "JSON Schema", diff --git a/understand-anything-plugin/packages/core/src/languages/configs/kubernetes.ts b/understand-anything-plugin/packages/core/src/languages/configs/kubernetes.ts index 9380319..94e03af 100644 --- a/understand-anything-plugin/packages/core/src/languages/configs/kubernetes.ts +++ b/understand-anything-plugin/packages/core/src/languages/configs/kubernetes.ts @@ -1,5 +1,10 @@ import type { LanguageConfig } from "../types.js"; +// TODO: Kubernetes manifests are YAML files with no unique extension or filename. +// Detection requires content-based or path-pattern heuristics (e.g., checking for +// `apiVersion`/`kind` fields in YAML, or matching paths like `k8s/`, `kubernetes/`, +// `deploy/`). Currently these files will match `yamlConfig` by extension (.yaml/.yml). +// A future content-based detection pass could re-classify them as Kubernetes. export const kubernetesConfig = { id: "kubernetes", displayName: "Kubernetes", diff --git a/understand-anything-plugin/packages/core/src/languages/configs/openapi.ts b/understand-anything-plugin/packages/core/src/languages/configs/openapi.ts index ae250e5..ad026be 100644 --- a/understand-anything-plugin/packages/core/src/languages/configs/openapi.ts +++ b/understand-anything-plugin/packages/core/src/languages/configs/openapi.ts @@ -4,6 +4,7 @@ export const openapiConfig = { id: "openapi", displayName: "OpenAPI", extensions: [], + filenames: ["openapi.yaml", "openapi.json", "swagger.yaml", "swagger.json"], concepts: ["paths", "operations", "schemas", "parameters", "responses", "security schemes", "tags", "servers"], filePatterns: { entryPoints: ["openapi.yaml", "swagger.yaml"], diff --git a/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx b/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx index e3cd4bc..3732fa2 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx @@ -228,12 +228,20 @@ function useLayerDetailTopology() { table: "data", endpoint: "data", schema: "data", }; - // Non-technical persona only sees concept/module/file nodes + // All top-level (file-level) node types that should appear in the graph. + // This includes the 8 new non-code types plus the original "file" type. + const fileLevelTypes = new Set([ + "file", "config", "document", "service", "table", + "endpoint", "pipeline", "schema", "resource", + ]); + + // Non-technical persona: show module, concept, and file-level types (hide function/class) + // Junior/experienced persona: show everything including function/class let filteredGraphNodes = persona === "non-technical" ? graph.nodes.filter( - (n) => layerNodeIds.has(n.id) && (n.type === "concept" || n.type === "module" || n.type === "file"), + (n) => layerNodeIds.has(n.id) && (n.type === "concept" || n.type === "module" || fileLevelTypes.has(n.type)), ) - : graph.nodes.filter((n) => layerNodeIds.has(n.id) && n.type === "file"); + : graph.nodes.filter((n) => layerNodeIds.has(n.id) && (fileLevelTypes.has(n.type) || n.type === "module" || n.type === "concept" || n.type === "function" || n.type === "class")); // Apply node type category filters filteredGraphNodes = filteredGraphNodes.filter((n) => { diff --git a/understand-anything-plugin/skills/understand/file-analyzer-prompt.md b/understand-anything-plugin/skills/understand/file-analyzer-prompt.md index ab95e1d..974d0a0 100644 --- a/understand-anything-plugin/skills/understand/file-analyzer-prompt.md +++ b/understand-anything-plugin/skills/understand/file-analyzer-prompt.md @@ -403,7 +403,10 @@ Using the script's structural data and file categories, create edges: | `migrates` | SQL migration file modifies a table/schema (e.g., ALTER TABLE, CREATE TABLE) | `0.7` | `forward` | | `triggers` | CI/CD config triggers a pipeline or deployment (e.g., GitHub Actions workflow deploys on push to main) | `0.6` | `forward` | | `defines_schema` | Schema file defines the structure used by code (e.g., GraphQL schema defines API types, Protobuf defines message format) | `0.8` | `forward` | -| `related` | Non-code file is topically related to another file without a specific structural relationship | `0.3` | `forward` | +| `serves` | K8s Service/Deployment exposes an endpoint, or a reverse proxy routes to a service | `0.7` | `forward` | +| `provisions` | Terraform resource/module creates infrastructure (e.g., creates a database, provisions a VM) | `0.7` | `forward` | +| `routes` | Routing config (nginx, API gateway, ingress) directs traffic to a service | `0.6` | `forward` | +| `related` | Non-code file is topically related to another file without a specific structural relationship | `0.5` | `forward` | | `depends_on` | Non-code file depends on another file (e.g., docker-compose depends on Dockerfile, CI workflow depends on Makefile targets) | `0.6` | `forward` | **Import edge creation rule for code files:** For each resolved path in `batchImportData[filePath]` (provided in the input JSON), create an `imports` edge from the current file node to `file:`. The `batchImportData` values contain only resolved project-internal paths — external packages have already been filtered out. Do NOT attempt to re-resolve imports from source. @@ -415,6 +418,9 @@ Using the script's structural data and file categories, create edges: - **SQL files:** Create `migrates` edges between migration files and the table nodes they modify. Create `defines_schema` edges from schema files to API handlers that serve that data. - **CI configs:** Create `triggers` edges to the deployment targets or test suites they invoke. - **GraphQL/Protobuf schemas:** Create `defines_schema` edges to the code files that implement the resolvers or service handlers. +- **K8s manifests:** Create `serves` edges when a Service/Deployment exposes an endpoint or routes to a container. Create `deploys` edges to the application code that runs inside the container. +- **Terraform files:** Create `provisions` edges from Terraform resource/module definitions to the infrastructure they create (e.g., database resources, VM instances). +- **Routing configs (nginx, API gateway, ingress):** Create `routes` edges from routing configuration to the services they direct traffic to. Do NOT use edge types not listed in the tables above. @@ -536,7 +542,7 @@ Produce a single, valid JSON block. Validate it mentally before writing -- malfo **Required fields for every node:** - `id` (string) -- must follow the ID conventions above -- `type` (string) -- one of: `file`, `function`, `class`, `config`, `document`, `service`, `table`, `endpoint`, `pipeline`, `schema`, `resource` +- `type` (string) -- one of: `file`, `function`, `class`, `config`, `document`, `service`, `table`, `endpoint`, `pipeline`, `schema`, `resource` (11 of the 13 schema types; `module` and `concept` are reserved for higher-level analysis agents) - `name` (string) -- display name (filename for file nodes, function/class name for others) - `summary` (string) -- 1-2 sentence description, NEVER empty - `tags` (string[]) -- 3-5 lowercase hyphenated tags, NEVER empty