Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
67 changes: 36 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ A multi-agent pipeline scans your project, extracts every file, function, class,
On the **first run** in a project — when you don't pass `--language` and no language is stored yet — `/understand` detects the language you're conversing in. If it isn't English, it asks you to confirm (or override) before generating; English conversations are unaffected. Your choice is saved to `.understand-anything/config.json` and reused on every later run.

The `--language` parameter affects:

- Node summaries and descriptions in the knowledge graph
- Dashboard UI labels, buttons, and tooltips
- Guided tour explanations
Expand Down Expand Up @@ -188,6 +189,9 @@ An interactive web dashboard opens with your codebase visualized as a graph —

## 🌐 Multi-Platform Installation

> **Prerequisites:** [Node.js ≥ 22](https://nodejs.org) and [pnpm ≥ 10](https://pnpm.io/installation).
> The installers below will check for pnpm and install it automatically if it's missing.

Understand-Anything works across multiple AI coding platforms.

### Claude Code (Native)
Expand All @@ -202,13 +206,15 @@ Understand-Anything works across multiple AI coding platforms.


**macOS / Linux:**

```bash
curl -fsSL https://raw.githubusercontent.com/Egonex-AI/Understand-Anything/main/install.sh | bash
# or skip the prompt by passing the platform:
curl -fsSL https://raw.githubusercontent.com/Egonex-AI/Understand-Anything/main/install.sh | bash -s codex
```

**Windows (PowerShell):**

```powershell
iwr -useb https://raw.githubusercontent.com/Egonex-AI/Understand-Anything/main/install.ps1 | iex
```
Expand Down Expand Up @@ -251,26 +257,25 @@ For personal skills (available across all projects), run the `install.sh` above

### Platform Compatibility

| Platform | Status | Install Method |
|----------|--------|----------------|
| Claude Code | ✅ Native | Plugin marketplace |
| Cursor | ✅ Supported | Auto-discovery |
| VS Code + GitHub Copilot | ✅ Supported | Auto-discovery |
| Copilot CLI | ✅ Supported | Plugin install |
| Codex | ✅ Supported | `install.sh codex` |
| OpenCode | ✅ Supported | `install.sh opencode` |
| OpenClaw | ✅ Supported | `install.sh openclaw` |
| Antigravity | ✅ Supported | `install.sh antigravity` |
| Gemini CLI | ✅ Supported | `install.sh gemini` |
| Pi Agent | ✅ Supported | `install.sh pi` |
| Vibe CLI | ✅ Supported | `install.sh vibe` |
| Hermes | ✅ Supported | `install.sh hermes` |
| Cline | ✅ Supported | `install.sh cline` |
| KIMI CLI | ✅ Supported | `install.sh kimi` |
| Trae | ✅ Supported | `install.sh trae` |
| Nanobot | ✅ Supported | `install.sh nanobot` |
| Kiro CLI / IDE | ✅ Supported | `install.sh kiro` |

| Platform | Status | Install Method |
| ------------------------ | ------------ | ------------------------ |
| Claude Code | ✅ Native | Plugin marketplace |
| Cursor | ✅ Supported | Auto-discovery |
| VS Code + GitHub Copilot | ✅ Supported | Auto-discovery |
| Copilot CLI | ✅ Supported | Plugin install |
| Codex | ✅ Supported | `install.sh codex` |
| OpenCode | ✅ Supported | `install.sh opencode` |
| OpenClaw | ✅ Supported | `install.sh openclaw` |
| Antigravity | ✅ Supported | `install.sh antigravity` |
| Gemini CLI | ✅ Supported | `install.sh gemini` |
| Pi Agent | ✅ Supported | `install.sh pi` |
| Vibe CLI | ✅ Supported | `install.sh vibe` |
| Hermes | ✅ Supported | `install.sh hermes` |
| Cline | ✅ Supported | `install.sh cline` |
| KIMI CLI | ✅ Supported | `install.sh kimi` |
| Trae | ✅ Supported | `install.sh trae` |
| Nanobot | ✅ Supported | `install.sh nanobot` |
| Kiro CLI / IDE | ✅ Supported | `install.sh kiro` |

---

Expand All @@ -280,7 +285,7 @@ The graph is just JSON — **commit it once, and teammates skip the pipeline**.

> **Example:** [GoogleCloudPlatform/microservices-demo](https://github.com/GoogleCloudPlatform/microservices-demo) — Go / Java / Python / Node reference with a committed graph.

**What to commit:** everything in `.understand-anything/` *except* `intermediate/` and `diff-overlay.json` (those are local scratch).
**What to commit:** everything in `.understand-anything/` _except_ `intermediate/` and `diff-overlay.json` (those are local scratch).

```gitignore
.understand-anything/intermediate/
Expand Down Expand Up @@ -308,21 +313,21 @@ Static analysis and LLMs do what each does best:
- **Tree-sitter (deterministic)** — parses source into a concrete syntax tree and extracts structural facts: imports, exports, function/class definitions, call sites, inheritance. Pre-resolved into an `importMap` during the scan phase and passed to file-analyzers so they don't re-derive imports from source. Same input → same output, every run. Also powers fingerprint-based change detection for incremental updates.
- **LLM (semantic)** — reads the parsed structure alongside the original source to produce what parsers can't: plain-English summaries, tags, architectural layer assignments, business-domain mapping, guided tours, language concept callouts.

This split is why the graph is reproducible on the structural side (the same code always yields the same edges) while still capturing intent on the semantic side (what a file is *for*, not just what it imports).
This split is why the graph is reproducible on the structural side (the same code always yields the same edges) while still capturing intent on the semantic side (what a file is _for_, not just what it imports).

### Multi-Agent Pipeline

The `/understand` command orchestrates 5 specialized agents, and `/understand-domain` adds a 6th:

| Agent | Role |
|-------|------|
| `project-scanner` | Discover files, detect languages and frameworks |
| `file-analyzer` | Extract functions, classes, imports; produce graph nodes and edges |
| `architecture-analyzer` | Identify architectural layers |
| `tour-builder` | Generate guided learning tours |
| `graph-reviewer` | Validate graph completeness and referential integrity (runs inline by default; use `--review` for full LLM review) |
| `domain-analyzer` | Extract business domains, flows, and process steps (used by `/understand-domain`) |
| `article-analyzer` | Extract entities, claims, and implicit relationships from wiki articles (used by `/understand-knowledge`) |
| Agent | Role |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `project-scanner` | Discover files, detect languages and frameworks |
| `file-analyzer` | Extract functions, classes, imports; produce graph nodes and edges |
| `architecture-analyzer` | Identify architectural layers |
| `tour-builder` | Generate guided learning tours |
| `graph-reviewer` | Validate graph completeness and referential integrity (runs inline by default; use `--review` for full LLM review) |
| `domain-analyzer` | Extract business domains, flows, and process steps (used by `/understand-domain`) |
| `article-analyzer` | Extract entities, claims, and implicit relationships from wiki articles (used by `/understand-knowledge`) |

File analyzers run in parallel (up to 5 concurrent, 20-30 files per batch). Supports incremental updates — only re-analyzes files that changed since the last run.

Expand Down
28 changes: 28 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,22 @@ function Prompt-Platform {

function Get-SkillsRoot { Join-Path $RepoDir 'understand-anything-plugin\skills' }

function Ensure-Pnpm {
if (Get-Command pnpm -ErrorAction SilentlyContinue) { return }
Write-Host '→ pnpm not found — installing...'
if (Get-Command corepack -ErrorAction SilentlyContinue) {
corepack enable pnpm
} elseif (Get-Command npm -ErrorAction SilentlyContinue) {
npm install -g pnpm
} else {
Write-Error "Node.js (and npm or corepack) is required but not found.`nInstall Node.js >= 22 from https://nodejs.org, then re-run this script."
}
if (-not (Get-Command pnpm -ErrorAction SilentlyContinue)) {
Write-Error "pnpm installation failed. Install it manually: https://pnpm.io/installation"
}
Write-Host '✓ pnpm installed.'
}

