dsh-code-index 0.4.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/LICENSE +0 -0
- package/README.md +23 -2
- package/README.zh.md +24 -3
- package/cordis.patch.yml +0 -0
- package/dist/client.js +287 -0
- package/dist/client.js.map +1 -0
- package/dist/index.js +2262 -157
- package/dist/index.js.map +1 -1
- package/package.json +25 -6
- package/scripts/fix-wsl-links.mjs +0 -0
- package/scripts/release-smoke.mjs +111 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to dsh-code-index are documented here.
|
|
4
|
+
|
|
5
|
+
## 0.6.1 — 2026-09-18
|
|
6
|
+
|
|
7
|
+
- **fix(change-context):** map pure renames to both current and baseline symbols instead of returning only a warning.
|
|
8
|
+
- **fix(git):** include non-ignored untracked source files in working-tree change context while retaining bounded synthetic hunks.
|
|
9
|
+
- **chore(compat):** keep the conservative `@deepseek-ai/dsh-tools` peer range and verify the rc.8 registration surface with a packaged smoke test.
|
|
10
|
+
- **ci:** verify Node 22 and Node 24, including a clean install and plugin/tool boot from the packed tarball.
|
|
11
|
+
- **docs:** document the release smoke command and the measured-vs-unmeasured benchmark policy.
|
|
12
|
+
|
|
13
|
+
## 0.6.0 — 2026-09-18
|
|
14
|
+
|
|
15
|
+
- **feat(change-context):** add `code_change_context` for changed symbols, direct callers, import dependents, bounded transitive impact, entry paths, and likely affected tests.
|
|
16
|
+
- **feat(ids):** add stable file-qualified symbol IDs with lexical scope and duplicate-name ordinals.
|
|
17
|
+
- **feat(refs):** label graph provenance as `exact`, `import-scoped`, or `name-only` so weak matches are explicit.
|
|
18
|
+
- **feat(git):** map working-tree, staged, added, deleted, renamed, and explicit unified diffs onto indexed symbols with baseline reads for deletions.
|
|
19
|
+
- **bench:** add reproducible stock vs 0.5 vs local 0.6 benchmark infrastructure with honest null token accounting and completion-rate gates.
|
|
20
|
+
|
|
21
|
+
## 0.5.0 — 2026-09-13
|
|
22
|
+
|
|
23
|
+
- **feat(refs):** new `code_refs` tool — a function-level call graph built from the cache. For any symbol it returns in-repo definitions, callers (every call site invoking it, with the enclosing function) and callees (what the definition itself calls, resolved to `file:line`). Name-based resolution, so like-named symbols surface as candidate definitions rather than being guessed apart.
|
|
24
|
+
- **feat(extract):** call-site extraction across all eight languages (TS/JS calls + `new`, Python `call`/`attribute`, Go `call_expression`/`selector_expression`, Rust calls + `field_expression`/`scoped_identifier`, Java `method_invocation`/`object_creation_expression`, C/C++ calls + `field_expression`/`qualified_identifier`). Each call records its enclosing function (`''` at module scope), which is what makes callers/callees precise without a second parse.
|
|
25
|
+
- **feat(health):** optional `code_health` tool (off by default; enable with `codeHealth: true`) — Tarjan SCC detection over the import graph reports circular dependencies, and orphan-module detection flags symbol-bearing files that neither import nor are imported, excluding entry points and tests.
|
|
26
|
+
- **feat(search):** call fan-in is now a tie-break in `code_search` ranking — when relevance and export status are equal, a symbol called from more places ranks first.
|
|
27
|
+
- **feat(ui):** `code_search` and `code_symbols` emit a search-shaped `presentationMeta` and a `search` result card, so capable clients render matches as grouped-by-file results instead of raw text.
|
|
28
|
+
- **feat(client):** browser half — per-tool cards for the `code_*` tools (structured search results with clickable file:line) and a `code-index` settings card in the Plugins settings page (auto-inject / code_health toggles plus repo-map size), backed by a Host settings namespace registered when a settings provider is present.
|
|
29
|
+
- **feat(store):** caches written before call-graph support are treated as stale (calls are compared during the equality check) instead of silently shadowing the richer fresh index.
|
|
30
|
+
- **test:** call-extraction coverage across all eight languages, call-graph resolution, cycle/orphan detection, and end-to-end `code_refs`/`code_health` execution.
|
|
31
|
+
|
|
32
|
+
## 0.4.0 — 2026-09-08
|
|
33
|
+
|
|
34
|
+
- **feat(extract):** C and C++ support — 11 new extensions (`.cpp .cc .cxx .c++ .hpp .hxx .hh .h .ipp .tpp .inl .c`) parse through `tree-sitter-cpp`/`tree-sitter-c` with zero new dependencies (both grammars ship in the existing `tree-sitter-wasms` pack). Symbol extraction resolves names through the declarator chain: free functions, in-class methods, out-of-class definitions (`Scene::queryRegion` → bare `queryRegion`), constructors, templates, namespaces (→ module kind), structs/unions, enums and typedefs. `public:`/`private:` section tracking (structs default public, classes private) plus `static` internal-linkage detection drive the exported flag.
|
|
35
|
+
- **feat(map):** `#include "…"` specifiers feed the import graph — quoted includes resolve in-repo (sibling-relative and project-root styles), `<system>` headers are dropped, and bare-path candidates match extension-carrying targets (`net/socket.hpp`).
|
|
36
|
+
- **feat(extract):** signatures hard-capped at 80 chars so multi-line C++ declarations (mock frameworks, template specials) cannot blow up repo-map rows or search hits.
|
|
37
|
+
- **fix(map):** symbol dedupe on `kind|name|line` guards double captures (e.g. a typedef aliasing a struct definition).
|
|
38
|
+
|
|
39
|
+
## 0.3.1 — 2026-09-04
|
|
40
|
+
|
|
41
|
+
- **feat(extract):** adapt to web-tree-sitter ≥ 0.25 — ESM named exports (`Language`, `Query`, `Node`) replace the 0.20.x CJS default export, the deprecated `lang.query()` gives way to the `Query` constructor, a null `parse()` result now raises instead of failing downstream, and node-child access hardens with optional chaining.
|
|
42
|
+
- **fix(pkg):** declare `@deepseek-ai/dsh-tools` as an optional peer dependency instead of a bundled runtime dependency (it moves to devDependencies for local typechecking), and ship the `scripts/` directory in the published package.
|
|
43
|
+
- **docs:** refresh the positioning copy in both READMEs and update the web-tree-sitter version note to `^0.25`.
|
|
44
|
+
|
|
45
|
+
## 0.3.0 — 2026-09-03
|
|
46
|
+
|
|
47
|
+
- **feat(map):** personalized PageRank over the import graph replaces flat in-degree counting — rank flows A→B when A imports B, so a hub that other hubs themselves import now outranks a merely popular leaf (the transitive signal +0.5-per-importer could not see). The teleport vector is each file's density share, keeping graph-less repos at their exact 0.2.x ordering; deterministic power iteration (d=0.85).
|
|
48
|
+
- **docs:** add a Feedback section to both READMEs — usage reports and ranking misbehaves now have an explicit entry point.
|
|
49
|
+
|
|
50
|
+
## 0.2.0 — 2026-08-28
|
|
51
|
+
|
|
52
|
+
- **feat(extract):** Go, Rust and Java support — functions/methods/types map onto the existing kind model (Go structs → class, interfaces → interface, uppercase = exported; Rust impl fns → method, `pub` = exported; Java records/interface members handled), and their imports feed reference ranking (`import` paths, `use` declarations, Java package imports).
|
|
53
|
+
- **feat(map):** reference-aware ranking — imports are extracted per file (ES imports/re-exports, Python imports) and each in-repo import adds 0.5 to the target's map score, so heavily-imported core files outrank symbol-dense-but-peripheral ones. Resolution handles extensions, index files, `__init__.py`, and Go/Java-style rooted paths via suffix fallback.
|
|
54
|
+
- **feat(search):** subsequence fuzzy matching (score 0.3, 3+ char queries) — 'cfgldr' now finds `configLoader` below exact/prefix/substring hits.
|
|
55
|
+
- **feat(extract):** correct Python semantics — class-body `def`s are now `method` (was `function`), and top-level defs/classes are `exported: true` since Python module-level symbols are importable; `exportedOnly` filtering now works for Python.
|
|
56
|
+
- **feat(config):** new `mapTtlMs` option for the auto-injected map's refresh interval (default 60 000 ms, floor 1 000 ms).
|
|
57
|
+
- **feat(map):** downweight test-looking paths in repo-map ranking (0.2× multiplier for `tests/`, `__tests__/`, `*.spec.*`, `*.test.*`, `test_*.py`, `*_test.py/go`) — symbol density actively favoured test files, so maps led with them.
|
|
58
|
+
- **fix(extract):** collapse multi-line parameter lists in signatures to one line; they wrapped the repo-map format and burned its char budget.
|
|
59
|
+
- **fix(extract):** index only module-level variables. The `variable_declarator` query matches at any depth, so function-body locals leaked in as noise — on one mid-size repo, 36% of all indexed symbols were locals.
|
|
60
|
+
- **fix(extract):** `exported` no longer leaks through class bodies: methods inside an `export class` (including `private` ones) were marked `exported: true` because the ancestor walk reached the enclosing `export_statement`. Only module-level declarations count now.
|
|
61
|
+
- **fix(store):** self-heal empty `symbol.file` fields from legacy caches on load, so the "file is always set" invariant holds at the persistence boundary.
|
|
62
|
+
- **feat:** index Python sources (functions + classes) alongside TS/JS.
|
|
63
|
+
- **chore(pkg):** add Node engines, repository/homepage metadata, npm + CI badges.
|
|
64
|
+
- **ci:** GitHub Actions workflow (typecheck + test + build) on push/PR.
|
|
65
|
+
- **test:** end-to-end tool-execute coverage (fixture repo → session cwd → query) and cache self-heal tests.
|
|
66
|
+
|
|
67
|
+
## 0.1.0 — 2026-08-21
|
|
68
|
+
|
|
69
|
+
- Initial release.
|
|
70
|
+
- Tools: `code_index`, `code_symbols`, `code_search`, `code_map`.
|
|
71
|
+
- Tree-sitter symbol index for TypeScript + JavaScript (WASM, no native build).
|
|
72
|
+
- Incremental mtime cache under `<repo>/.dsh-code-index/`.
|
|
73
|
+
- Optional bounded system-prompt repo-map injection (60s TTL, `autoInject` toggle).
|
|
74
|
+
- Config: `excludeDirs`, `mapTopFiles`, `mapMaxChars`.
|
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
English | [中文](README.zh.md)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Structural context engine for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`): a local, no-service, no-API-key tree-sitter index with ranked symbol search, repo map, call graph, and change-aware context.
|
|
9
9
|
|
|
10
10
|
Fits a niche the ecosystem took a while to fill: alongside git/voice/browser/memory plugins, several code-intelligence plugins have appeared (graph-based, embedding-based), while this one stays deliberately **dependency-free** — pure in-process tree-sitter over WASM, the aider repo-map / Cursor `@Codebase` style for dsh agents.
|
|
11
11
|
|
|
@@ -17,6 +17,9 @@ Fits a niche the ecosystem took a while to fill: alongside git/voice/browser/mem
|
|
|
17
17
|
| `code_symbols` | List symbols (functions, classes, interfaces, types, methods…) with file:line — filtered by name, path, kind, exported |
|
|
18
18
|
| `code_search` | Ranked lookup: exact > prefix > substring > subsequence-fuzzy, exports first, relevance score + file:line |
|
|
19
19
|
| `code_map` | Bounded ranked repo map (top files by symbol density + import-graph PageRank, key symbols + lines) |
|
|
20
|
+
| `code_refs` | Trace a symbol through the call graph: callers (who calls it) and callees (what it calls), resolved to file:line |
|
|
21
|
+
| `code_change_context` | Start from the working-tree or an explicit diff and return changed symbols, callers, import dependents, bounded impact paths, and likely tests |
|
|
22
|
+
| `code_health` | Opt-in (`codeHealth: true`): circular dependencies (import cycles) and orphan modules |
|
|
20
23
|
|
|
21
24
|
Plus an optional **auto-injected system prompt section** (`code-index:repo-map`, order 60): a compact ranked map of the default workspace, refreshed on a TTL (`mapTtlMs`, default 60s). Set `autoInject: false` to disable and rely on the `code_map` tool only.
|
|
22
25
|
|
|
@@ -50,6 +53,7 @@ In a workspace session, ask the agent:
|
|
|
50
53
|
- "Find every function whose name contains `parse` and where it lives."
|
|
51
54
|
- "List the exported symbols in src/core."
|
|
52
55
|
- "Rebuild the code index."
|
|
56
|
+
- "What changed in the working tree, who calls it, and which tests are likely affected?"
|
|
53
57
|
|
|
54
58
|
No API key is needed to *index*; the model must of course be configured to call the tools.
|
|
55
59
|
|
|
@@ -75,6 +79,16 @@ export function extractSymbols(code, id) — src/extract.ts:121
|
|
|
75
79
|
|
|
76
80
|
The index builds lazily on first use; later calls are served from the on-disk cache with mtime-incremental refresh.
|
|
77
81
|
|
|
82
|
+
### Call graph
|
|
83
|
+
|
|
84
|
+
`code_refs` traces a symbol through the call graph — the run below is on this repo itself (`getIndex` is defined at `src/tools.ts:105`, called from 7 sites, and its callee resolves to `src/tools.ts:74`):
|
|
85
|
+
|
|
86
|
+

|
|
87
|
+
|
|
88
|
+
### Change-aware context
|
|
89
|
+
|
|
90
|
+
`code_change_context` defaults to the current Git working tree against `HEAD`. It also accepts an inline unified diff, repo-relative `files`, or stable `symbols` IDs. Results are bounded and each inferred relationship carries a provenance label: `exact`, `import-scoped`, or `name-only`. Deletions and renames use the baseline ref when available; untracked non-ignored files are included in working-tree mode.
|
|
91
|
+
|
|
78
92
|
## Configuration
|
|
79
93
|
|
|
80
94
|
Options are passed as the plugin row's `config` in the profile patch (or defaults are used if absent):
|
|
@@ -96,6 +110,7 @@ Options are passed as the plugin row's `config` in the profile patch (or default
|
|
|
96
110
|
| `mapMaxChars` | `3200` | Hard cap on rendered map characters |
|
|
97
111
|
| `mapTtlMs` | `60000` | Refresh interval for the auto-injected map (ms, min 1000) |
|
|
98
112
|
| `autoInject` | `true` | Register the system prompt section |
|
|
113
|
+
| `codeHealth` | `false` | Register the `code_health` tool (cycles / orphan modules) |
|
|
99
114
|
|
|
100
115
|
## Supported languages
|
|
101
116
|
|
|
@@ -106,6 +121,9 @@ TypeScript, JavaScript, Python, Go, Rust, Java, C++ and C (`.ts .tsx .mts .cts .
|
|
|
106
121
|
- **Index build** (`src/buildIndex.ts`): recursive scan (excludes applied), per-file tree-sitter extraction (`src/extract.ts`), JSON cache under `<repo>/.dsh-code-index/`, incremental refresh by mtime (only touched files re-parse).
|
|
107
122
|
- **Search** (`src/search.ts`): pure scoring — exact `1` / prefix `0.8` / substring `0.5`, export boost, name order tiebreak.
|
|
108
123
|
- **Repo map** (`src/repomap.ts`): personalized PageRank over the import graph (teleport = per-file density share, so hub files that are themselves imported by other hubs rise above flat in-degree counting), seeded by the density-aware file score (class/interface/function weighted, test paths damped), top-N files, per-file symbol cap, hard char truncation.
|
|
124
|
+
- **Call graph** (`src/refgraph.ts`): call sites extracted per file (per language, with their enclosing function) are resolved by name into callers and callees — `code_refs` exposes this directly, and `code_search` uses call fan-in as a ranking tie-break.
|
|
125
|
+
- **Change context** (`src/change-context.ts`): maps Git hunks to stable symbols, then follows bounded provenance-labeled callers, import dependents, entry paths, impact, and likely affected tests without returning the whole repository.
|
|
126
|
+
- **Health** (`src/health.ts`): Tarjan SCC over the import graph yields circular dependencies; orphan-module detection lists symbol-bearing files with no inbound or outbound imports (entry points and tests excluded).
|
|
109
127
|
- **Workspace resolution**: each tool resolves the session cwd (`agent.session.header.cwd`) and walks up to the nearest `.git` (bounded — a directory without a repo marker is never indexed).
|
|
110
128
|
|
|
111
129
|
## Known limitations
|
|
@@ -119,11 +137,14 @@ TypeScript, JavaScript, Python, Go, Rust, Java, C++ and C (`.ts .tsx .mts .cts .
|
|
|
119
137
|
|
|
120
138
|
```sh
|
|
121
139
|
pnpm install
|
|
122
|
-
pnpm test # vitest — extractor, scan, cache, search, repo map
|
|
140
|
+
pnpm test # vitest — extractor, scan, cache, search, repo map, call graph, health
|
|
123
141
|
pnpm typecheck
|
|
124
142
|
pnpm build # tsup → dist/index.js (ESM, external deps)
|
|
143
|
+
pnpm build && pnpm release:smoke # pack, clean-install the tarball, boot the plugin, and exercise core tools
|
|
125
144
|
```
|
|
126
145
|
|
|
146
|
+
The benchmark harness under `bench/` compares stock DSH, the published 0.5 baseline, and the local 0.6 treatment. It records task completion, input tokens, tool calls, turns, and wall time; it does not invent missing provider usage. The repository contains infrastructure and sample tasks, not measured performance claims.
|
|
147
|
+
|
|
127
148
|
**WSL → Windows checkouts:** running `pnpm install` from WSL against a checkout on `/mnt/c` leaves Linux-style symlinks that Windows Node cannot traverse (`Cannot find package 'web-tree-sitter'`, `EACCES`). Repair without a reinstall from the Windows side:
|
|
128
149
|
|
|
129
150
|
```sh
|
package/README.zh.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`) 的结构化上下文引擎:本地运行、无需外部服务和 API key,基于 tree-sitter 提供符号索引、排名搜索、仓库地图、调用图和变更感知上下文。
|
|
6
6
|
|
|
7
7
|
在一个生态姗姗来迟的细分领域占位:git/语音/浏览器/记忆类插件之外,代码智能方向的插件已陆续出现(图谱路线、向量嵌入路线),而本插件刻意保持**零外部依赖**——纯进程内 tree-sitter WASM,把 aider repo-map / Cursor `@Codebase` 的同类能力带给 dsh agent。
|
|
8
8
|
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
| `code_symbols` | 列出符号(函数、类、接口、类型、方法……),带 file:line——支持按名称、路径、类型、是否导出过滤 |
|
|
15
15
|
| `code_search` | 排名检索:精确 > 前缀 > 子串 > 子序列模糊,导出优先,带相关度分数与 file:line |
|
|
16
16
|
| `code_map` | 限量排名仓库地图(按符号密度 + import 图 PageRank 取核心文件 + 关键符号与行号) |
|
|
17
|
+
| `code_refs` | 沿调用图追踪符号:callers(谁调用了它)与 callees(它调用了谁),解析到 file:line |
|
|
18
|
+
| `code_change_context` | 从工作区或显式 diff 出发,返回变更符号、调用者、import 依赖、有限影响路径和可能受影响的测试 |
|
|
19
|
+
| `code_health` | 可选开启(`codeHealth: true`):环依赖(import 环)与孤儿模块 |
|
|
17
20
|
|
|
18
21
|
外加一个可选的**自动注入系统提示词段**(`code-index:repo-map`,序 60):默认工作区的精简排名地图,按 TTL 自动刷新(`mapTtlMs`,默认 60 秒)。将 `autoInject: false` 可关闭,只依赖 `code_map` 工具。
|
|
19
22
|
|
|
@@ -47,6 +50,7 @@ npx @deepseek-ai/dsh plugin --profile web add ./dsh-code-index
|
|
|
47
50
|
- "找出所有名字含 `parse` 的函数及其位置。"
|
|
48
51
|
- "列出 src/core 里的导出符号。"
|
|
49
52
|
- "重建代码索引。"
|
|
53
|
+
- "工作区改了什么、谁调用了它、哪些测试可能受影响?"
|
|
50
54
|
|
|
51
55
|
*索引*本身不需要 API key;模型当然要配置好才能调用这些工具。
|
|
52
56
|
|
|
@@ -72,6 +76,16 @@ export function extractSymbols(code, id) — src/extract.ts:121
|
|
|
72
76
|
|
|
73
77
|
索引在首次使用时惰性构建;后续调用由磁盘缓存提供,并按 mtime 增量刷新。
|
|
74
78
|
|
|
79
|
+
### 调用图
|
|
80
|
+
|
|
81
|
+
`code_refs` 沿调用图追踪符号——下例直接跑在本仓库自身(`getIndex` 定义于 `src/tools.ts:105`,有 7 个调用点,其 callee 解析到 `src/tools.ts:74`):
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
### 变更感知上下文
|
|
86
|
+
|
|
87
|
+
`code_change_context` 默认分析相对于 `HEAD` 的当前 Git 工作区,也支持内联 unified diff、仓库相对 `files` 或稳定的 `symbols` ID。结果有数量和字符预算,每条推导关系都会标注 `exact`、`import-scoped` 或 `name-only` 来源。删除和重命名在可用时读取 baseline;工作区模式也会包含未被忽略的未跟踪源码文件。
|
|
88
|
+
|
|
75
89
|
## 配置
|
|
76
90
|
|
|
77
91
|
选项通过插件行的 `config` 在 profile 补丁中传入(缺省时使用默认值):
|
|
@@ -93,6 +107,7 @@ export function extractSymbols(code, id) — src/extract.ts:121
|
|
|
93
107
|
| `mapMaxChars` | `3200` | 渲染地图的硬性字符上限 |
|
|
94
108
|
| `mapTtlMs` | `60000` | 自动注入地图的刷新间隔(毫秒,最小 1000) |
|
|
95
109
|
| `autoInject` | `true` | 是否注册系统提示词段 |
|
|
110
|
+
| `codeHealth` | `false` | 是否注册 `code_health` 工具(环/孤儿模块) |
|
|
96
111
|
|
|
97
112
|
## 支持的语言
|
|
98
113
|
|
|
@@ -103,6 +118,9 @@ TypeScript、JavaScript、Python、Go、Rust、Java、C++、C(`.ts .tsx .mts .ct
|
|
|
103
118
|
- **索引构建**(`src/buildIndex.ts`):递归扫描(应用排除规则),逐文件 tree-sitter 提取(`src/extract.ts`),JSON 缓存置于 `<repo>/.dsh-code-index/`,按 mtime 增量刷新(只有被改动的文件才重新解析)。
|
|
104
119
|
- **搜索**(`src/search.ts`):纯打分——精确 `1` / 前缀 `0.8` / 子串 `0.5`,导出加权,名称序平局裁决。
|
|
105
120
|
- **仓库地图**(`src/repomap.ts`):import 图上的个性化 PageRank(传送向量 = 各文件密度份额,被其他枢纽文件引用的枢纽会比平铺入度统计排得更靠前),以密度感知的文件打分为底(class/interface/function 加权,测试路径衰减),取 Top-N 文件,每文件符号上限,硬截断。
|
|
121
|
+
- **调用图**(`src/refgraph.ts`):逐文件提取调用点(按语言,并记录其所属函数),按名称解析成 callers 与 callees——`code_refs` 直接暴露,`code_search` 也把调用热度作为排名平局裁决。
|
|
122
|
+
- **变更上下文**(`src/change-context.ts`):把 Git hunk 映射到稳定符号,再在有限深度内追踪带来源标签的调用者、import 依赖、入口路径、影响范围和可能受影响的测试,避免返回整个仓库。
|
|
123
|
+
- **健康检查**(`src/health.ts`):对 import 图跑 Tarjan SCC 得到环依赖;孤儿模块检测列出既不被 import、也不 import 任何文件的含符号文件(排除入口与测试)。
|
|
106
124
|
- **工作区解析**:每个工具解析会话 cwd(`agent.session.header.cwd`)并向上查找最近的 `.git`(有界——没有仓库标记的目录绝不会被索引)。
|
|
107
125
|
|
|
108
126
|
## 已知限制
|
|
@@ -116,11 +134,14 @@ TypeScript、JavaScript、Python、Go、Rust、Java、C++、C(`.ts .tsx .mts .ct
|
|
|
116
134
|
|
|
117
135
|
```sh
|
|
118
136
|
pnpm install
|
|
119
|
-
pnpm test # vitest ——
|
|
137
|
+
pnpm test # vitest —— 提取器、扫描、缓存、搜索、仓库地图、调用图、健康检查
|
|
120
138
|
pnpm typecheck
|
|
121
139
|
pnpm build # tsup → dist/index.js(ESM,外部依赖)
|
|
140
|
+
pnpm build && pnpm release:smoke # 打包、干净安装 tarball、启动插件并调用核心工具
|
|
122
141
|
```
|
|
123
142
|
|
|
143
|
+
`bench/` 下的 benchmark 对比 stock DSH、已发布的 0.5 基线和本地 0.6 处理组,记录任务完成、输入 token、工具调用、轮次和墙钟时间; provider 未提供的 usage 会保留为 null,不会伪造性能数据。当前仓库提供基础设施和示例任务,不宣称已测得性能提升。
|
|
144
|
+
|
|
124
145
|
**WSL → Windows 检出**:从 WSL 对 `/mnt/c` 下的检出跑 `pnpm install`,会留下 Windows 侧 Node 无法穿透的 Linux 风格符号链接(`Cannot find package 'web-tree-sitter'`、`EACCES`)。无需重装,在 Windows 侧跑一次修复:
|
|
125
146
|
|
|
126
147
|
```sh
|
|
@@ -136,4 +157,4 @@ node.exe scripts\fix-wsl-links.mjs C:\Users\you\.dsh\profiles\web # dsh profil
|
|
|
136
157
|
|
|
137
158
|
## 许可证
|
|
138
159
|
|
|
139
|
-
MIT。与 DeepSeek 无关;基于公开的 `dsh` 插件接口构建。
|
|
160
|
+
MIT。与 DeepSeek 无关;基于公开的 `dsh` 插件接口构建。
|
package/cordis.patch.yml
CHANGED
|
File without changes
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({ id: "dsh-code-index", factory: (require) => {
|
|
2
|
+
var module = { exports: {} }; var exports = module.exports;
|
|
3
|
+
"use strict";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name2 in all)
|
|
10
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/client/index.tsx
|
|
23
|
+
var client_exports = {};
|
|
24
|
+
__export(client_exports, {
|
|
25
|
+
apply: () => apply,
|
|
26
|
+
inject: () => inject,
|
|
27
|
+
name: () => name
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(client_exports);
|
|
30
|
+
var import_react = require("react");
|
|
31
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
var CARD_STYLE = {
|
|
33
|
+
border: "1px solid var(--dsh-border, #333)",
|
|
34
|
+
borderRadius: 6,
|
|
35
|
+
padding: "6px 8px",
|
|
36
|
+
fontFamily: "var(--dsh-mono-font, monospace)",
|
|
37
|
+
fontSize: 12,
|
|
38
|
+
margin: "4px 0"
|
|
39
|
+
};
|
|
40
|
+
var ROW_STYLE = {
|
|
41
|
+
display: "flex",
|
|
42
|
+
gap: 8,
|
|
43
|
+
alignItems: "baseline"
|
|
44
|
+
};
|
|
45
|
+
function parseArgs(raw) {
|
|
46
|
+
if (!raw) return {};
|
|
47
|
+
try {
|
|
48
|
+
const value = JSON.parse(raw);
|
|
49
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
50
|
+
} catch {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function summarizeArgs(args) {
|
|
55
|
+
const parts = [];
|
|
56
|
+
for (const key of ["query", "symbol", "action", "direction", "kind"]) {
|
|
57
|
+
const value = args[key];
|
|
58
|
+
if (typeof value === "string" && value) parts.push(`${key}=${value}`);
|
|
59
|
+
}
|
|
60
|
+
return parts.join(" ");
|
|
61
|
+
}
|
|
62
|
+
function asSearchView(view) {
|
|
63
|
+
if (typeof view !== "object" || view === null || Array.isArray(view)) return void 0;
|
|
64
|
+
const record = view;
|
|
65
|
+
return record.card === "search" ? record : void 0;
|
|
66
|
+
}
|
|
67
|
+
function resultText(block) {
|
|
68
|
+
return (block.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join("\n");
|
|
69
|
+
}
|
|
70
|
+
function CodeToolCard(props) {
|
|
71
|
+
const { toolName, block, openFile } = props;
|
|
72
|
+
const head = block.call ?? block;
|
|
73
|
+
const summary = summarizeArgs(parseArgs(head.argsRaw));
|
|
74
|
+
const search = asSearchView(block.resultView);
|
|
75
|
+
const isError = block.isError === true;
|
|
76
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: CARD_STYLE, children: [
|
|
77
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: ROW_STYLE, children: [
|
|
78
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: toolName }),
|
|
79
|
+
summary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.75 }, children: summary }) : null,
|
|
80
|
+
isError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#e5534b" }, children: "error" }) : null
|
|
81
|
+
] }),
|
|
82
|
+
search?.shape === "matches" && search.files ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 4 }, children: [
|
|
83
|
+
search.files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
84
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { opacity: 0.85 }, children: file.path }),
|
|
85
|
+
(file.matches ?? []).map((match, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
style: { paddingLeft: 12, cursor: openFile ? "pointer" : "default" },
|
|
89
|
+
onClick: () => openFile?.(file.path, { line: match.lineNumber }),
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.6 }, children: match.lineNumber }),
|
|
92
|
+
" ",
|
|
93
|
+
match.line
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
`${match.lineNumber}-${index}`
|
|
97
|
+
))
|
|
98
|
+
] }, file.path)),
|
|
99
|
+
search.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { opacity: 0.6 }, children: [
|
|
100
|
+
"\u2026 truncated (",
|
|
101
|
+
search.total,
|
|
102
|
+
" total)"
|
|
103
|
+
] }) : null
|
|
104
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { style: { margin: "4px 0 0", whiteSpace: "pre-wrap", maxHeight: 220, overflow: "auto" }, children: resultText(block) })
|
|
105
|
+
] });
|
|
106
|
+
}
|
|
107
|
+
var activeScope = null;
|
|
108
|
+
var CARD_CSS = `
|
|
109
|
+
.dci-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
|
|
110
|
+
.dci-card:hover{border-color:var(--dsw-alias-label-dimmed)}
|
|
111
|
+
.dci-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
112
|
+
.dci-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
|
|
113
|
+
.dci-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
114
|
+
.dci-headText{flex-direction:column;flex:1 1 0%;gap:4px;min-width:0;display:flex}
|
|
115
|
+
.dci-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
|
|
116
|
+
.dci-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
117
|
+
.dci-chevron{color:var(--dsw-alias-label-tertiary);flex:0 0 auto;transition:transform .16s}
|
|
118
|
+
.dci-chevronOpen{transform:rotate(180deg)}
|
|
119
|
+
.dci-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
|
|
120
|
+
.dci-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
121
|
+
.dci-field+.dci-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
122
|
+
.dci-head{align-items:center;gap:8px;display:flex}
|
|
123
|
+
.dci-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1 1 0%;font-size:13px;font-weight:500;line-height:1.5}
|
|
124
|
+
.dci-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font:inherit;font-size:13px;line-height:1.5}
|
|
125
|
+
.dci-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
126
|
+
.dci-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
127
|
+
.dci-check{width:16px;height:16px;flex:0 0 auto;accent-color:var(--dsw-alias-brand-primary)}
|
|
128
|
+
.dci-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
129
|
+
.dci-footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}
|
|
130
|
+
.dci-discard,.dci-save{appearance:none;font:inherit;cursor:pointer;border:1px solid transparent;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}
|
|
131
|
+
.dci-discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}
|
|
132
|
+
.dci-discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
|
|
133
|
+
.dci-save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
134
|
+
.dci-discard:disabled,.dci-save:disabled{opacity:.4;cursor:default}
|
|
135
|
+
`;
|
|
136
|
+
var CHEVRON_PATH = "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z";
|
|
137
|
+
var SETTINGS_DEFAULTS = { autoInject: true, codeHealth: false, mapTopFiles: 24 };
|
|
138
|
+
var SETTINGS_FIELDS = ["autoInject", "codeHealth", "mapTopFiles"];
|
|
139
|
+
function CodeIndexSettingsCard() {
|
|
140
|
+
const scope = activeScope;
|
|
141
|
+
const [open, setOpen] = (0, import_react.useState)(false);
|
|
142
|
+
const [snapshot, setSnapshot] = (0, import_react.useState)(
|
|
143
|
+
scope ? scope.getSnapshot() : null
|
|
144
|
+
);
|
|
145
|
+
const [draft, setDraft] = (0, import_react.useState)(null);
|
|
146
|
+
(0, import_react.useEffect)(() => {
|
|
147
|
+
if (!scope) return;
|
|
148
|
+
setSnapshot(scope.getSnapshot());
|
|
149
|
+
return scope.subscribe(() => setSnapshot(scope.getSnapshot()));
|
|
150
|
+
}, [scope]);
|
|
151
|
+
const resolved = snapshot?.value ?? {};
|
|
152
|
+
const form = { ...SETTINGS_DEFAULTS, ...resolved, ...draft };
|
|
153
|
+
const writable = snapshot?.writable ?? false;
|
|
154
|
+
const changed = (key) => form[key] !== (resolved[key] ?? SETTINGS_DEFAULTS[key]);
|
|
155
|
+
const dirty = draft !== null && SETTINGS_FIELDS.some(changed);
|
|
156
|
+
const edit = (key, value) => {
|
|
157
|
+
setDraft({ ...form, [key]: value });
|
|
158
|
+
};
|
|
159
|
+
const save = () => {
|
|
160
|
+
if (!scope) return;
|
|
161
|
+
for (const key of SETTINGS_FIELDS) {
|
|
162
|
+
if (changed(key)) {
|
|
163
|
+
scope.set(key, form[key]).catch((error) => {
|
|
164
|
+
console.error("[dsh-code-index] settings save failed", error);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
setDraft(null);
|
|
169
|
+
};
|
|
170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: open ? "dci-card dci-cardOpen" : "dci-card", children: [
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CARD_CSS }),
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
173
|
+
"button",
|
|
174
|
+
{
|
|
175
|
+
type: "button",
|
|
176
|
+
className: "dci-header",
|
|
177
|
+
"aria-expanded": open,
|
|
178
|
+
"aria-label": `${open ? "\u6536\u8D77\u8BBE\u7F6E" : "\u5C55\u5F00\u8BBE\u7F6E"}: dsh-code-index`,
|
|
179
|
+
onClick: () => {
|
|
180
|
+
setOpen(!open);
|
|
181
|
+
},
|
|
182
|
+
children: [
|
|
183
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dci-headText", children: [
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dci-name", children: "dsh-code-index" }),
|
|
185
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dci-description", children: "\u7ED3\u6784\u5316\u4ED3\u5E93\u7D22\u5F15\uFF1A\u7B26\u53F7\u641C\u7D22\u3001\u4ED3\u5E93\u5730\u56FE\u4E0E\u8C03\u7528\u56FE\u3002" })
|
|
186
|
+
] }),
|
|
187
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: open ? "dci-chevron dci-chevronOpen" : "dci-chevron", width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" }) })
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
),
|
|
191
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-body", children: [
|
|
192
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
|
|
193
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-head", children: [
|
|
194
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-auto-inject", children: "\u81EA\u52A8\u6CE8\u5165\u4ED3\u5E93\u5730\u56FE" }),
|
|
195
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
196
|
+
"input",
|
|
197
|
+
{
|
|
198
|
+
id: "dci-auto-inject",
|
|
199
|
+
className: "dci-check",
|
|
200
|
+
type: "checkbox",
|
|
201
|
+
checked: form.autoInject,
|
|
202
|
+
disabled: !writable,
|
|
203
|
+
onChange: (event) => {
|
|
204
|
+
edit("autoInject", event.target.checked);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] }),
|
|
209
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: "\u628A\u7CBE\u7B80\u7684\u6392\u540D\u4ED3\u5E93\u5730\u56FE\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3002" })
|
|
210
|
+
] }),
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
|
|
212
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-head", children: [
|
|
213
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-code-health", children: "\u4EE3\u7801\u5065\u5EB7\u5DE5\u5177" }),
|
|
214
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
215
|
+
"input",
|
|
216
|
+
{
|
|
217
|
+
id: "dci-code-health",
|
|
218
|
+
className: "dci-check",
|
|
219
|
+
type: "checkbox",
|
|
220
|
+
checked: form.codeHealth,
|
|
221
|
+
disabled: !writable,
|
|
222
|
+
onChange: (event) => {
|
|
223
|
+
edit("codeHealth", event.target.checked);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
] }),
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: "\u6CE8\u518C code_health \u5DE5\u5177\uFF0C\u62A5\u544A\u73AF\u4F9D\u8D56\u4E0E\u5B64\u513F\u6A21\u5757\uFF08\u91CD\u542F\u540E\u751F\u6548\uFF09\u3002" })
|
|
229
|
+
] }),
|
|
230
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dci-head", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-map-files", children: "\u5730\u56FE\u6587\u4EF6\u6570" }) }),
|
|
232
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
233
|
+
"input",
|
|
234
|
+
{
|
|
235
|
+
id: "dci-map-files",
|
|
236
|
+
className: "dci-input",
|
|
237
|
+
type: "text",
|
|
238
|
+
inputMode: "numeric",
|
|
239
|
+
value: String(form.mapTopFiles),
|
|
240
|
+
disabled: !writable,
|
|
241
|
+
onChange: (event) => {
|
|
242
|
+
edit("mapTopFiles", Number(event.target.value));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
),
|
|
246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: "\u4ED3\u5E93\u5730\u56FE\u4E2D\u6700\u591A\u5C55\u793A\u7684\u6587\u4EF6\u6570\u3002" })
|
|
247
|
+
] }),
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-footer", children: [
|
|
249
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dci-discard", disabled: !dirty || !writable, onClick: () => {
|
|
250
|
+
setDraft(null);
|
|
251
|
+
}, children: "\u653E\u5F03\u4FEE\u6539" }),
|
|
252
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dci-save", disabled: !dirty || !writable, onClick: save, children: "\u4FDD\u5B58" })
|
|
253
|
+
] })
|
|
254
|
+
] }) : null,
|
|
255
|
+
scope ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", style: { padding: "0 16px 12px" }, children: "\u8BBE\u7F6E\u670D\u52A1\u4E0D\u53EF\u7528\u3002" })
|
|
256
|
+
] });
|
|
257
|
+
}
|
|
258
|
+
var name = "dsh-code-index";
|
|
259
|
+
var inject = ["slots"];
|
|
260
|
+
var TOOLVIEW_KEYS = [
|
|
261
|
+
"code_index",
|
|
262
|
+
"code_symbols",
|
|
263
|
+
"code_search",
|
|
264
|
+
"code_map",
|
|
265
|
+
"code_refs",
|
|
266
|
+
"code_change_context",
|
|
267
|
+
"code_health"
|
|
268
|
+
];
|
|
269
|
+
function apply(ctx) {
|
|
270
|
+
const card = CodeToolCard;
|
|
271
|
+
ctx.slots.inject("tool.call.toolview", function* () {
|
|
272
|
+
for (const key of TOOLVIEW_KEYS) {
|
|
273
|
+
yield ctx.slots.register({ name: "tool.call.toolview", key }, card);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
ctx.inject(["settingsScope"], (scoped) => {
|
|
277
|
+
activeScope = scoped.settingsScope?.bind({ namespace: "code-index" }) ?? null;
|
|
278
|
+
ctx.slots.inject("settings.plugin.item", function* () {
|
|
279
|
+
yield ctx.slots.register(
|
|
280
|
+
{ name: "settings.plugin.item", key: "code-index" },
|
|
281
|
+
CodeIndexSettingsCard
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return module.exports; } });
|
|
287
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/index.tsx"],"sourcesContent":["/**\n * Browser half of dsh-code-index: structured result cards for the code_* tools\n * plus a settings card for the `code-index` namespace.\n *\n * Deliberately imports no harness client package: this bundle only needs\n * `react` (a shell platform module) plus the slot registry and settings scope\n * services reached through the plugin context. Keeping the surface local avoids\n * a dependency on a synchronized client-package version.\n */\n\nimport { useEffect, useState, type ReactNode } from 'react'\n\n/** Minimal structural view of the client plugin context this bundle uses. */\ninterface ClientContextLike {\n slots: {\n inject(name: string, run: () => Iterable<unknown>): void\n register(entry: Record<string, unknown>, component: (props: never) => ReactNode): () => void\n }\n inject(deps: string[], run: (ctx: ClientContextLike) => void): unknown\n settingsScope?: { bind(spec: { namespace: string }): SettingsScopeLike<CodeIndexSettings> }\n}\n\ninterface SettingsSnapshot<T> {\n status: 'loading' | 'ready' | 'unavailable'\n value: T | undefined\n writable: boolean\n}\n\ninterface SettingsScopeLike<T> {\n getSnapshot(): SettingsSnapshot<T>\n subscribe(listener: () => void): () => void\n set(field: string, value: unknown): Promise<void>\n unset(field: string): Promise<void>\n}\n\ninterface CodeIndexSettings {\n excludeDirs?: string[]\n mapTopFiles?: number\n mapMaxChars?: number\n mapTtlMs?: number\n autoInject?: boolean\n codeHealth?: boolean\n}\n\ninterface ToolCallHead {\n name?: string\n argsRaw?: string\n}\n\ninterface TextContent {\n type?: string\n text?: string\n}\n\ninterface ToolResultLike {\n call?: ToolCallHead | null\n content?: readonly TextContent[]\n isError?: boolean\n resultView?: unknown\n}\n\ninterface SearchView {\n card: 'search'\n shape?: 'matches' | 'paths'\n files?: Array<{ path: string; matches?: Array<{ lineNumber: number; line: string }> }>\n paths?: string[]\n truncated?: boolean\n total?: number\n}\n\ninterface ToolViewProps {\n toolName: string\n block: ToolResultLike & ToolCallHead\n openFile?: (path: string, options?: unknown) => void\n}\n\nconst CARD_STYLE: Record<string, string | number> = {\n border: '1px solid var(--dsh-border, #333)',\n borderRadius: 6,\n padding: '6px 8px',\n fontFamily: 'var(--dsh-mono-font, monospace)',\n fontSize: 12,\n margin: '4px 0',\n}\n\nconst ROW_STYLE: Record<string, string | number> = {\n display: 'flex',\n gap: 8,\n alignItems: 'baseline',\n}\n\nfunction parseArgs(raw: string | undefined): Record<string, unknown> {\n if (!raw) return {}\n try {\n const value: unknown = JSON.parse(raw)\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {}\n } catch {\n return {}\n }\n}\n\nfunction summarizeArgs(args: Record<string, unknown>): string {\n const parts: string[] = []\n for (const key of ['query', 'symbol', 'action', 'direction', 'kind']) {\n const value = args[key]\n if (typeof value === 'string' && value) parts.push(`${key}=${value}`)\n }\n return parts.join(' ')\n}\n\nfunction asSearchView(view: unknown): SearchView | undefined {\n if (typeof view !== 'object' || view === null || Array.isArray(view)) return undefined\n const record = view as Record<string, unknown>\n return record.card === 'search' ? (record as unknown as SearchView) : undefined\n}\n\nfunction resultText(block: ToolResultLike): string {\n return (block.content ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text ?? '')\n .join('\\n')\n}\n\n/** One code tool's card: argument summary, structured search matches, or text. */\nfunction CodeToolCard(props: ToolViewProps): ReactNode {\n const { toolName, block, openFile } = props\n const head: ToolCallHead = block.call ?? block\n const summary = summarizeArgs(parseArgs(head.argsRaw))\n const search = asSearchView(block.resultView)\n const isError = block.isError === true\n\n return (\n <div style={CARD_STYLE}>\n <div style={ROW_STYLE}>\n <strong>{toolName}</strong>\n {summary ? <span style={{ opacity: 0.75 }}>{summary}</span> : null}\n {isError ? <span style={{ color: '#e5534b' }}>error</span> : null}\n </div>\n {search?.shape === 'matches' && search.files ? (\n <div style={{ marginTop: 4 }}>\n {search.files.map((file) => (\n <div key={file.path}>\n <div style={{ opacity: 0.85 }}>{file.path}</div>\n {(file.matches ?? []).map((match, index) => (\n <div\n key={`${match.lineNumber}-${index}`}\n style={{ paddingLeft: 12, cursor: openFile ? 'pointer' : 'default' }}\n onClick={() => openFile?.(file.path, { line: match.lineNumber })}\n >\n <span style={{ opacity: 0.6 }}>{match.lineNumber}</span> {match.line}\n </div>\n ))}\n </div>\n ))}\n {search.truncated ? <div style={{ opacity: 0.6 }}>… truncated ({search.total} total)</div> : null}\n </div>\n ) : (\n <pre style={{ margin: '4px 0 0', whiteSpace: 'pre-wrap', maxHeight: 220, overflow: 'auto' }}>\n {resultText(block)}\n </pre>\n )}\n </div>\n )\n}\n\n/** The scope is captured at apply() time; the card reads it through React state. */\nlet activeScope: SettingsScopeLike<CodeIndexSettings> | null = null\n\n/** Mirrors the first-party plugin card + field CSS modules, under our own class names. */\nconst CARD_CSS = `\n.dci-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}\n.dci-card:hover{border-color:var(--dsw-alias-label-dimmed)}\n.dci-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}\n.dci-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}\n.dci-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}\n.dci-headText{flex-direction:column;flex:1 1 0%;gap:4px;min-width:0;display:flex}\n.dci-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}\n.dci-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}\n.dci-chevron{color:var(--dsw-alias-label-tertiary);flex:0 0 auto;transition:transform .16s}\n.dci-chevronOpen{transform:rotate(180deg)}\n.dci-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}\n.dci-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}\n.dci-field+.dci-field{border-top:1px solid var(--dsw-alias-border-l2)}\n.dci-head{align-items:center;gap:8px;display:flex}\n.dci-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1 1 0%;font-size:13px;font-weight:500;line-height:1.5}\n.dci-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font:inherit;font-size:13px;line-height:1.5}\n.dci-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}\n.dci-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}\n.dci-check{width:16px;height:16px;flex:0 0 auto;accent-color:var(--dsw-alias-brand-primary)}\n.dci-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.dci-footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}\n.dci-discard,.dci-save{appearance:none;font:inherit;cursor:pointer;border:1px solid transparent;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}\n.dci-discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}\n.dci-discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}\n.dci-save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}\n.dci-discard:disabled,.dci-save:disabled{opacity:.4;cursor:default}\n`\n\nconst CHEVRON_PATH =\n 'M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z'\n\nconst SETTINGS_DEFAULTS = { autoInject: true, codeHealth: false, mapTopFiles: 24 }\nconst SETTINGS_FIELDS = ['autoInject', 'codeHealth', 'mapTopFiles'] as const\n\n/** Settings card for `code-index`, laid out like the first-party plugin cards. */\nfunction CodeIndexSettingsCard(): ReactNode {\n const scope = activeScope\n const [open, setOpen] = useState(false)\n const [snapshot, setSnapshot] = useState<SettingsSnapshot<CodeIndexSettings> | null>(\n scope ? scope.getSnapshot() : null,\n )\n const [draft, setDraft] = useState<CodeIndexSettings | null>(null)\n\n useEffect(() => {\n if (!scope) return\n setSnapshot(scope.getSnapshot())\n return scope.subscribe(() => setSnapshot(scope.getSnapshot()))\n }, [scope])\n\n const resolved = snapshot?.value ?? {}\n const form: CodeIndexSettings = { ...SETTINGS_DEFAULTS, ...resolved, ...draft }\n const writable = snapshot?.writable ?? false\n const changed = (key: (typeof SETTINGS_FIELDS)[number]): boolean =>\n form[key] !== (resolved[key] ?? SETTINGS_DEFAULTS[key])\n const dirty = draft !== null && SETTINGS_FIELDS.some(changed)\n\n const edit = (key: (typeof SETTINGS_FIELDS)[number], value: string | number | boolean): void => {\n setDraft({ ...form, [key]: value })\n }\n const save = (): void => {\n if (!scope) return\n for (const key of SETTINGS_FIELDS) {\n if (changed(key)) {\n scope.set(key, form[key]).catch((error: unknown) => {\n console.error('[dsh-code-index] settings save failed', error)\n })\n }\n }\n setDraft(null)\n }\n\n return (\n <li className={open ? 'dci-card dci-cardOpen' : 'dci-card'}>\n <style>{CARD_CSS}</style>\n <button\n type=\"button\"\n className=\"dci-header\"\n aria-expanded={open}\n aria-label={`${open ? '收起设置' : '展开设置'}: dsh-code-index`}\n onClick={() => { setOpen(!open) }}\n >\n <span className=\"dci-headText\">\n <span className=\"dci-name\">dsh-code-index</span>\n <span className=\"dci-description\">结构化仓库索引:符号搜索、仓库地图与调用图。</span>\n </span>\n <svg className={open ? 'dci-chevron dci-chevronOpen' : 'dci-chevron'} width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d={CHEVRON_PATH} fill=\"currentColor\" />\n </svg>\n </button>\n {open ? (\n <div className=\"dci-body\">\n <div className=\"dci-field\">\n <div className=\"dci-head\">\n <label className=\"dci-label\" htmlFor=\"dci-auto-inject\">自动注入仓库地图</label>\n <input\n id=\"dci-auto-inject\"\n className=\"dci-check\"\n type=\"checkbox\"\n checked={form.autoInject}\n disabled={!writable}\n onChange={(event) => { edit('autoInject', event.target.checked) }}\n />\n </div>\n <p className=\"dci-hint\">把精简的排名仓库地图注入系统提示词。</p>\n </div>\n <div className=\"dci-field\">\n <div className=\"dci-head\">\n <label className=\"dci-label\" htmlFor=\"dci-code-health\">代码健康工具</label>\n <input\n id=\"dci-code-health\"\n className=\"dci-check\"\n type=\"checkbox\"\n checked={form.codeHealth}\n disabled={!writable}\n onChange={(event) => { edit('codeHealth', event.target.checked) }}\n />\n </div>\n <p className=\"dci-hint\">注册 code_health 工具,报告环依赖与孤儿模块(重启后生效)。</p>\n </div>\n <div className=\"dci-field\">\n <div className=\"dci-head\">\n <label className=\"dci-label\" htmlFor=\"dci-map-files\">地图文件数</label>\n </div>\n <input\n id=\"dci-map-files\"\n className=\"dci-input\"\n type=\"text\"\n inputMode=\"numeric\"\n value={String(form.mapTopFiles)}\n disabled={!writable}\n onChange={(event) => { edit('mapTopFiles', Number(event.target.value)) }}\n />\n <p className=\"dci-hint\">仓库地图中最多展示的文件数。</p>\n </div>\n <div className=\"dci-footer\">\n <button type=\"button\" className=\"dci-discard\" disabled={!dirty || !writable} onClick={() => { setDraft(null) }}>\n 放弃修改\n </button>\n <button type=\"button\" className=\"dci-save\" disabled={!dirty || !writable} onClick={save}>保存</button>\n </div>\n </div>\n ) : null}\n {scope ? null : <p className=\"dci-hint\" style={{ padding: '0 16px 12px' }}>设置服务不可用。</p>}\n </li>\n )\n}\n\nexport const name = 'dsh-code-index'\n\n/** `slots` is required for direct access; `settingsScope` binds lazily below. */\nexport const inject = ['slots']\n\nconst TOOLVIEW_KEYS = [\n 'code_index',\n 'code_symbols',\n 'code_search',\n 'code_map',\n 'code_refs',\n 'code_change_context',\n 'code_health',\n]\n\nexport function apply(ctx: ClientContextLike): void {\n const card = CodeToolCard as unknown as (props: never) => ReactNode\n ctx.slots.inject('tool.call.toolview', function* () {\n for (const key of TOOLVIEW_KEYS) {\n yield ctx.slots.register({ name: 'tool.call.toolview', key }, card)\n }\n })\n\n ctx.inject(['settingsScope'], (scoped: ClientContextLike) => {\n activeScope = scoped.settingsScope?.bind({ namespace: 'code-index' }) ?? null\n ctx.slots.inject('settings.plugin.item', function* () {\n yield ctx.slots.register(\n { name: 'settings.plugin.item', key: 'code-index' },\n CodeIndexSettingsCard as unknown as (props: never) => ReactNode,\n )\n })\n })\n}\n"],"mappings":"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","names":[]}
|