Skip to content

Commit 1fb759e

Browse files
authored
Merge pull request #51 from Lum1104/feat/universal-file-type-support
feat: universal file type support for non-code files
2 parents fd6235a + 41d72e3 commit 1fb759e

84 files changed

Lines changed: 4390 additions & 286 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
{
1010
"name": "understand-anything",
1111
"description": "Multi-agent codebase analysis with interactive dashboard, guided tours, and skill commands",
12-
"version": "1.3.1",
12+
"version": "2.0.0",
1313
"source": "./understand-anything-plugin"
1414
}
1515
]

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "understand-anything",
33
"description": "AI-powered codebase understanding — analyze, visualize, and explain any project",
4-
"version": "1.3.1",
4+
"version": "2.0.0",
55
"author": {
66
"name": "Lum1104"
77
},

.cursor-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "understand-anything",
33
"displayName": "Understand Anything",
44
"description": "AI-powered codebase understanding — analyze, visualize, and explain any project",
5-
"version": "1.3.1",
5+
"version": "2.0.0",
66
"author": {
77
"name": "Lum1104"
88
},

understand-anything-plugin/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@understand-anything/skill",
3-
"version": "1.3.1",
3+
"version": "2.0.0",
44
"type": "module",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

understand-anything-plugin/packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"tree-sitter-javascript": "^0.25.0",
4242
"tree-sitter-typescript": "^0.23.2",
4343
"web-tree-sitter": "^0.26.6",
44+
"yaml": "^2.8.3",
4445
"zod": "^4.3.6"
4546
}
4647
}

understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, it, expect } from "vitest";
22
import { LanguageRegistry } from "../languages/language-registry.js";
3+
import { StrictLanguageConfigSchema } from "../languages/types.js";
34
import { typescriptConfig } from "../languages/configs/typescript.js";
45
import { pythonConfig } from "../languages/configs/python.js";
56

@@ -32,9 +33,9 @@ describe("LanguageRegistry", () => {
3233
expect(registry.getForFile("file.unknown")).toBeNull();
3334
});
3435

35-
it("returns null for files without extensions", () => {
36+
it("returns null for files without extensions and no filename match", () => {
3637
const registry = new LanguageRegistry();
37-
expect(registry.getForFile("Makefile")).toBeNull();
38+
expect(registry.getForFile("SOMEFILE")).toBeNull();
3839
});
3940

4041
it("lists all registered languages", () => {
@@ -48,10 +49,10 @@ describe("LanguageRegistry", () => {
4849
});
4950

5051
describe("createDefault", () => {
51-
it("registers all 12 built-in language configs", () => {
52+
it("registers all 38 built-in language configs", () => {
5253
const registry = LanguageRegistry.createDefault();
5354
const all = registry.getAllLanguages();
54-
expect(all.length).toBe(12);
55+
expect(all.length).toBe(38);
5556
});
5657

5758
it("maps all expected extensions", () => {
@@ -88,4 +89,107 @@ describe("LanguageRegistry", () => {
8889
}
8990
});
9091
});
92+
93+
describe("Non-code language configs", () => {
94+
it("detects all non-code file types via extension", () => {
95+
const registry = LanguageRegistry.createDefault();
96+
const expectations: [string, string][] = [
97+
["README.md", "markdown"],
98+
["config.yaml", "yaml"],
99+
["package.json", "json"],
100+
["config.toml", "toml"],
101+
[".env", "env"],
102+
["pom.xml", "xml"],
103+
["Dockerfile", "dockerfile"],
104+
["schema.sql", "sql"],
105+
["schema.graphql", "graphql"],
106+
["types.proto", "protobuf"],
107+
["main.tf", "terraform"],
108+
["Makefile", "makefile"],
109+
["deploy.sh", "shell"],
110+
["index.html", "html"],
111+
["styles.css", "css"],
112+
["data.csv", "csv"],
113+
["deploy.ps1", "powershell"],
114+
];
115+
for (const [file, expectedId] of expectations) {
116+
const config = registry.getForFile(file);
117+
expect(config?.id, `${file} should be detected as ${expectedId}`).toBe(expectedId);
118+
}
119+
});
120+
121+
it("detects filename-based configs (Dockerfile, Makefile, Jenkinsfile)", () => {
122+
const registry = LanguageRegistry.createDefault();
123+
expect(registry.getForFile("Dockerfile")?.id).toBe("dockerfile");
124+
expect(registry.getForFile("Makefile")?.id).toBe("makefile");
125+
expect(registry.getForFile("Jenkinsfile")?.id).toBe("jenkinsfile");
126+
expect(registry.getForFile("src/Dockerfile")?.id).toBe("dockerfile");
127+
expect(registry.getForFile("build/Makefile")?.id).toBe("makefile");
128+
});
129+
130+
it("detects filename-based configs for docker-compose", () => {
131+
const registry = LanguageRegistry.createDefault();
132+
expect(registry.getForFile("docker-compose.yml")?.id).toBe("docker-compose");
133+
expect(registry.getForFile("docker-compose.yaml")?.id).toBe("docker-compose");
134+
expect(registry.getForFile("compose.yml")?.id).toBe("docker-compose");
135+
});
136+
137+
it("detects .env file variants", () => {
138+
const registry = LanguageRegistry.createDefault();
139+
expect(registry.getForFile(".env")?.id).toBe("env");
140+
expect(registry.getForFile(".env.local")?.id).toBe("env");
141+
expect(registry.getForFile(".env.production")?.id).toBe("env");
142+
});
143+
});
144+
145+
describe("StrictLanguageConfigSchema refinement", () => {
146+
it("rejects configs with empty extensions AND no filenames", () => {
147+
const result = StrictLanguageConfigSchema.safeParse({
148+
id: "empty-lang",
149+
displayName: "Empty",
150+
extensions: [],
151+
concepts: ["nothing"],
152+
filePatterns: { entryPoints: [], barrels: [], tests: [], config: [] },
153+
});
154+
expect(result.success).toBe(false);
155+
if (!result.success) {
156+
expect(result.error.issues[0].message).toContain("at least one extension or filename");
157+
}
158+
});
159+
160+
it("rejects configs with empty extensions AND empty filenames", () => {
161+
const result = StrictLanguageConfigSchema.safeParse({
162+
id: "empty-lang",
163+
displayName: "Empty",
164+
extensions: [],
165+
filenames: [],
166+
concepts: ["nothing"],
167+
filePatterns: { entryPoints: [], barrels: [], tests: [], config: [] },
168+
});
169+
expect(result.success).toBe(false);
170+
});
171+
172+
it("accepts configs with extensions but no filenames", () => {
173+
const result = StrictLanguageConfigSchema.safeParse({
174+
id: "ext-lang",
175+
displayName: "ExtLang",
176+
extensions: [".ext"],
177+
concepts: ["something"],
178+
filePatterns: { entryPoints: [], barrels: [], tests: [], config: [] },
179+
});
180+
expect(result.success).toBe(true);
181+
});
182+
183+
it("accepts configs with filenames but empty extensions", () => {
184+
const result = StrictLanguageConfigSchema.safeParse({
185+
id: "filename-lang",
186+
displayName: "FilenameLang",
187+
extensions: [],
188+
filenames: ["Specialfile"],
189+
concepts: ["something"],
190+
filePatterns: { entryPoints: [], barrels: [], tests: [], config: [] },
191+
});
192+
expect(result.success).toBe(true);
193+
});
194+
});
91195
});

0 commit comments

Comments
 (0)