Add TDD tests and implement extractTopLevelFunction with helpers for
extracting function name, params, and return type (including generics
where the grammar emits type_identifier + type_arguments as siblings).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Empty extractor that satisfies the LanguageExtractor interface so the
plugin pipeline can load it. Real extraction logic lands in subsequent
TDD commits.
Adds the Dart language config and wires it into builtinLanguageConfigs
so .dart files are recognized by the language registry. References the
vendored @understand-anything/tree-sitter-dart-wasm package for grammar
loading.
No extractor yet — structural extraction lands in the next commit.
Wires Kotlin into the existing tree-sitter pipeline so .kt and .kts
files now produce functions, classes, data classes, sealed classes,
interfaces, objects, imports, exports, and call-graph edges — matching
the behavior of the other language extractors.
## Why @tree-sitter-grammars/tree-sitter-kotlin
The standard `tree-sitter-kotlin` (v0.3.8) ships only native bindings.
The new `@tree-sitter-grammars/tree-sitter-kotlin@1.1.0` ships a
prebuilt `.wasm` (loads cleanly with `web-tree-sitter@^0.26.6`,
nodeTypeCount=289, parses class_declaration / function_declaration as
expected). Same shape that PR1 used for Swift, just a different
publisher because the repomix WASM bundle does not include Kotlin.
`@tree-sitter-grammars` is the official tree-sitter org's GitHub
account, so this is the canonical upstream WASM source for Kotlin.
## Notes for reviewers
- `kotlinConfig` already existed as a stub (no `treeSitter` field), so
Android / JVM / Gradle codebases currently produce no structural
edges between `.kt` files. This PR adds the `treeSitter` field; the
existing plugin loader picks it up unchanged.
- **Visibility rule differs from Swift**: Kotlin's default visibility
is `public`, so the extractor treats *every* declaration with no
modifier as exported. Only an explicit `private` opts out. `internal`
and `protected` remain exported in the project-graph sense because
they are still resolvable from other files (within the module / via
inheritance).
- `class_declaration` in tree-sitter-kotlin is overloaded for class,
data class, sealed class, and interface (distinguished by the keyword
child and `modifiers > class_modifier`). The extractor handles all
four uniformly.
- `object_declaration` is a separate node type (Kotlin singletons) —
treated as a class-like entry with its own `name` and members.
- Primary-constructor parameters marked `val` / `var` are surfaced as
class properties; plain `parameter`s without `val/var` are
constructor-only and are NOT counted as properties (matching Kotlin
semantics).
- Import handling distinguishes the three forms: plain dotted
(`import a.b.C`), wildcard (`import a.b.*` → specifier `"*"`), and
aliased (`import a.b.C as Foo` → specifier `"Foo"`).
## Verification
- `pnpm lint` clean
- `pnpm --filter @understand-anything/core build` clean
- `pnpm --filter @understand-anything/skill build` clean
- `pnpm --filter @understand-anything/core test`: **692/692** (+22 new
Kotlin tests, matching the bar set by go-extractor.test.ts /
swift-extractor.test.ts)
- `pnpm test`: 196/196 (no regressions)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- typescript-eslint preset: strict -> recommended for a usable first-pass
baseline (per PR discussion); ratchet up in a follow-up.
- Drop the projectService/parserOptions block. Neither `recommended` nor
`strict` is type-aware, so it was unused; removing it also avoids the
pnpm-workspace tsconfig-resolution failure mode flagged in review.
- Add Node + browser globals via the `globals` package so .mjs scripts and
the dashboard stop hitting `no-undef`.
- Expand ignores: built bundles (**/public/**), Astro generated (.astro/),
and .private/ (eval scratch). Cuts 2400+ errors in vendored output.
- Allow `_`-prefixed unused vars/args/caught errors; skip irregular
whitespace inside comments (json-parser intentionally embeds ZWSP-escaped
block-comment examples in JSDoc).
- Fix the residual 13 genuine errors: drop dead imports/vars, replace
two `as any[]` in schema.ts with `Array<Record<string, unknown>>`,
drop unused destructure in change-classifier, drop unused catch binding
in extract-structure.mjs.
- Add EOF newline to eslint.config.mjs.
- Refresh pnpm-lock.yaml.
- Add `pnpm lint` step to .github/workflows/ci.yml so the tooling
actually enforces something.
pnpm lint now exits 0 locally; 33+13 test files / 1445 tests still pass.
A deep audit of the project-scanner → file-analyzer → merge pipeline
turned up a wide range of silent data-loss bugs. Each one alone is
small; together they were producing graphs with very few import edges,
missing sub-file nodes for non-code formats, and inconsistent metrics.
Root-cause fixes (high impact):
- project-scanner.md: extend import-pattern table to resolve absolute
imports for Python (`from a.b.c import x`), TS/JS (tsconfig.json
paths/baseUrl aliases), Java/Kotlin (`com.foo.Bar` ↔ file paths),
Ruby (`require 'foo/bar'` load-path), PHP (composer PSR-4 namespaces),
and C/C++ (`#include` headers). Was relative-only, which produced
empty importMap entries for the majority of real projects.
- project-scanner.md: add `.ps1`, `.bat`, `.cmd`, `.jsonc` to language
table; require non-null `language` field with an explicit fallback.
- file-analyzer.md: document `sections`, `definitions`, `services`,
`endpoints`, `steps`, `resources` in the extraction-output schema and
spell out the sub-file node-creation rules per category. Was missing,
so per-table / endpoint / resource nodes were never created from
SQL / OpenAPI / Terraform / K8s / Dockerfile parser output.
- file-analyzer.md: add explicit source-reading fallback rules for
PowerShell, Batch, Bash, Swift, Kotlin (no tree-sitter coverage).
- yaml-parser: declare `kubernetes`, `docker-compose`, `github-actions`,
`openapi` languages so files the language-registry tags with those
ids actually get section extraction. Recognize quoted top-level keys
(e.g. `"on":` in GitHub Actions). Emit one section per entry for
array-root YAML documents.
- json-parser: declare `json-schema`, `openapi`; add `stripJsoncSyntax`
helper that removes line / block comments and trailing commas before
parse so `.jsonc` files (wrangler, tsconfig with comments) parse cleanly.
- shell-parser: declare `jenkinsfile`. Tighten function-detection regex
to require a reachable `{` brace so `name() echo hi` and patterns
appearing inside heredocs are no longer false-positives.
- markdown-parser: track fenced-code-block state and skip headings
inside ``` / ~~~ blocks (`# install` shell comments were being
emitted as level-1 sections).
- merge-batch-graphs.py: add `article`, `entity`, `topic`, `claim`,
`source` to VALID_NODE_PREFIXES and TYPE_TO_PREFIX so knowledge-base
node types stop being flagged unknown / coerced to `file:`. Add
`direction` to the edge dedup key so `forward` and `bidirectional`
variants of the same (src, tgt, type) don't overwrite each other.
Use a placeholder in bare-id fallback when `filePath` is missing on
function/class nodes so unrelated `parse()` functions don't merge.
- typescript-extractor: actually compute `isDefault` for default
exports (was always emitted as `false` from buildResult).
- extract-structure.mjs: match `wc -l` semantics for `totalLines` so
the scanner's `sizeLines` and the extractor's `totalLines` agree on
POSIX text files. Filter the parser-imports fallback to relative-only
so `importCount` semantics stay *internal-import* whether the scanner
resolved them or not. Drop unused `isCode` local.
Tests: +19 cases covering JSONC parsing, markdown fenced-code skip,
YAML quoted-keys / array-root, shell function false-positives,
extract-structure import fallback semantics + totalLines off-by-one.
764 passing (was 745).
Bumps version to 2.6.2 across the five tracked manifests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Handle block-scoped PHP namespaces (`namespace Foo { class Bar {} }`)
by recursing into compound_statement bodies in PhpExtractor
- Separate C (.c/.h) from C++ (.cpp/.cc/.hpp) into distinct language
configs so .c/.h files resolve to language "c" instead of "cpp"
- Add Lua language config so .lua files resolve to "lua" instead of
"unknown" after the EXTENSION_LANGUAGE map was replaced by LanguageRegistry
- Update TreeSitterPlugin JSDoc to reflect all 10 supported languages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Completes the language extractor architecture — 10 languages with
tree-sitter support (TS, JS, Python, Go, Rust, Java, Ruby, PHP, C/C++, C#).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Handles methods, classes, modules, attr_* properties, require imports,
and call graph including bare identifier calls (no-arg method invocations).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the LanguageExtractor interface for Go, handling functions,
methods with receivers, structs, interfaces, imports, exports (via
capitalization convention), and call graph extraction. Includes 25 tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the LanguageExtractor interface for Python, extracting functions
(with type annotations, defaults, *args/**kwargs), classes (methods +
annotated properties), imports (plain, from, aliased, wildcard), exports
(top-level defs), and caller-callee call graphs. Includes 31 tests using
the real tree-sitter parser.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract all TypeScript/JavaScript-specific AST extraction functions
(extractParams, extractReturnType, extractImportSpecifiers, processTopLevelNode,
extractFunction, extractClass, extractVariableDeclarations, extractImport,
processExportStatement, and call graph walking) from TreeSitterPlugin into the
new TypeScriptExtractor class. TreeSitterPlugin now dispatches to registered
LanguageExtractor instances, defaulting to TypeScriptExtractor for backward
compatibility. All 426 existing tests pass unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GraphBuilder maintained its own ~60-line extension-to-language mapping that
duplicated and could diverge from the canonical LanguageRegistry. Now delegates
language detection to LanguageRegistry.getForFile(), eliminating the duplication
and ensuring new language configs are automatically picked up everywhere.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All five child-node loops in addNonCodeFileWithAnalysis shared the same four-line pattern: dedup check, nodeIds.add, nodes.push, and a contains edge push. A private addChildNode helper centralises this so each loop only constructs the node object specific to its type.
Default sort() uses Unicode code point ordering which can produce unexpected results for non-ASCII strings. localeCompare guarantees correct alphabetical ordering regardless of character set.
build() was returning direct references to the builder's internal arrays, allowing callers to mutate graph.nodes or graph.edges and corrupt the builder's state. Spreading into new arrays at build time prevents this at negligible cost.
addImportEdge and addCallEdge previously pushed edges unconditionally, allowing duplicate relationships if multiple agents reported the same import or call. A shared edgeKeys set keyed on type|source|target silently skips any edge that has already been recorded.
The endpoint name template was evaluated twice — once for name and once for summary. A single const removes the duplication and resolves the nested template literal lint warning.
addNonCodeFileWithAnalysis was reconstructing the fileId string independently of addNonCodeFile, creating a silent correctness risk if the ID construction logic ever changed. addNonCodeFile now returns the ID it used so the caller cannot go out of sync.
The same filePath.split("/").pop() ?? filePath expression appeared three times across addFile, addFileWithAnalysis, and addNonCodeFile. A private static helper centralises the logic and makes call sites easier to read.
Previously addNonCodeFileWithAnalysis rebuilt a full Set from this.nodes on every call, making duplicate checks O(n) per file and O(n²) overall. Moving nodeIds to a class field and updating it incrementally at each insertion reduces duplicate detection to O(1) per check.
Previously the mapping object was recreated on every mapKindToNodeType call. Moving it to module level means it is allocated once at load time instead of once per definition node processed.
Read the project's .gitignore at starter-file generation time and include
non-default patterns as commented suggestions in .understandignore. Patterns
already covered by hardcoded defaults are deduplicated (with trailing-slash
normalization). This is a one-time inclusion — users can later remove patterns
for files they want analyzed without the filter re-reading .gitignore.
Also fixes the starter header listing bin/ as a built-in default when it is
intentionally excluded from DEFAULT_IGNORE_PATTERNS (bin/ is used by Node/Ruby
CLI launchers).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove bin/ from DEFAULT_IGNORE_PATTERNS (Node/Ruby CLI launchers use bin/)
- .NET users can add bin/ to .understandignore manually
- Fix project-scanner Step 2.5 to re-filter from original file list when
.understandignore exists, ensuring ! negation correctly overrides defaults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove direction-inverting `implemented_by` alias (same pattern as fd0df15)
- Replace ambiguous `process` alias with `business_process`
- Fix duplicate React Flow edge IDs in DomainGraphView
- Fix navigateToDomain clearing selectedNodeId and losing history
- Preserve domain viewMode when structural graph loads after domain graph
- Add domain/flow/step to fileLevelTypes in GraphView
- Add domain edge category to EDGE_CATEGORY_MAP
- Extend COMPLEXITY_STRING_MAP with trivial/basic/mid/average/advanced
- Normalize string complexity values in normalizeBatchOutput (not just numeric)
- Infer node type from ID prefix in edge fallback normalization
- Include flow discriminator in bare-path step ID normalization
- Clean up domain-context.json intermediate file in SKILL.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix race condition: setGraph no longer wipes domainGraph on parallel fetch
- Remove workflow/action aliases that conflicted with pipeline type
- Remove duplicate onNodeDoubleClick handler in DomainGraphView
- Add clearActiveDomain store action (replaces direct setState call)
- Remove auto-switch to domain viewMode in setDomainGraph
- Add DomainMetaSchema Zod validation for domainMeta fields
- Add Array.isArray guards for domainMeta collections in NodeInfo
- Remove as-any cast in getDomainMeta (use typed domainMeta directly)
- Add "domain" filter category for domain/flow/step nodes
- Keep flow discriminator in step ID normalization to prevent collisions
- Update SKILL.md Phase 2 to use tool-based scanning (no missing script)
- Update EDGE_LABELS comment to reflect 29 edge types
- Bump version to 2.1.0 in all 4 required files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add all 13 node types (including non-code) to VALID_PREFIXES and TYPE_TO_PREFIX
to prevent valid IDs like config:tsconfig.json from being stripped
- Add fallback normalizeNodeId on edge endpoints not found in idMap, fixing
silent relationship loss when edges use different malformed variants than nodes
- Add DroppedEdge interface with source, target, type, and reason fields so
callers can surface exactly which edges were lost and why
- Use honest Record<string, unknown>[] return types instead of unsafe type casts
- Align SKILL.md complexity aliases with COMPLEXITY_STRING_MAP
- Add 5 new tests for non-code types, cross-variant edges, dropped edge detail,
and validateGraph integration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parallel file-analyzer subagents can produce inconsistent node IDs
(project-name prefixed, double-prefixed, bare paths) and invalid
complexity values. Phase 3 ASSEMBLE now normalizes these deterministically
before merging, preventing cascading edge drops and dashboard load failures.
- Add normalize-graph.ts with normalizeNodeId, normalizeComplexity,
and normalizeBatchOutput utilities
- Rewrite SKILL.md Phase 3 with 6-step normalization sequence
- Strengthen file-analyzer prompt with ID format warnings
- Add 32 normalization tests and 2 schema boundary tests