mirror of
https://github.com/Egonex-AI/Understand-Anything.git
synced 2026-06-22 10:58:03 +08:00
Merge branch 'main' into fix/import-resolver-nodenext-rewrite
Resolve conflict in tests/skill/understand/test_extract_import_map.test.mjs by keeping both new test groups — they cover independent fixes that should coexist: - upstream #214: tsconfig path-alias targets with leading "./" - this PR #294: NodeNext .js → .ts rewrite for ESM TypeScript imports The extract-import-map.mjs script auto-merged cleanly; both fixes are already present in the merged source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
<a href="#gemini-cli"><img src="https://img.shields.io/badge/Gemini_CLI-4285F4" alt="Gemini CLI" /></a>
|
||||
<a href="#opencode"><img src="https://img.shields.io/badge/OpenCode-38bdf8" alt="OpenCode" /></a>
|
||||
<a href="#mistral-vibe-cli"><img src="https://img.shields.io/badge/Vibe_CLI-7c3aed" alt="Vibe CLI" /></a>
|
||||
<a href="#trae"><img src="https://img.shields.io/badge/Trae-7e22ce" alt="Trae" /></a>
|
||||
<a href="https://understand-anything.com"><img src="https://img.shields.io/badge/Homepage-d4a574" alt="Homepage" /></a>
|
||||
<a href="https://understand-anything.com/demo/"><img src="https://img.shields.io/badge/Live_Demo-00c853" alt="Live Demo" /></a>
|
||||
</p>
|
||||
@@ -183,7 +184,7 @@ Understand-Anything works across multiple AI coding platforms.
|
||||
/plugin install understand-anything
|
||||
```
|
||||
|
||||
### One-line install (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI)
|
||||
### One-line install (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI / Trae)
|
||||
|
||||
**macOS / Linux:**
|
||||
```bash
|
||||
@@ -199,7 +200,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins
|
||||
|
||||
The installer clones the repo to `~/.understand-anything/repo` and creates the right symlinks for the chosen platform. Restart your CLI/IDE afterwards.
|
||||
|
||||
- Supported `<platform>` values: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode`, `hermes`, `cline`, `kimi`
|
||||
- Supported `<platform>` values: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode`, `hermes`, `cline`, `kimi`, `trae`
|
||||
- Update later: `./install.sh --update`
|
||||
- Uninstall: `./install.sh --uninstall <platform>`
|
||||
|
||||
@@ -239,6 +240,7 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin
|
||||
| Hermes | ✅ Supported | `install.sh hermes` |
|
||||
| Cline | ✅ Supported | `install.sh cline` |
|
||||
| KIMI CLI | ✅ Supported | `install.sh kimi` |
|
||||
| Trae | ✅ Supported | `install.sh trae` |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -205,6 +205,8 @@ El instalador clona el repositorio en `~/.understand-anything/repo` y crea los e
|
||||
|
||||
Cursor detecta automáticamente el plugin a través de `.cursor-plugin/plugin.json` cuando se clona este repositorio. No requiere instalación manual: simplemente clona y abre en Cursor.
|
||||
|
||||
Si la detección automática no lo reconoce, instálalo manualmente: abre **Cursor Settings → Plugins**, pega `https://github.com/Lum1104/Understand-Anything` en el campo de búsqueda y añádelo desde allí.
|
||||
|
||||
### VS Code + GitHub Copilot
|
||||
|
||||
VS Code con GitHub Copilot (v1.108+) detecta automáticamente el plugin a través de `.copilot-plugin/plugin.json` cuando se clona este repositorio. No requiere instalación manual: simplemente clona y abre en VS Code.
|
||||
|
||||
@@ -206,6 +206,8 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins
|
||||
|
||||
Cursorはこのリポジトリをクローンすると `.cursor-plugin/plugin.json` 経由でプラグインを自動検出します。手動インストールは不要です — クローンしてCursorで開くだけです。
|
||||
|
||||
自動検出されない場合は、手動でインストールしてください:**Cursor Settings → Plugins** を開き、検索欄に `https://github.com/Lum1104/Understand-Anything` を貼り付けて追加します。
|
||||
|
||||
### VS Code + GitHub Copilot
|
||||
|
||||
GitHub Copilot拡張機能(v1.108+)をインストールしたVS Codeは、`.copilot-plugin/plugin.json` 経由でプラグインを自動検出します。クローンしてVS Codeで開くだけで、手動インストールは不要です。
|
||||
|
||||
@@ -205,6 +205,8 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins
|
||||
|
||||
이 저장소를 클론하면 Cursor가 `.cursor-plugin/plugin.json`을 통해 플러그인을 자동으로 인식합니다. 수동 설치가 필요 없습니다. 클론 후 Cursor에서 열기만 하면 됩니다.
|
||||
|
||||
자동 인식이 되지 않으면 수동으로 설치하세요: **Cursor Settings → Plugins**를 열고 검색란에 `https://github.com/Lum1104/Understand-Anything`를 붙여넣은 뒤 추가하세요.
|
||||
|
||||
### VS Code + GitHub Copilot
|
||||
|
||||
GitHub Copilot(v1.108+)이 설치된 VS Code는 `.copilot-plugin/plugin.json`을 통해 플러그인을 자동으로 인식합니다. 수동 설치가 필요 없습니다. 클론 후 VS Code에서 열기만 하면 됩니다.
|
||||
|
||||
@@ -206,6 +206,8 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins
|
||||
|
||||
Cursor автоматически обнаруживает плагин через `.cursor-plugin/plugin.json` при клонировании этого репозитория. Ручная установка не требуется — просто склонируйте и откройте в Cursor.
|
||||
|
||||
Если автообнаружение не сработало, установите вручную: откройте **Cursor Settings → Plugins**, вставьте `https://github.com/Lum1104/Understand-Anything` в поле поиска и добавьте оттуда.
|
||||
|
||||
### VS Code + GitHub Copilot
|
||||
|
||||
VS Code с GitHub Copilot (v1.108+) автоматически обнаруживает плагин через `.copilot-plugin/plugin.json` при клонировании этого репозитория. Ручная установка не требуется — просто склонируйте и откройте в VS Code.
|
||||
|
||||
@@ -206,6 +206,8 @@ Kurulum betiği depoyu `~/.understand-anything/repo` dizinine klonlar ve seçile
|
||||
|
||||
Bu depo klonlandığında Cursor, eklentiyi `.cursor-plugin/plugin.json` aracılığıyla otomatik olarak keşfeder. Manuel kurulum gerekmez — sadece klonla ve Cursor'da aç.
|
||||
|
||||
Otomatik keşif çalışmazsa manuel kur: **Cursor Settings → Plugins**'i aç, arama alanına `https://github.com/Lum1104/Understand-Anything` yapıştır ve oradan ekle.
|
||||
|
||||
### VS Code + GitHub Copilot
|
||||
|
||||
GitHub Copilot uzantısı (v1.108+) yüklü VS Code, `.copilot-plugin/plugin.json` aracılığıyla eklentiyi otomatik keşfeder. Manuel kurulum gerekmez — sadece klonla ve VS Code'da aç.
|
||||
|
||||
@@ -205,6 +205,8 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins
|
||||
|
||||
克隆此仓库后,Cursor 会自动通过 `.cursor-plugin/plugin.json`文件发现插件。无需手动安装 — 只需克隆并在 Cursor 中打开即可。
|
||||
|
||||
若自动发现未生效,可手动安装:打开 **Cursor Settings → Plugins**,在搜索框中粘贴 `https://github.com/Lum1104/Understand-Anything` 并添加。
|
||||
|
||||
### VS Code + GitHub Copilot
|
||||
|
||||
安装 GitHub Copilot 扩展(v1.108+)后,VS Code 会通过 `.copilot-plugin/plugin.json` 自动发现插件,克隆后直接在 VS Code 中打开即可,无需手动安装。
|
||||
|
||||
@@ -205,6 +205,8 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins
|
||||
|
||||
複製此儲存庫後,Cursor 會自動透過 `.cursor-plugin/plugin.json` 檔案發現外掛程式。無需手動安裝 — 只需複製並在 Cursor 中開啟即可。
|
||||
|
||||
若自動發現未生效,可手動安裝:開啟 **Cursor Settings → Plugins**,在搜尋框中貼上 `https://github.com/Lum1104/Understand-Anything` 並新增。
|
||||
|
||||
### VS Code + GitHub Copilot
|
||||
|
||||
安裝 GitHub Copilot 擴充功能(v1.108+)後,VS Code 會透過 `.copilot-plugin/plugin.json` 自動發現外掛程式,複製後直接在 VS Code 中開啟即可,無需手動安裝。
|
||||
|
||||
@@ -38,6 +38,7 @@ $Platforms = [ordered]@{
|
||||
hermes = @{ Target = (Join-Path $HOME '.hermes\skills'); Style = 'folder' }
|
||||
cline = @{ Target = (Join-Path $HOME '.cline\skills'); Style = 'folder' }
|
||||
kimi = @{ Target = (Join-Path $HOME '.kimi\skills'); Style = 'folder' }
|
||||
trae = @{ Target = (Join-Path $HOME '.trae\skills'); Style = 'per-skill' }
|
||||
}
|
||||
|
||||
function Show-Usage {
|
||||
|
||||
@@ -39,6 +39,7 @@ vscode|$HOME/.copilot/skills|per-skill
|
||||
hermes|$HOME/.hermes/skills|folder
|
||||
cline|$HOME/.cline/skills|folder
|
||||
kimi|$HOME/.kimi/skills|folder
|
||||
trae|$HOME/.trae/skills|per-skill
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
@@ -238,6 +238,111 @@ describe('extract-import-map.mjs — TypeScript / JavaScript resolver', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// ── Issue #214: tsconfig path-alias targets with leading "./" ───────────
|
||||
// create-next-app ships `"@/*": ["./*"]` as the default. With a root
|
||||
// tsconfig the candidate would stay as "./lib/thing" while ctx.fileSet
|
||||
// stores normalized "lib/thing", silently dropping every cross-module
|
||||
// import edge. Three originally broken cases plus one regression guard
|
||||
// for the already working `["*"]` form.
|
||||
|
||||
it('resolves tsconfig paths with leading "./" target and no baseUrl (#214)', () => {
|
||||
projectRoot = setupTree({
|
||||
'tsconfig.json': JSON.stringify({
|
||||
compilerOptions: {
|
||||
paths: { '@/*': ['./*'] },
|
||||
},
|
||||
}),
|
||||
'src/app.ts': `import { x } from '@/lib/thing';\nconst _ = x;\n`,
|
||||
'lib/thing.ts': `export const x = 1;\n`,
|
||||
});
|
||||
|
||||
const result = runScript(projectRoot, {
|
||||
projectRoot,
|
||||
files: [
|
||||
{ path: 'tsconfig.json', language: 'json', fileCategory: 'config' },
|
||||
{ path: 'src/app.ts', language: 'typescript', fileCategory: 'code' },
|
||||
{ path: 'lib/thing.ts', language: 'typescript', fileCategory: 'code' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.output.importMap['src/app.ts']).toContain('lib/thing.ts');
|
||||
});
|
||||
|
||||
it('resolves tsconfig paths with leading "./" target and baseUrl "." (#214)', () => {
|
||||
projectRoot = setupTree({
|
||||
'tsconfig.json': JSON.stringify({
|
||||
compilerOptions: {
|
||||
baseUrl: '.',
|
||||
paths: { '@/*': ['./*'] },
|
||||
},
|
||||
}),
|
||||
'src/app.ts': `import { x } from '@/lib/thing';\nconst _ = x;\n`,
|
||||
'lib/thing.ts': `export const x = 1;\n`,
|
||||
});
|
||||
|
||||
const result = runScript(projectRoot, {
|
||||
projectRoot,
|
||||
files: [
|
||||
{ path: 'tsconfig.json', language: 'json', fileCategory: 'config' },
|
||||
{ path: 'src/app.ts', language: 'typescript', fileCategory: 'code' },
|
||||
{ path: 'lib/thing.ts', language: 'typescript', fileCategory: 'code' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.output.importMap['src/app.ts']).toContain('lib/thing.ts');
|
||||
});
|
||||
|
||||
it('resolves tsconfig paths with leading "./" target and baseUrl "src" (#214)', () => {
|
||||
projectRoot = setupTree({
|
||||
'tsconfig.json': JSON.stringify({
|
||||
compilerOptions: {
|
||||
baseUrl: 'src',
|
||||
paths: { '@/*': ['./*'] },
|
||||
},
|
||||
}),
|
||||
'src/app.ts': `import { x } from '@/thing';\nconst _ = x;\n`,
|
||||
'src/thing.ts': `export const x = 1;\n`,
|
||||
});
|
||||
|
||||
const result = runScript(projectRoot, {
|
||||
projectRoot,
|
||||
files: [
|
||||
{ path: 'tsconfig.json', language: 'json', fileCategory: 'config' },
|
||||
{ path: 'src/app.ts', language: 'typescript', fileCategory: 'code' },
|
||||
{ path: 'src/thing.ts', language: 'typescript', fileCategory: 'code' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.output.importMap['src/app.ts']).toContain('src/thing.ts');
|
||||
});
|
||||
|
||||
it('keeps resolving tsconfig paths with bare "*" target (#214 regression guard)', () => {
|
||||
projectRoot = setupTree({
|
||||
'tsconfig.json': JSON.stringify({
|
||||
compilerOptions: {
|
||||
paths: { '@/*': ['*'] },
|
||||
},
|
||||
}),
|
||||
'src/app.ts': `import { x } from '@/lib/thing';\nconst _ = x;\n`,
|
||||
'lib/thing.ts': `export const x = 1;\n`,
|
||||
});
|
||||
|
||||
const result = runScript(projectRoot, {
|
||||
projectRoot,
|
||||
files: [
|
||||
{ path: 'tsconfig.json', language: 'json', fileCategory: 'config' },
|
||||
{ path: 'src/app.ts', language: 'typescript', fileCategory: 'code' },
|
||||
{ path: 'lib/thing.ts', language: 'typescript', fileCategory: 'code' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.output.importMap['src/app.ts']).toContain('lib/thing.ts');
|
||||
});
|
||||
|
||||
// ── #294: NodeNext / ESM TypeScript `.js → .ts` rewrite ────────────────
|
||||
//
|
||||
// Under `moduleResolution: NodeNext`, TypeScript does NOT rewrite import
|
||||
|
||||
@@ -470,9 +470,18 @@ export function resolveTsJsImport(rawImport, file, ctx) {
|
||||
const relativeToConfig = normalizedBase
|
||||
? posix.join(normalizedBase, mapped)
|
||||
: mapped;
|
||||
const candidate = tsConfigDir
|
||||
? posix.join(tsConfigDir, relativeToConfig)
|
||||
: relativeToConfig;
|
||||
// posix.normalize strips a leading "./" left over when both
|
||||
// tsConfigDir and normalizedBase are empty (root tsconfig with
|
||||
// `"@/*": ["./*"]`, the create-next-app default). Without this the
|
||||
// candidate stays as "./foo" while ctx.fileSet stores "foo", and
|
||||
// probeWithExtensions silently drops every cross-module edge.
|
||||
const candidate = posix.normalize(
|
||||
tsConfigDir
|
||||
? posix.join(tsConfigDir, relativeToConfig)
|
||||
: relativeToConfig,
|
||||
);
|
||||
// Defensive: tsconfig targets shouldn't escape the project root.
|
||||
if (candidate.startsWith('..')) continue;
|
||||
const probed = probeWithExtensions(candidate, ctx.fileSet);
|
||||
if (probed) return probed;
|
||||
}
|
||||
|
||||
@@ -464,17 +464,25 @@ function toPosix(p) {
|
||||
* so the ignore filter has to do more work in the fallback path.
|
||||
*/
|
||||
function enumerateViaGit(projectRoot) {
|
||||
const result = spawnSync('git', ['ls-files', '-co', '--exclude-standard'], {
|
||||
// -z = NUL-terminated output. Without it, `git ls-files` C-escapes non-ASCII
|
||||
// bytes in path names — paths containing emoji, accented characters, CJK
|
||||
// codepoints, etc. come back quoted with octal escapes (e.g.
|
||||
// `"30. \360\237\217\227 BD-CCER/file.md"` for a path containing 🏗️).
|
||||
// Those quoted-escaped strings then fail to round-trip back to real disk
|
||||
// paths in downstream consumers, so files in such directories are silently
|
||||
// dropped from the scan. The -z form emits raw bytes between NUL separators,
|
||||
// preserving every codepoint as-is. This is the same approach git itself
|
||||
// uses for `--null` everywhere downstream (xargs -0, etc.).
|
||||
const result = spawnSync('git', ['ls-files', '-z', '-co', '--exclude-standard'], {
|
||||
cwd: projectRoot,
|
||||
encoding: 'utf-8',
|
||||
maxBuffer: 256 * 1024 * 1024, // 256MB — huge monorepos can produce >10MB of paths
|
||||
});
|
||||
if (result.status !== 0 || !result.stdout) return null;
|
||||
// Each line is one path, project-relative, already POSIX on all platforms
|
||||
// because git emits forward slashes regardless of OS.
|
||||
// Each NUL-separated chunk is one path, project-relative, already POSIX on
|
||||
// all platforms because git emits forward slashes regardless of OS.
|
||||
return result.stdout
|
||||
.split('\n')
|
||||
.map(s => s.trim())
|
||||
.split('\0')
|
||||
.filter(Boolean)
|
||||
.map(toPosix);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user