From c11db6f1793e393e0659a034332d1df2dcdb06df Mon Sep 17 00:00:00 2001 From: mvoutov Date: Thu, 16 Apr 2026 18:42:24 -0700 Subject: [PATCH 1/3] scanner recognizes C# and other languages. + bug release 0.7.1 --- CHANGELOG.md | 9 +++++++++ package-lock.json | 4 ++-- package.json | 4 ++-- src/lib/scanner.js | 15 ++++++++++++--- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b793732..684c158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## [0.7.1] - 2026-04-16 + +### Fixed +- **Domain discovery for C#, Java, Kotlin, Swift, PHP, Elixir, F#** — scanner's `SOURCE_EXTS` only recognized JS/TS/Python/Ruby/Go/Rust files, so `doc init` reported zero domains for projects in other languages even when language detection itself succeeded. Added `.cs`, `.java`, `.kt`, `.kts`, `.swift`, `.php`, `.ex`, `.exs`, `.fs`, `.fsx`, `.mjs`, and `.cjs` to the source-extension set. +- **Build-output skipping** — repo-size estimation and domain detection now skip `bin/`, `obj/`, and `target/` directories, preventing .NET / Java / Rust build artifacts from polluting module lists or line counts. + +### Known Limitations +- Import graph, hub files, and cluster detection remain JS/TS/Python-only — `doc init` on C#/Java/Swift/PHP/Elixir/Kotlin/Rust/Go/Ruby projects will generate skills and domains but produce a minimal atlas. Full multi-language import parsing is tracked for a future release. + ## [0.7.0] - 2026-04-10 ### Added diff --git a/package-lock.json b/package-lock.json index 55ea24f..db662f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aspens", - "version": "0.7.0", + "version": "0.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aspens", - "version": "0.7.0", + "version": "0.7.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c139cf6..083af74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aspens", - "version": "0.7.0", + "version": "0.7.1", "description": "Keep coding-agent context accurate as your codebase changes", "type": "module", "bin": { @@ -23,7 +23,7 @@ "test": "vitest run", "start": "node bin/cli.js", "lint": "echo 'No linter configured yet' && exit 0", - "postinstall": "echo '\n 📌 aspens v0.7.0: Existing repos should refresh generated docs after upgrading.\n Recommended: aspens doc sync --refresh\n\n 🌲 aspens is in active development — please keep it up to date.\n Run into issues? Let us know: https://github.com/aspenkit/aspens/issues\n'" + "postinstall": "echo '\n 📌 aspens v0.7.1: Scanner now recognizes C#, Java, Kotlin, Swift, PHP, Elixir, and F# source files for domain discovery.\n If a prior `doc init` came up empty for one of these languages, re-run it after upgrading.\n\n 🌲 aspens is in active development — please keep it up to date.\n Run into issues? Let us know: https://github.com/aspenkit/aspens/issues\n'" }, "engines": { "node": ">=20" diff --git a/src/lib/scanner.js b/src/lib/scanner.js index 5a3631d..fe5db0e 100644 --- a/src/lib/scanner.js +++ b/src/lib/scanner.js @@ -82,7 +82,8 @@ function estimateRepoSize(repoPath) { for (const entry of entries) { if (entry.startsWith('.') || entry === 'node_modules' || entry === '__pycache__' || entry === 'dist' || entry === 'build' || entry === '.next' || entry === 'vendor' || - entry === '.git' || entry === 'coverage') continue; + entry === '.git' || entry === 'coverage' || entry === 'bin' || entry === 'obj' || + entry === 'target') continue; const full = join(dir, entry); try { @@ -317,7 +318,7 @@ function detectStructure(repoPath) { // Always skip — purely structural, build output, dependencies, IDE const SKIP_DIR_NAMES = new Set([ - 'src', 'app', 'bin', 'cmd', 'pkg', 'internal', 'vendor', + 'src', 'app', 'bin', 'obj', 'cmd', 'pkg', 'internal', 'vendor', 'dist', 'build', 'out', 'output', 'target', 'coverage', 'node_modules', '__pycache__', '.next', '.nuxt', '.cache', '.github', '.vscode', '.idea', '.git', @@ -631,4 +632,12 @@ function findSourceRoot(repoPath) { return repoPath; } -const SOURCE_EXTS = new Set(['.py', '.ts', '.js', '.tsx', '.jsx', '.rb', '.go', '.rs']); +const SOURCE_EXTS = new Set([ + '.py', '.ts', '.js', '.tsx', '.jsx', '.mjs', '.cjs', + '.rb', '.go', '.rs', + '.java', '.kt', '.kts', + '.cs', '.fs', '.fsx', + '.swift', + '.php', + '.ex', '.exs', +]); From 0841327b66d4ebb0d76baa764fda90a36235ff26 Mon Sep 17 00:00:00 2001 From: mvoutov Date: Thu, 16 Apr 2026 18:44:31 -0700 Subject: [PATCH 2/3] scanner test --- .../architecture/references/code-map.md | 4 +- .agents/skills/repo-scanning/SKILL.md | 10 ++-- .claude/skills/repo-scanning/skill.md | 10 ++-- AGENTS.md | 4 +- tests/scanner.test.js | 46 +++++++++++++++++++ 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/.agents/skills/architecture/references/code-map.md b/.agents/skills/architecture/references/code-map.md index 47d21cc..5e68dea 100644 --- a/.agents/skills/architecture/references/code-map.md +++ b/.agents/skills/architecture/references/code-map.md @@ -5,8 +5,8 @@ **Hub files (most depended-on):** - `src/lib/runner.js` - 9 dependents - `src/lib/target.js` - 9 dependents +- `src/lib/errors.js` - 8 dependents - `src/lib/scanner.js` - 8 dependents -- `src/lib/errors.js` - 7 dependents - `src/lib/skill-writer.js` - 7 dependents **Domain clusters:** @@ -16,7 +16,7 @@ | src | 44 | `src/commands/doc-init.js`, `src/lib/runner.js`, `src/lib/target.js` | **High-churn hotspots:** -- `src/commands/doc-init.js` - 33 changes +- `src/commands/doc-init.js` - 34 changes - `src/commands/doc-sync.js` - 20 changes - `src/lib/runner.js` - 17 changes diff --git a/.agents/skills/repo-scanning/SKILL.md b/.agents/skills/repo-scanning/SKILL.md index 3531f57..c88208d 100644 --- a/.agents/skills/repo-scanning/SKILL.md +++ b/.agents/skills/repo-scanning/SKILL.md @@ -27,15 +27,15 @@ You are working on **aspens' repo scanning system** — a fully deterministic an - **Multi-target detection:** Scanner checks for both `.claude` dir + `AGENTS.md` (Claude Code) and `.codex` dir + `AGENTS.md` (Codex CLI) to inform target selection during `doc init` - **Detection via marker files:** Languages detected by presence of files like `package.json`, `go.mod`, `Cargo.toml` — not by scanning source extensions - **Framework detection:** JS/TS from `package.json` deps, Python from `requirements.txt`/`pyproject.toml`/`Pipfile`, Go from `go.mod` contents, Ruby from `Gemfile` -- **Domain detection:** Scans dirs under source root + repo root, skips `SKIP_DIR_NAMES` set (structural/build/IDE dirs), requires at least one source file via `collectModules()` +- **Domain detection:** Scans dirs under source root + repo root, skips `SKIP_DIR_NAMES` set (structural/build/IDE/.NET/Java/Rust build dirs), requires at least one source file via `collectModules()` - **extraDomains:** User-specified domains merged via `mergeExtraDomains()` — marked with `userSpecified: true`, resolved against source root then repo root - **Source root:** First match of `src`, `app`, `lib`, `server`, `pages` via `findSourceRoot()` -- **Size estimation:** Lines estimated at ~40 bytes/line from `stat.size`, walk capped at depth 5 +- **Size estimation:** Lines estimated at ~40 bytes/line from `stat.size`, walk capped at depth 5, skips `bin`/`obj`/`target` build output alongside `node_modules`/`dist`/etc. - **Graph is opt-out:** `scanCommand` builds graph by default (`options.graph !== false`); errors are caught and only logged with `--verbose` ## Critical Rules -- **`SOURCE_EXTS`**: Only `.py`, `.ts`, `.js`, `.tsx`, `.jsx`, `.rb`, `.go`, `.rs` — adding a language requires updating this set AND the `detectLanguages` indicators -- **`SKIP_DIR_NAMES`**: Directories like `src`, `app`, `dist`, `node_modules` are skipped in domain detection — adding a skip dir here affects all repos +- **`SOURCE_EXTS`**: `.py`, `.ts`, `.js`, `.tsx`, `.jsx`, `.mjs`, `.cjs`, `.rb`, `.go`, `.rs`, `.java`, `.kt`, `.kts`, `.cs`, `.fs`, `.fsx`, `.swift`, `.php`, `.ex`, `.exs` — adding a language requires updating this set AND the `detectLanguages` indicators. Import graph / hub / cluster detection remains JS/TS/Python-only; other languages get domain discovery but a minimal atlas. +- **`SKIP_DIR_NAMES`**: Includes `src`, `app`, `bin`, `obj`, `dist`, `target`, `node_modules`, etc. — skipped in domain detection. `bin`/`obj`/`target` added to avoid .NET/Java/Rust build artifacts. - **`BOILERPLATE_STEMS`**: `__init__`, `index`, `mod` are excluded from module collection — don't add real module names here - **TypeScript implies JavaScript**: TS detection in `detectLanguages()` automatically adds JS to the languages array - **Graph failure is non-fatal**: `buildRepoGraph` errors in `scanCommand()` are caught and silently ignored unless `--verbose` @@ -46,4 +46,4 @@ You are working on **aspens' repo scanning system** — a fully deterministic an - **No guidelines directory** — `.claude/guidelines/` does not exist yet for this domain --- -**Last Updated:** 2026-04-02 +**Last Updated:** 2026-04-17 diff --git a/.claude/skills/repo-scanning/skill.md b/.claude/skills/repo-scanning/skill.md index 78b9acb..dd344e3 100644 --- a/.claude/skills/repo-scanning/skill.md +++ b/.claude/skills/repo-scanning/skill.md @@ -27,15 +27,15 @@ You are working on **aspens' repo scanning system** — a fully deterministic an - **Multi-target detection:** Scanner checks for both `.claude` dir + `CLAUDE.md` (Claude Code) and `.codex` dir + `AGENTS.md` (Codex CLI) to inform target selection during `doc init` - **Detection via marker files:** Languages detected by presence of files like `package.json`, `go.mod`, `Cargo.toml` — not by scanning source extensions - **Framework detection:** JS/TS from `package.json` deps, Python from `requirements.txt`/`pyproject.toml`/`Pipfile`, Go from `go.mod` contents, Ruby from `Gemfile` -- **Domain detection:** Scans dirs under source root + repo root, skips `SKIP_DIR_NAMES` set (structural/build/IDE dirs), requires at least one source file via `collectModules()` +- **Domain detection:** Scans dirs under source root + repo root, skips `SKIP_DIR_NAMES` set (structural/build/IDE/.NET/Java/Rust build dirs), requires at least one source file via `collectModules()` - **extraDomains:** User-specified domains merged via `mergeExtraDomains()` — marked with `userSpecified: true`, resolved against source root then repo root - **Source root:** First match of `src`, `app`, `lib`, `server`, `pages` via `findSourceRoot()` -- **Size estimation:** Lines estimated at ~40 bytes/line from `stat.size`, walk capped at depth 5 +- **Size estimation:** Lines estimated at ~40 bytes/line from `stat.size`, walk capped at depth 5, skips `bin`/`obj`/`target` build output alongside `node_modules`/`dist`/etc. - **Graph is opt-out:** `scanCommand` builds graph by default (`options.graph !== false`); errors are caught and only logged with `--verbose` ## Critical Rules -- **`SOURCE_EXTS`**: Only `.py`, `.ts`, `.js`, `.tsx`, `.jsx`, `.rb`, `.go`, `.rs` — adding a language requires updating this set AND the `detectLanguages` indicators -- **`SKIP_DIR_NAMES`**: Directories like `src`, `app`, `dist`, `node_modules` are skipped in domain detection — adding a skip dir here affects all repos +- **`SOURCE_EXTS`**: `.py`, `.ts`, `.js`, `.tsx`, `.jsx`, `.mjs`, `.cjs`, `.rb`, `.go`, `.rs`, `.java`, `.kt`, `.kts`, `.cs`, `.fs`, `.fsx`, `.swift`, `.php`, `.ex`, `.exs` — adding a language requires updating this set AND the `detectLanguages` indicators. Import graph / hub / cluster detection remains JS/TS/Python-only; other languages get domain discovery but a minimal atlas. +- **`SKIP_DIR_NAMES`**: Includes `src`, `app`, `bin`, `obj`, `dist`, `target`, `node_modules`, etc. — skipped in domain detection. `bin`/`obj`/`target` added to avoid .NET/Java/Rust build artifacts. - **`BOILERPLATE_STEMS`**: `__init__`, `index`, `mod` are excluded from module collection — don't add real module names here - **TypeScript implies JavaScript**: TS detection in `detectLanguages()` automatically adds JS to the languages array - **Graph failure is non-fatal**: `buildRepoGraph` errors in `scanCommand()` are caught and silently ignored unless `--verbose` @@ -46,4 +46,4 @@ You are working on **aspens' repo scanning system** — a fully deterministic an - **No guidelines directory** — `.claude/guidelines/` does not exist yet for this domain --- -**Last Updated:** 2026-04-02 +**Last Updated:** 2026-04-17 diff --git a/AGENTS.md b/AGENTS.md index 0eec347..145a909 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,8 +3,8 @@ **Hub files (most depended-on):** - `src/lib/runner.js` - 9 dependents - `src/lib/target.js` - 9 dependents +- `src/lib/errors.js` - 8 dependents - `src/lib/scanner.js` - 8 dependents -- `src/lib/errors.js` - 7 dependents - `src/lib/skill-writer.js` - 7 dependents **Domain clusters:** @@ -14,7 +14,7 @@ | src | 44 | `src/commands/doc-init.js`, `src/lib/runner.js`, `src/lib/target.js` | **High-churn hotspots:** -- `src/commands/doc-init.js` - 33 changes +- `src/commands/doc-init.js` - 34 changes - `src/commands/doc-sync.js` - 20 changes - `src/lib/runner.js` - 17 changes diff --git a/tests/scanner.test.js b/tests/scanner.test.js index 3b8fcee..c5fdf45 100644 --- a/tests/scanner.test.js +++ b/tests/scanner.test.js @@ -205,6 +205,52 @@ describe('scanRepo', () => { expect(scan.domains.some(d => d.name === 'auth')).toBe(true); }); + it('detects domains from C# source files', () => { + const dir = createFixture('csharp-project', { + 'MyApp.csproj': '', + 'Controllers/UsersController.cs': 'public class UsersController {}', + 'Controllers/OrdersController.cs': 'public class OrdersController {}', + 'Services/PaymentService.cs': 'public class PaymentService {}', + }); + const scan = scanRepo(dir); + expect(scan.languages).toContain('csharp'); + expect(scan.domains.some(d => d.name === 'controllers')).toBe(true); + expect(scan.domains.some(d => d.name === 'services')).toBe(true); + }); + + it('detects domains from Java, Swift, PHP, Elixir, Kotlin source files', () => { + const dir = createFixture('multi-lang-project', { + 'services/UserService.java': 'public class UserService {}', + 'views/HomeView.swift': 'struct HomeView {}', + 'handlers/webhook.php': ' d.name); + expect(names).toContain('services'); + expect(names).toContain('views'); + expect(names).toContain('handlers'); + expect(names).toContain('workers'); + expect(names).toContain('ui'); + }); + + it('skips bin, obj, and target build output directories', () => { + const dir = createFixture('build-output-project', { + 'MyApp.csproj': '', + 'bin/Debug/MyApp.cs': '// compiled artifact', + 'obj/project.assets.cs': '// intermediate', + 'target/classes/Foo.java': '// build output', + 'Services/Real.cs': 'public class Real {}', + }); + const scan = scanRepo(dir); + const names = scan.domains.map(d => d.name); + expect(names).not.toContain('bin'); + expect(names).not.toContain('obj'); + expect(names).not.toContain('target'); + expect(names).toContain('services'); + }); + it('returns empty domains for featureless project', () => { const dir = createFixture('minimal-project', { 'package.json': '{}', From 8ae41470f3634c2f87524cc7a45d30e49ae09a5e Mon Sep 17 00:00:00 2001 From: mvoutov Date: Thu, 16 Apr 2026 18:56:18 -0700 Subject: [PATCH 3/3] Canonical set of source-file extensions for scanner --- .agents/skills/repo-scanning/SKILL.md | 2 +- .claude/skills/repo-scanning/skill.md | 2 +- CHANGELOG.md | 4 ++-- package.json | 2 +- src/lib/context-builder.js | 3 +-- src/lib/impact.js | 10 ++++++---- src/lib/scanner.js | 10 +--------- src/lib/source-exts.js | 18 ++++++++++++++++++ tests/scanner.test.js | 10 +++++++++- 9 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 src/lib/source-exts.js diff --git a/.agents/skills/repo-scanning/SKILL.md b/.agents/skills/repo-scanning/SKILL.md index c88208d..8aaf52c 100644 --- a/.agents/skills/repo-scanning/SKILL.md +++ b/.agents/skills/repo-scanning/SKILL.md @@ -46,4 +46,4 @@ You are working on **aspens' repo scanning system** — a fully deterministic an - **No guidelines directory** — `.claude/guidelines/` does not exist yet for this domain --- -**Last Updated:** 2026-04-17 +**Last Updated:** 2026-04-16 diff --git a/.claude/skills/repo-scanning/skill.md b/.claude/skills/repo-scanning/skill.md index dd344e3..5509474 100644 --- a/.claude/skills/repo-scanning/skill.md +++ b/.claude/skills/repo-scanning/skill.md @@ -46,4 +46,4 @@ You are working on **aspens' repo scanning system** — a fully deterministic an - **No guidelines directory** — `.claude/guidelines/` does not exist yet for this domain --- -**Last Updated:** 2026-04-17 +**Last Updated:** 2026-04-16 diff --git a/CHANGELOG.md b/CHANGELOG.md index 684c158..936bc57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,8 @@ ## [0.7.1] - 2026-04-16 ### Fixed -- **Domain discovery for C#, Java, Kotlin, Swift, PHP, Elixir, F#** — scanner's `SOURCE_EXTS` only recognized JS/TS/Python/Ruby/Go/Rust files, so `doc init` reported zero domains for projects in other languages even when language detection itself succeeded. Added `.cs`, `.java`, `.kt`, `.kts`, `.swift`, `.php`, `.ex`, `.exs`, `.fs`, `.fsx`, `.mjs`, and `.cjs` to the source-extension set. -- **Build-output skipping** — repo-size estimation and domain detection now skip `bin/`, `obj/`, and `target/` directories, preventing .NET / Java / Rust build artifacts from polluting module lists or line counts. +- **Domain discovery for C#, Java, Swift, PHP, and Elixir** — scanner's `SOURCE_EXTS` only recognized JS/TS/Python/Ruby/Go/Rust files, so `doc init` reported zero domains for projects in other languages even when language detection itself succeeded. Added `.cs`, `.java`, `.swift`, `.php`, `.ex`, `.exs`, `.mjs`, and `.cjs` to the source-extension set. Kotlin (`.kt`/`.kts`) and F# (`.fs`/`.fsx`) files are also counted as source now; full language detection for those will follow in a future release. +- **Build-output skipping** — repo-size estimation now skips `bin/`, `obj/`, and `target/` directories, and domain detection also skips `obj/`, preventing .NET / Java / Rust build artifacts from polluting module lists or line counts. ### Known Limitations - Import graph, hub files, and cluster detection remain JS/TS/Python-only — `doc init` on C#/Java/Swift/PHP/Elixir/Kotlin/Rust/Go/Ruby projects will generate skills and domains but produce a minimal atlas. Full multi-language import parsing is tracked for a future release. diff --git a/package.json b/package.json index 083af74..9d81984 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "test": "vitest run", "start": "node bin/cli.js", "lint": "echo 'No linter configured yet' && exit 0", - "postinstall": "echo '\n 📌 aspens v0.7.1: Scanner now recognizes C#, Java, Kotlin, Swift, PHP, Elixir, and F# source files for domain discovery.\n If a prior `doc init` came up empty for one of these languages, re-run it after upgrading.\n\n 🌲 aspens is in active development — please keep it up to date.\n Run into issues? Let us know: https://github.com/aspenkit/aspens/issues\n'" + "postinstall": "echo '\n 📌 aspens v0.7.1: Scanner now recognizes C#, Java, Swift, PHP, and Elixir source files for domain discovery.\n If a prior `doc init` came up empty for one of these languages, re-run it after upgrading.\n\n 🌲 aspens is in active development — please keep it up to date.\n Run into issues? Let us know: https://github.com/aspenkit/aspens/issues\n'" }, "engines": { "node": ">=20" diff --git a/src/lib/context-builder.js b/src/lib/context-builder.js index 3b3c30c..e8760b2 100644 --- a/src/lib/context-builder.js +++ b/src/lib/context-builder.js @@ -1,6 +1,7 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'fs'; import { join, extname, relative } from 'path'; import { execSync } from 'child_process'; +import { SOURCE_EXTS } from './source-exts.js'; /** * Build context string from a repo scan result. @@ -186,8 +187,6 @@ function listDirRecursive(dirPath, maxDepth, currentDepth = 0, prefix = '') { } } -const SOURCE_EXTS = new Set(['.js', '.ts', '.tsx', '.jsx', '.py', '.go', '.rs', '.rb', '.java', '.kt', '.swift', '.php', '.ex', '.exs']); - function listSourceFiles(dirPath) { try { return readdirSync(dirPath) diff --git a/src/lib/impact.js b/src/lib/impact.js index 3ec363d..a2c518c 100644 --- a/src/lib/impact.js +++ b/src/lib/impact.js @@ -5,12 +5,14 @@ import { buildRepoGraph } from './graph-builder.js'; import { loadConfig, TARGETS } from './target.js'; import { findSkillFiles } from './skill-reader.js'; import { getGitRoot } from './git-helpers.js'; +import { SOURCE_EXTS as SCANNER_SOURCE_EXTS } from './source-exts.js'; +// Freshness analysis scans a broader set than scanner domain detection — +// includes ecosystem extensions (.scala/.clj/.elm/.vue/.svelte) that scanner +// doesn't yet detect as languages but which still signal source-file edits. const SOURCE_EXTS = new Set([ - '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', - '.py', '.rb', '.go', '.rs', '.java', '.cs', - '.php', '.swift', '.kt', '.kts', '.scala', - '.clj', '.ex', '.exs', '.elm', '.vue', '.svelte', + ...SCANNER_SOURCE_EXTS, + '.scala', '.clj', '.elm', '.vue', '.svelte', ]); const LOW_SIGNAL_DOMAIN_NAMES = new Set([ diff --git a/src/lib/scanner.js b/src/lib/scanner.js index fe5db0e..e26035a 100644 --- a/src/lib/scanner.js +++ b/src/lib/scanner.js @@ -1,5 +1,6 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'fs'; import { join, basename, extname, relative } from 'path'; +import { SOURCE_EXTS } from './source-exts.js'; /** * Scan a repository and return its tech stack, structure, and domains. @@ -632,12 +633,3 @@ function findSourceRoot(repoPath) { return repoPath; } -const SOURCE_EXTS = new Set([ - '.py', '.ts', '.js', '.tsx', '.jsx', '.mjs', '.cjs', - '.rb', '.go', '.rs', - '.java', '.kt', '.kts', - '.cs', '.fs', '.fsx', - '.swift', - '.php', - '.ex', '.exs', -]); diff --git a/src/lib/source-exts.js b/src/lib/source-exts.js new file mode 100644 index 0000000..8ecb3bc --- /dev/null +++ b/src/lib/source-exts.js @@ -0,0 +1,18 @@ +/** + * Canonical set of source-file extensions recognized by the scanner and + * context-builder. Keep in sync with `detectLanguages()` indicators in + * `scanner.js` when adding a language. + * + * Note: `graph-builder.js` keeps its own smaller set because it only parses + * JS/TS/Python imports. + */ +export const SOURCE_EXTS = new Set([ + '.py', + '.ts', '.js', '.tsx', '.jsx', '.mjs', '.cjs', + '.rb', '.go', '.rs', + '.java', '.kt', '.kts', + '.cs', '.fs', '.fsx', + '.swift', + '.php', + '.ex', '.exs', +]); diff --git a/tests/scanner.test.js b/tests/scanner.test.js index c5fdf45..74a47a1 100644 --- a/tests/scanner.test.js +++ b/tests/scanner.test.js @@ -218,8 +218,12 @@ describe('scanRepo', () => { expect(scan.domains.some(d => d.name === 'services')).toBe(true); }); - it('detects domains from Java, Swift, PHP, Elixir, Kotlin source files', () => { + it('detects domains and languages from Java, Swift, PHP, Elixir, Kotlin source files', () => { const dir = createFixture('multi-lang-project', { + 'pom.xml': '', + 'Package.swift': '// swift-tools-version:5.0', + 'composer.json': '{}', + 'mix.exs': 'defmodule App.MixProject do end', 'services/UserService.java': 'public class UserService {}', 'views/HomeView.swift': 'struct HomeView {}', 'handlers/webhook.php': ' { 'ui/MainActivity.kt': 'class MainActivity {}', }); const scan = scanRepo(dir); + expect(scan.languages).toContain('java'); + expect(scan.languages).toContain('swift'); + expect(scan.languages).toContain('php'); + expect(scan.languages).toContain('elixir'); const names = scan.domains.map(d => d.name); expect(names).toContain('services'); expect(names).toContain('views');