Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .agents/skills/architecture/references/code-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand All @@ -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

10 changes: 5 additions & 5 deletions .agents/skills/repo-scanning/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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
10 changes: 5 additions & 5 deletions .claude/skills/repo-scanning/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand All @@ -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

9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +5 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor date drift.

Release date here is 2026-04-16 but the skill files' Last Updated is 2026-04-17. Pick one to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 5 - 12, The changelog entry for version header
"[0.7.1] - 2026-04-16" conflicts with the skill files' "Last Updated" date of
2026-04-17; pick a single canonical date and make them consistent—either update
the CHANGELOG header "[0.7.1] - 2026-04-16" to 2026-04-17 or update the skill
files' "Last Updated" to 2026-04-16 so both match; ensure you change all
occurrences (the version header in CHANGELOG.md and any "Last Updated" fields in
the skill files) to the chosen date.


## [0.7.0] - 2026-04-10

### Added
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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"
Expand Down
15 changes: 12 additions & 3 deletions src/lib/scanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
46 changes: 46 additions & 0 deletions tests/scanner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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': '<Project Sdk="Microsoft.NET.Sdk"></Project>',
'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': '<?php class Webhook {}',
'workers/processor.ex': 'defmodule Processor do end',
'ui/MainActivity.kt': 'class MainActivity {}',
});
const scan = scanRepo(dir);
const names = scan.domains.map(d => 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': '<Project></Project>',
'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': '{}',
Expand Down
Loading