function Clone-Or-Update {
if (Test-Path (Join-Path $RepoDir '.git')) {
Write-Host "→ Updating existing checkout at $RepoDir"
Expand All @@ -95,6 +111,17 @@ function Clone-Or-Update {
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent | Out-Null }
git clone $RepoUrl $RepoDir
}
# If this script is running from a local git checkout, install that same branch.
# Falls back gracefully when run via curl/iwr (no git context).
$scriptBranch = ''
if (Test-Path (Join-Path $PSScriptRoot '.git')) {
$scriptBranch = git -C $PSScriptRoot branch --show-current 2>$null
}
if ($scriptBranch -and $scriptBranch -ne 'main') {
Write-Host "→ Checking out branch: $scriptBranch"
git -C $RepoDir fetch origin
git -C $RepoDir checkout $scriptBranch
}
}

function Get-SkillNames {
Expand Down Expand Up @@ -200,6 +227,7 @@ function ConvertTo-FileUri([string]$Path) {

function Cmd-Install([string]$Id) {
$cfg = Resolve-Platform $Id
Ensure-Pnpm
Clone-Or-Update
Write-Host "→ Linking skills for $Id ($($cfg.Style) → $($cfg.Target))"
Link-Skills $cfg.Target $cfg.Style
Expand Down
34 changes: 34 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,27 @@ prompt_platform() {
printf '%s\n' "${ids[$((choice-1))]}"
}

ensure_pnpm() {
if command -v pnpm >/dev/null 2>&1; then
return 0
fi
printf -- '→ pnpm not found — installing...\n'
if command -v corepack >/dev/null 2>&1; then
corepack enable pnpm
elif command -v npm >/dev/null 2>&1; then
npm install -g pnpm
else
printf 'Error: Node.js (and npm or corepack) is required but not found.\n' >&2
printf 'Install Node.js ≥ 22 from https://nodejs.org, then re-run this script.\n' >&2
exit 1
fi
if ! command -v pnpm >/dev/null 2>&1; then
printf 'Error: pnpm installation failed. Install it manually: https://pnpm.io/installation\n' >&2
exit 1
fi
printf '✓ pnpm installed.\n'
}

clone_or_update() {
if [[ -d "$REPO_DIR/.git" ]]; then
printf -- '→ Updating existing checkout at %s\n' "$REPO_DIR"
Expand All @@ -99,6 +120,18 @@ clone_or_update() {
mkdir -p "$(dirname "$REPO_DIR")"
git clone "$REPO_URL" "$REPO_DIR"
fi
# If this script is running from a local git checkout, install that same branch.
# Falls back gracefully when piped from curl (no git context).
local script_dir script_branch=""
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-}")" 2>/dev/null && pwd || true)"
if [[ -n "$script_dir" ]] && git -C "$script_dir" rev-parse --git-dir >/dev/null 2>&1; then
script_branch="$(git -C "$script_dir" branch --show-current 2>/dev/null || true)"
fi
if [[ -n "$script_branch" && "$script_branch" != "main" ]]; then
printf -- '→ Checking out branch: %s\n' "$script_branch"
git -C "$REPO_DIR" fetch origin
git -C "$REPO_DIR" checkout "$script_branch"
fi
}

skills_root() { printf '%s\n' "$REPO_DIR/understand-anything-plugin/skills"; }
Expand Down Expand Up @@ -185,6 +218,7 @@ cmd_install() {
target="$(printf '%s\n' "$row" | cut -d'|' -f2)"
style="$(printf '%s\n' "$row" | cut -d'|' -f3)"

ensure_pnpm
clone_or_update
printf -- '→ Linking skills for %s (%s → %s)\n' "$id" "$style" "$target"
link_skills "$target" "$style"
Expand Down
41 changes: 41 additions & 0 deletions scripts/build-pascal-wasm.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<#
.SYNOPSIS
Build tree-sitter-pascal.wasm using Docker + Emscripten.

.DESCRIPTION
The resulting WASM is placed inside the installed package so web-tree-sitter
can load it via require.resolve().

.NOTES
Prerequisites: Docker daemon running with Emscripten image available.
Run 'pnpm install' inside understand-anything-plugin/ before this script.
#>

$ErrorActionPreference = 'Stop'

$ScriptDir = $PSScriptRoot
$PluginDir = Join-Path $ScriptDir '..\understand-anything-plugin'
$GrammarDir = Join-Path $PluginDir 'node_modules\tree-sitter-pascal'

if (-not (Test-Path $GrammarDir)) {
Write-Error "tree-sitter-pascal not found at $GrammarDir`nRun 'pnpm install' inside understand-anything-plugin/ first."
}

$GrammarDirAbs = (Resolve-Path $GrammarDir).Path
$OutFile = Join-Path $GrammarDirAbs 'tree-sitter-pascal.wasm'

Write-Host "→ Building tree-sitter-pascal.wasm..."
docker run --rm `
-v "${GrammarDirAbs}:/src" `
-w /src `
emscripten/emsdk `
emcc src/parser.c `
-o tree-sitter-pascal.wasm `
-Os `
-s WASM=1 `
-s SIDE_MODULE=1 `
"-s EXPORTED_FUNCTIONS=['_tree_sitter_pascal']" `
-fvisibility=hidden `
-I./src

Write-Host "✓ Built: $OutFile"
37 changes: 37 additions & 0 deletions scripts/build-pascal-wasm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Build tree-sitter-pascal.wasm using Docker + Emscripten.
# The resulting WASM is placed inside the installed package so web-tree-sitter
# can load it via require.resolve().
#
# Prerequisites: Docker daemon running with Emscripten image available.
# Usage: bash scripts/build-pascal-wasm.sh

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLUGIN_DIR="$SCRIPT_DIR/../understand-anything-plugin"
GRAMMAR_DIR="$PLUGIN_DIR/node_modules/tree-sitter-pascal"

if [[ ! -d "$GRAMMAR_DIR" ]]; then
echo "Error: tree-sitter-pascal not found at $GRAMMAR_DIR"
echo "Run 'pnpm install' inside understand-anything-plugin/ first."
exit 1
fi

OUT_FILE="$GRAMMAR_DIR/tree-sitter-pascal.wasm"

echo "→ Building tree-sitter-pascal.wasm..."
docker run --rm \
-v "$GRAMMAR_DIR:/src" \
-w /src \
emscripten/emsdk \
emcc src/parser.c \
-o tree-sitter-pascal.wasm \
-Os \
-s WASM=1 \
-s SIDE_MODULE=1 \
-s "EXPORTED_FUNCTIONS=['_tree_sitter_pascal']" \
-fvisibility=hidden \
-I./src

echo "✓ Built: $OUT_FILE"
4 changes: 3 additions & 1 deletion tests/skill/understand/test_extract_import_map.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1672,7 +1672,9 @@ describe('extract-import-map.mjs — Rust crate root missing', () => {
});
});

describe('extract-import-map.mjs — tree-sitter init graceful failure', () => {
// ESM loader hooks (--import) do not reliably intercept native module resolution
// on Windows, so the synthetic tree-sitter failure cannot be injected there.
describe.skipIf(process.platform === 'win32')('extract-import-map.mjs — tree-sitter init graceful failure', () => {
let projectRoot;

afterEach(() => {
Expand Down
6 changes: 5 additions & 1 deletion tests/skill/understand/test_scan_project.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,16 @@ describe('scan-project.mjs — .understandignore handling', () => {
// specific file with `!keep.log`. After the override, keep.log MUST
// appear in the output. It is NOT counted in filteredByIgnore (it
// was re-included, not additionally filtered).
//
// gitInit:false — use the recursive walker so global gitignore rules
// (which may include *.log on some developer machines) don't shadow
// the .understandignore negation before it can take effect.
projectRoot = setupTree({
'.understandignore': '!keep.log\n',
'src/index.ts': 'export const x = 1;\n',
'keep.log': 'important diagnostic\n',
'drop.log': 'noise\n',
});
}, { gitInit: false });
const r = runScript(projectRoot);
expect(r.status).toBe(0);
expect(byPath(r.output, 'keep.log')).toBeDefined();
Expand Down
1 change: 1 addition & 0 deletions understand-anything-plugin/packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"dependencies": {
"@tree-sitter-grammars/tree-sitter-kotlin": "1.1.0",
"@understand-anything/tree-sitter-dart-wasm": "workspace:*",
"@understand-anything/tree-sitter-pascal-wasm": "workspace:*",
"fuse.js": "^7.1.0",
"ignore": "^7.0.5",
"tree-sitter-c-sharp": "^0.23.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { cppConfig } from "./cpp.js";
import { dartConfig } from "./dart.js";
import { csharpConfig } from "./csharp.js";
import { luaConfig } from "./lua.js";
import { pascalConfig } from "./pascal.js";
// Non-code language configs
import { markdownConfig } from "./markdown.js";
import { yamlConfig } from "./yaml.js";
Expand Down Expand Up @@ -55,6 +56,7 @@ export const builtinLanguageConfigs: LanguageConfig[] = [
swiftConfig,
kotlinConfig,
luaConfig,
pascalConfig,
cConfig,
cppConfig,
dartConfig,
Expand Down Expand Up @@ -101,6 +103,7 @@ export {
swiftConfig,
kotlinConfig,
luaConfig,
pascalConfig,
cConfig,
cppConfig,
dartConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { LanguageConfig } from "../types.js";

export const pascalConfig = {
id: "pascal",
displayName: "Pascal",
extensions: [".pas", ".dpr", ".lpr", ".pp"],
treeSitter: {
wasmPackage: "@understand-anything/tree-sitter-pascal-wasm",
wasmFile: "tree-sitter-pascal.wasm",
},
concepts: [
"units and interfaces",
"classes and records",
"properties and RTTI",
"generics",
"interfaces (COM-compatible)",
"anonymous methods",
"operator overloading",
"inline variables",
"attributes",
"message handling",
],
filePatterns: {
entryPoints: ["*.dpr", "*.lpr"],
barrels: [],
tests: ["*Test.pas", "*Tests.pas", "*_test.pas"],
config: ["*.dproj", "*.lpi", "*.cfg", "*.ini"],
},
} satisfies LanguageConfig;
Loading