dsh-code-index 0.6.1 → 0.8.0
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 +32 -0
- package/README.md +55 -6
- package/README.zh.md +35 -6
- package/dist/client.js +76 -20
- package/dist/client.js.map +1 -1
- package/dist/index.js +802 -107
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
- package/scripts/release-smoke.mjs +67 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to dsh-code-index are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.8.0 — 2026-09-23
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Project-aware `RepoContextManager` keyed by canonical worktree root, with bounded inactive-context eviction and per-project index/cache isolation.
|
|
9
|
+
- External filesystem observation for active projects, debounced event batching, and dirty-file incremental reparsing for add/change/delete/rename events.
|
|
10
|
+
- Per-session dynamic repo-map selection through DSH system-prompt assembly context.
|
|
11
|
+
- Chinese and English dictionaries for the Web settings card.
|
|
12
|
+
|
|
13
|
+
### Improved
|
|
14
|
+
- `code_context`, `code_change_context`, and existing tools resolve the current session cwd and perform a freshness scan before returning context.
|
|
15
|
+
- Repo roots are canonicalized through the filesystem; Git worktrees keep separate roots and change state.
|
|
16
|
+
- Plugin disposal closes watchers, timers, settings observers, and in-flight context ownership.
|
|
17
|
+
- Development API target is `@deepseek-ai/dsh-tools@0.1.7-alpha.2`; package smoke retains the existing full tool surface by default.
|
|
18
|
+
|
|
19
|
+
### Release verification
|
|
20
|
+
- Real DSH Web verification passed: separate repo-A/repo-B contexts, A→B→A isolation, external add/delete freshness without manual rebuild, `code_change_context`, and call-relationship results; no cross-project contamination was observed.
|
|
21
|
+
- Settings-card Chinese and English localization is adapted to the current DSH UI. Thanks to @garyschulte for the related localization contribution in [PR #1](https://github.com/lemonxiny55/dsh-code-index/pull/1).
|
|
22
|
+
|
|
23
|
+
### Compatibility
|
|
24
|
+
- Existing v0.7 tools remain available; `toolSurface: full` remains the default.
|
|
25
|
+
- `excludeDirs` continues to mean directory names matched as exact path components; glob patterns are not accepted.
|
|
26
|
+
- No embeddings, remote services, or API keys are used.
|
|
27
|
+
|
|
28
|
+
## 0.7.0 — 2026-09-19
|
|
29
|
+
|
|
30
|
+
- **feat(context):** add deterministic task-aware `code_context` routing for change, symbol, architecture, test, exploration, and ambiguous tasks.
|
|
31
|
+
- **feat(context):** unify existing search, repo-map, call/import graph, change-aware context, affected tests, provenance, and structural signals behind one ranked context package.
|
|
32
|
+
- **feat(context):** add hard character budgeting, deterministic ordering, candidate deduplication, and low-priority trimming.
|
|
33
|
+
- **feat(surface):** add optional `toolSurface: compact` experiment while keeping the full v0.6 tool surface as the default.
|
|
34
|
+
- **bench:** add the v0.7 treatment arm to the context-economy benchmark without inventing model or token results.
|
|
35
|
+
- **compatibility:** retain all v0.6 tools and their existing argument/output behavior.
|
|
36
|
+
|
|
5
37
|
## 0.6.1 — 2026-09-18
|
|
6
38
|
|
|
7
39
|
- **fix(change-context):** map pure renames to both current and baseline symbols instead of returning only a warning.
|
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
English | [中文](README.zh.md)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Project-aware live structural context engine for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`). It is **task-aware**, **project-aware**, and **live**: local tree-sitter indexing, no API key, and no external service.
|
|
9
9
|
|
|
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 **
|
|
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 **local-first** — in-process tree-sitter over WASM and local file observation, with no remote service, embedding API, or vector database.
|
|
11
11
|
|
|
12
12
|
## What the model gets
|
|
13
13
|
|
|
@@ -19,14 +19,28 @@ Fits a niche the ecosystem took a while to fill: alongside git/voice/browser/mem
|
|
|
19
19
|
| `code_map` | Bounded ranked repo map (top files by symbol density + import-graph PageRank, key symbols + lines) |
|
|
20
20
|
| `code_refs` | Trace a symbol through the call graph: callers (who calls it) and callees (what it calls), resolved to file:line |
|
|
21
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_context` | Task-aware unified entry point: routes a plain-language task across search, repo map, call/import graph, change context, tests, and a hard character budget |
|
|
22
23
|
| `code_health` | Opt-in (`codeHealth: true`): circular dependencies (import cycles) and orphan modules |
|
|
23
24
|
|
|
24
|
-
Plus an optional **auto-injected system prompt section** (`code-index:repo-map`, order 60): a compact ranked map
|
|
25
|
+
Plus an optional **auto-injected system prompt section** (`code-index:repo-map`, order 60): a compact ranked map selected from the active DSH session workspace. Set `autoInject: false` to disable and rely on the `code_map` tool only.
|
|
26
|
+
|
|
27
|
+
## Project-aware live context
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
Open repo A → code_context uses A
|
|
31
|
+
Switch to repo B → code_context automatically uses B
|
|
32
|
+
Edit repo B from VS Code → the next code_context sees the new state
|
|
33
|
+
Switch back to repo A → A's symbols and Git changes remain isolated
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Each Git worktree gets its own index and change state. The plugin keeps contexts for up to four recently used projects and watches only projects a session has accessed. Filesystem events are coalesced; tool calls also scan current file metadata and reparse changed files before returning context.
|
|
25
37
|
|
|
26
38
|
## Install
|
|
27
39
|
|
|
28
40
|
Requires `dsh` (any install path — npx, npm, or source) and Node ≥ 22.
|
|
29
41
|
|
|
42
|
+
The development compatibility target is `@deepseek-ai/dsh@0.1.7-alpha.2` / `@deepseek-ai/dsh-tools@0.1.7-alpha.2` (Node 22 and 24 CI matrix). The harness is still a preview API; confirm the actual Web install and lifecycle against the target DSH build before release.
|
|
43
|
+
|
|
30
44
|
```sh
|
|
31
45
|
# from npm (prebuilt)
|
|
32
46
|
npx @deepseek-ai/dsh plugin --profile web add dsh-code-index
|
|
@@ -54,6 +68,7 @@ In a workspace session, ask the agent:
|
|
|
54
68
|
- "List the exported symbols in src/core."
|
|
55
69
|
- "Rebuild the code index."
|
|
56
70
|
- "What changed in the working tree, who calls it, and which tests are likely affected?"
|
|
71
|
+
- "Fix duplicate configuration loading during startup." (the router selects the smallest useful context automatically)
|
|
57
72
|
|
|
58
73
|
No API key is needed to *index*; the model must of course be configured to call the tools.
|
|
59
74
|
|
|
@@ -89,6 +104,30 @@ The index builds lazily on first use; later calls are served from the on-disk ca
|
|
|
89
104
|
|
|
90
105
|
`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
106
|
|
|
107
|
+
### Task-aware context
|
|
108
|
+
|
|
109
|
+
`code_context` is the high-level entry point for agents that have a task rather than a symbol query. The deterministic router recognizes change, symbol, architecture, test, exploration, and ambiguous tasks, then ranks existing primitives into one bounded package:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
code_context { task: "Fix duplicate configuration loading during startup" }
|
|
113
|
+
|
|
114
|
+
Task context — change
|
|
115
|
+
Primary symbols:
|
|
116
|
+
- [exact] function loadConfig() — src/config.ts:42
|
|
117
|
+
Relevant files:
|
|
118
|
+
- src/config.ts (current change)
|
|
119
|
+
- src/bootstrap.ts (entry path)
|
|
120
|
+
Relationships:
|
|
121
|
+
- bootstrap → loadConfig at src/bootstrap.ts:18 [exact; explicit-import-binding]
|
|
122
|
+
Current changes:
|
|
123
|
+
- modified function loadConfig() src/config.ts:42 [exact]
|
|
124
|
+
Likely affected tests:
|
|
125
|
+
- tests/config.spec.ts
|
|
126
|
+
Budget: 3720 / 5000 chars
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The `budgetChars`, `maxFiles`, and `maxSymbols` arguments are optional. `code_context` does not call an external model or API; `exact`, `import-scoped`, and `name-only` provenance remains explicit for inferred relationships.
|
|
130
|
+
|
|
92
131
|
## Configuration
|
|
93
132
|
|
|
94
133
|
Options are passed as the plugin row's `config` in the profile patch (or defaults are used if absent):
|
|
@@ -101,16 +140,20 @@ Options are passed as the plugin row's `config` in the profile patch (or default
|
|
|
101
140
|
mapTopFiles: 30
|
|
102
141
|
mapMaxChars: 4000
|
|
103
142
|
autoInject: true
|
|
143
|
+
toolSurface: full
|
|
104
144
|
```
|
|
105
145
|
|
|
106
146
|
| Key | Default | Meaning |
|
|
107
147
|
|---|---|---|
|
|
108
|
-
| `excludeDirs` | `[]` | Extra
|
|
148
|
+
| `excludeDirs` | `[]` | Extra directory names appended to built-in excludes. Matching is by exact path component; glob patterns such as `secrets/**` are not supported. |
|
|
109
149
|
| `mapTopFiles` | `24` | Max files in a ranked map |
|
|
110
150
|
| `mapMaxChars` | `3200` | Hard cap on rendered map characters |
|
|
111
151
|
| `mapTtlMs` | `60000` | Refresh interval for the auto-injected map (ms, min 1000) |
|
|
112
152
|
| `autoInject` | `true` | Register the system prompt section |
|
|
113
153
|
| `codeHealth` | `false` | Register the `code_health` tool (cycles / orphan modules) |
|
|
154
|
+
| `toolSurface` | `full` | Experimental `compact` mode exposes `code_index`, `code_context`, and enabled `code_health`; `full` preserves all tools |
|
|
155
|
+
| `externalWatch` | `true` | Watch source files in active project contexts for external edits |
|
|
156
|
+
| `watchDebounceMs` | `120` | Coalesce watcher events before refreshing affected files (minimum 20 ms) |
|
|
114
157
|
|
|
115
158
|
## Supported languages
|
|
116
159
|
|
|
@@ -123,13 +166,19 @@ TypeScript, JavaScript, Python, Go, Rust, Java, C++ and C (`.ts .tsx .mts .cts .
|
|
|
123
166
|
- **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
167
|
- **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
168
|
- **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.
|
|
169
|
+
- **Task-aware context** (`src/context.ts`): deterministically routes a task across the existing search, map, call graph, change context, and test signals, then deduplicates and trims them to a hard character budget.
|
|
126
170
|
- **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).
|
|
127
171
|
- **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).
|
|
172
|
+
- **Project contexts** (`src/repo-context.ts`): canonical real paths identify separate worktrees; up to four contexts are retained. Each tool call scans current metadata and reparses only changed files, while the watcher refreshes dirty files after a bounded debounce.
|
|
173
|
+
- **Ignore handling**: Git ignore rules from root and nested `.gitignore` files are applied to indexing. `excludeDirs` remains a list of exact directory names, not glob patterns.
|
|
128
174
|
|
|
129
175
|
## Known limitations
|
|
130
176
|
|
|
131
177
|
- **web-tree-sitter pinned to `^0.25` (ESM)** — the 0.25 line uses ESM named exports (`Language`/`Query`); this pairing with `tree-sitter-wasms` static builds is verified working under Node ≥ 22/24.
|
|
132
|
-
- Auto-injected
|
|
178
|
+
- Auto-injected maps use DSH's system-prompt assembly context for the active session. Agentless assembly falls back to the DSH process working directory. A newly accessed project may have an empty map on its first prompt while indexing completes; following assemblies receive its map.
|
|
179
|
+
- The watcher starts only for projects accessed by a tool and is disposed with the plugin. With `externalWatch: false`, per-call metadata scans still detect ordinary mtime changes.
|
|
180
|
+
- The preview DSH API is typechecked and exercised through package-level lifecycle fakes; a real DSH Web install/enable/disable run is a separate release gate.
|
|
181
|
+
- Large monorepos still require a directory metadata scan on each tool call. File parsing is incremental, but scan latency depends on repository size and storage speed.
|
|
133
182
|
- Local variables are indexed too — recall over precision; `code_search` ranking keeps them low.
|
|
134
183
|
- Developer-preview harness: expect breaking harness/plugin API changes upstream.
|
|
135
184
|
|
|
@@ -143,7 +192,7 @@ pnpm build # tsup → dist/index.js (ESM, external deps)
|
|
|
143
192
|
pnpm build && pnpm release:smoke # pack, clean-install the tarball, boot the plugin, and exercise core tools
|
|
144
193
|
```
|
|
145
194
|
|
|
146
|
-
The benchmark harness under `bench/` compares stock DSH, the published 0.5 baseline, and the
|
|
195
|
+
The benchmark harness under `bench/` compares stock DSH, the published 0.5 baseline, the v0.6 change-aware treatment, and the v0.7 task-aware 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
196
|
|
|
148
197
|
**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:
|
|
149
198
|
|
package/README.zh.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)
|
|
5
|
+
面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`) 的项目感知实时结构化上下文引擎。它具备**任务感知、项目感知、实时更新**能力:本地 tree-sitter 索引,无需 API key 或外部服务。
|
|
6
6
|
|
|
7
|
-
在一个生态姗姗来迟的细分领域占位:git/语音/浏览器/记忆类插件之外,代码智能方向的插件已陆续出现(图谱路线、向量嵌入路线)
|
|
7
|
+
在一个生态姗姗来迟的细分领域占位:git/语音/浏览器/记忆类插件之外,代码智能方向的插件已陆续出现(图谱路线、向量嵌入路线),而本插件刻意保持**本地优先**——进程内 tree-sitter WASM 与本地文件观察,不连接远程服务、embedding API 或向量数据库。
|
|
8
8
|
|
|
9
9
|
## 模型能得到什么
|
|
10
10
|
|
|
@@ -16,14 +16,28 @@
|
|
|
16
16
|
| `code_map` | 限量排名仓库地图(按符号密度 + import 图 PageRank 取核心文件 + 关键符号与行号) |
|
|
17
17
|
| `code_refs` | 沿调用图追踪符号:callers(谁调用了它)与 callees(它调用了谁),解析到 file:line |
|
|
18
18
|
| `code_change_context` | 从工作区或显式 diff 出发,返回变更符号、调用者、import 依赖、有限影响路径和可能受影响的测试 |
|
|
19
|
+
| `code_context` | 任务感知的统一入口:根据自然语言任务组合搜索、仓库地图、调用/import 图、变更上下文、测试和字符预算 |
|
|
19
20
|
| `code_health` | 可选开启(`codeHealth: true`):环依赖(import 环)与孤儿模块 |
|
|
20
21
|
|
|
21
|
-
外加一个可选的**自动注入系统提示词段**(`code-index:repo-map`,序 60)
|
|
22
|
+
外加一个可选的**自动注入系统提示词段**(`code-index:repo-map`,序 60):自动选择当前 DSH 会话工作区的精简排名地图。将 `autoInject: false` 可关闭,只依赖 `code_map` 工具。
|
|
23
|
+
|
|
24
|
+
## 项目感知实时上下文
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
打开仓库 A → code_context 使用 A
|
|
28
|
+
切换到仓库 B → code_context 自动改用 B
|
|
29
|
+
从 VS Code 修改仓库 B → 下一次 code_context 看到最新状态
|
|
30
|
+
切回仓库 A → A 的符号与 Git 变更仍然独立
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
每个 Git worktree 都有独立索引和变更状态。插件最多保留最近使用的四个项目上下文,只监听会话访问过的项目。文件事件会合并处理;工具调用也会扫描当前文件元数据,并在返回上下文前重解析已变更文件。
|
|
22
34
|
|
|
23
35
|
## 安装
|
|
24
36
|
|
|
25
37
|
需要 `dsh`(任意安装方式——npx、npm 或源码)与 Node ≥ 22。
|
|
26
38
|
|
|
39
|
+
当前开发兼容目标为 `@deepseek-ai/dsh@0.1.7-alpha.2` / `@deepseek-ai/dsh-tools@0.1.7-alpha.2`(CI 覆盖 Node 22 和 24)。Harness 仍属于预览 API;发布前还需在对应 DSH 构建中确认真实 Web 安装与插件生命周期。
|
|
40
|
+
|
|
27
41
|
```sh
|
|
28
42
|
# 从 npm(预编译)
|
|
29
43
|
npx @deepseek-ai/dsh plugin --profile web add dsh-code-index
|
|
@@ -51,6 +65,7 @@ npx @deepseek-ai/dsh plugin --profile web add ./dsh-code-index
|
|
|
51
65
|
- "列出 src/core 里的导出符号。"
|
|
52
66
|
- "重建代码索引。"
|
|
53
67
|
- "工作区改了什么、谁调用了它、哪些测试可能受影响?"
|
|
68
|
+
- "修复启动时重复加载配置。"(路由器会自动选择最有用的最小上下文)
|
|
54
69
|
|
|
55
70
|
*索引*本身不需要 API key;模型当然要配置好才能调用这些工具。
|
|
56
71
|
|
|
@@ -86,6 +101,10 @@ export function extractSymbols(code, id) — src/extract.ts:121
|
|
|
86
101
|
|
|
87
102
|
`code_change_context` 默认分析相对于 `HEAD` 的当前 Git 工作区,也支持内联 unified diff、仓库相对 `files` 或稳定的 `symbols` ID。结果有数量和字符预算,每条推导关系都会标注 `exact`、`import-scoped` 或 `name-only` 来源。删除和重命名在可用时读取 baseline;工作区模式也会包含未被忽略的未跟踪源码文件。
|
|
88
103
|
|
|
104
|
+
### 任务感知上下文
|
|
105
|
+
|
|
106
|
+
`code_context` 是 Agent 面对“代码任务”而不是单个符号时的高级入口。确定性的路由器识别修改、符号、架构、测试、探索和模糊任务,然后把已有能力合并、排序、去重并限制在字符预算内。只传 `task` 即可,也可以覆盖 `budgetChars`、`maxFiles`、`maxSymbols`。它不调用外部模型/API,推导关系的 `exact`、`import-scoped`、`name-only` provenance 会继续保留。
|
|
107
|
+
|
|
89
108
|
## 配置
|
|
90
109
|
|
|
91
110
|
选项通过插件行的 `config` 在 profile 补丁中传入(缺省时使用默认值):
|
|
@@ -98,16 +117,20 @@ export function extractSymbols(code, id) — src/extract.ts:121
|
|
|
98
117
|
mapTopFiles: 30
|
|
99
118
|
mapMaxChars: 4000
|
|
100
119
|
autoInject: true
|
|
120
|
+
toolSurface: full
|
|
101
121
|
```
|
|
102
122
|
|
|
103
123
|
| 键 | 默认 | 含义 |
|
|
104
124
|
|---|---|---|
|
|
105
|
-
| `excludeDirs` | `[]` |
|
|
125
|
+
| `excludeDirs` | `[]` | 追加到内置排除列表的目录名,按路径组件精确匹配;不支持 `secrets/**` 这类 glob |
|
|
106
126
|
| `mapTopFiles` | `24` | 排名地图中的最大文件数 |
|
|
107
127
|
| `mapMaxChars` | `3200` | 渲染地图的硬性字符上限 |
|
|
108
128
|
| `mapTtlMs` | `60000` | 自动注入地图的刷新间隔(毫秒,最小 1000) |
|
|
109
129
|
| `autoInject` | `true` | 是否注册系统提示词段 |
|
|
110
130
|
| `codeHealth` | `false` | 是否注册 `code_health` 工具(环/孤儿模块) |
|
|
131
|
+
| `toolSurface` | `full` | 实验性的 `compact` 模式只暴露 `code_index`、`code_context` 和已启用的 `code_health`; `full` 保持全部工具 |
|
|
132
|
+
| `externalWatch` | `true` | 监听当前活动项目的外部源码变更 |
|
|
133
|
+
| `watchDebounceMs` | `120` | 合并文件事件后再刷新受影响文件(最小 20 毫秒) |
|
|
111
134
|
|
|
112
135
|
## 支持的语言
|
|
113
136
|
|
|
@@ -120,13 +143,19 @@ TypeScript、JavaScript、Python、Go、Rust、Java、C++、C(`.ts .tsx .mts .ct
|
|
|
120
143
|
- **仓库地图**(`src/repomap.ts`):import 图上的个性化 PageRank(传送向量 = 各文件密度份额,被其他枢纽文件引用的枢纽会比平铺入度统计排得更靠前),以密度感知的文件打分为底(class/interface/function 加权,测试路径衰减),取 Top-N 文件,每文件符号上限,硬截断。
|
|
121
144
|
- **调用图**(`src/refgraph.ts`):逐文件提取调用点(按语言,并记录其所属函数),按名称解析成 callers 与 callees——`code_refs` 直接暴露,`code_search` 也把调用热度作为排名平局裁决。
|
|
122
145
|
- **变更上下文**(`src/change-context.ts`):把 Git hunk 映射到稳定符号,再在有限深度内追踪带来源标签的调用者、import 依赖、入口路径、影响范围和可能受影响的测试,避免返回整个仓库。
|
|
146
|
+
- **任务感知上下文**(`src/context.ts`):确定性地把任务路由到已有的搜索、地图、调用图、变更上下文和测试信号,再按统一优先级去重并限制字符预算。
|
|
123
147
|
- **健康检查**(`src/health.ts`):对 import 图跑 Tarjan SCC 得到环依赖;孤儿模块检测列出既不被 import、也不 import 任何文件的含符号文件(排除入口与测试)。
|
|
124
148
|
- **工作区解析**:每个工具解析会话 cwd(`agent.session.header.cwd`)并向上查找最近的 `.git`(有界——没有仓库标记的目录绝不会被索引)。
|
|
149
|
+
- **项目上下文**(`src/repo-context.ts`):用真实规范路径区分各 worktree,最多保留四个上下文。每次工具调用扫描文件元数据并只重解析变化文件;watcher 会在有限 debounce 后刷新脏文件。
|
|
150
|
+
- **忽略规则**:索引遵守仓库根目录及嵌套 `.gitignore`;`excludeDirs` 仍是精确目录名列表,不支持 glob。
|
|
125
151
|
|
|
126
152
|
## 已知限制
|
|
127
153
|
|
|
128
154
|
- **web-tree-sitter 固定为 `^0.25`(ESM)** —— 0.25 采用 ESM 具名导出(`Language`/`Query`);与 `tree-sitter-wasms` 静态构建的组合在 Node ≥ 22/24 下验证可用。
|
|
129
|
-
-
|
|
155
|
+
- 自动注入地图使用 DSH system prompt assembly 提供的当前会话上下文。没有 Agent 的 prompt assembly 会回退到 DSH 进程工作目录。新访问的项目第一次注入可能暂时为空,待索引完成后后续 assembly 会使用项目地图。
|
|
156
|
+
- Watcher 只为工具实际访问的项目启动,并随插件卸载清理。设置 `externalWatch: false` 后,每次工具调用仍会扫描元数据以发现常规 mtime 变化。
|
|
157
|
+
- 大型 monorepo 每次工具调用仍需要扫描目录元数据。文件解析是增量的,但扫描耗时取决于仓库规模和磁盘速度。
|
|
158
|
+
- 当前已通过预览版 DSH API 的类型检查、包级生命周期模拟;真实 DSH Web 安装/启停仍是发布前验证项。
|
|
130
159
|
- 局部变量也会被索引——召回优先于精确;`code_search` 的排名会压低它们。
|
|
131
160
|
- 开发者预览版 harness:上游 harness/插件 API 大概率有破坏性变更。
|
|
132
161
|
|
|
@@ -140,7 +169,7 @@ pnpm build # tsup → dist/index.js(ESM,外部依赖)
|
|
|
140
169
|
pnpm build && pnpm release:smoke # 打包、干净安装 tarball、启动插件并调用核心工具
|
|
141
170
|
```
|
|
142
171
|
|
|
143
|
-
`bench/` 下的 benchmark 对比 stock DSH、已发布的 0.5
|
|
172
|
+
`bench/` 下的 benchmark 对比 stock DSH、已发布的 0.5 基线、0.6 变更感知处理组和 0.7 任务感知处理组,记录任务完成、输入 token、工具调用、轮次和墙钟时间; provider 未提供的 usage 会保留为 null,不会伪造性能数据。当前仓库提供基础设施和示例任务,不宣称已测得性能提升。
|
|
144
173
|
|
|
145
174
|
**WSL → Windows 检出**:从 WSL 对 `/mnt/c` 下的检出跑 `pnpm install`,会留下 Windows 侧 Node 无法穿透的 Linux 风格符号链接(`Cannot find package 'web-tree-sitter'`、`EACCES`)。无需重装,在 Windows 侧跑一次修复:
|
|
146
175
|
|
package/dist/client.js
CHANGED
|
@@ -22,6 +22,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
// src/client/index.tsx
|
|
23
23
|
var client_exports = {};
|
|
24
24
|
__export(client_exports, {
|
|
25
|
+
SETTINGS_EN: () => SETTINGS_EN,
|
|
26
|
+
SETTINGS_ZH: () => SETTINGS_ZH,
|
|
25
27
|
apply: () => apply,
|
|
26
28
|
inject: () => inject,
|
|
27
29
|
name: () => name
|
|
@@ -53,7 +55,7 @@ function parseArgs(raw) {
|
|
|
53
55
|
}
|
|
54
56
|
function summarizeArgs(args) {
|
|
55
57
|
const parts = [];
|
|
56
|
-
for (const key of ["query", "symbol", "action", "direction", "kind"]) {
|
|
58
|
+
for (const key of ["task", "query", "symbol", "action", "direction", "kind", "budgetChars"]) {
|
|
57
59
|
const value = args[key];
|
|
58
60
|
if (typeof value === "string" && value) parts.push(`${key}=${value}`);
|
|
59
61
|
}
|
|
@@ -104,7 +106,6 @@ function CodeToolCard(props) {
|
|
|
104
106
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { style: { margin: "4px 0 0", whiteSpace: "pre-wrap", maxHeight: 220, overflow: "auto" }, children: resultText(block) })
|
|
105
107
|
] });
|
|
106
108
|
}
|
|
107
|
-
var activeScope = null;
|
|
108
109
|
var CARD_CSS = `
|
|
109
110
|
.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
111
|
.dci-card:hover{border-color:var(--dsw-alias-label-dimmed)}
|
|
@@ -136,8 +137,37 @@ var CARD_CSS = `
|
|
|
136
137
|
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
138
|
var SETTINGS_DEFAULTS = { autoInject: true, codeHealth: false, mapTopFiles: 24 };
|
|
138
139
|
var SETTINGS_FIELDS = ["autoInject", "codeHealth", "mapTopFiles"];
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
var SETTINGS_EN = {
|
|
141
|
+
collapse: "Collapse settings",
|
|
142
|
+
expand: "Expand settings",
|
|
143
|
+
description: "Project-aware live structural context: symbol search, repo map, and call graph.",
|
|
144
|
+
autoInject: "Inject repo map",
|
|
145
|
+
autoInjectHint: "Add a compact ranked map of the current project to the system prompt.",
|
|
146
|
+
codeHealth: "Code health tool",
|
|
147
|
+
codeHealthHint: "Register code_health for dependency cycles and orphan modules (restart required).",
|
|
148
|
+
mapFiles: "Map files",
|
|
149
|
+
mapFilesHint: "Maximum number of files shown in each repo map.",
|
|
150
|
+
discard: "Discard changes",
|
|
151
|
+
save: "Save",
|
|
152
|
+
unavailable: "Settings service unavailable."
|
|
153
|
+
};
|
|
154
|
+
var SETTINGS_ZH = {
|
|
155
|
+
collapse: "\u6536\u8D77\u8BBE\u7F6E",
|
|
156
|
+
expand: "\u5C55\u5F00\u8BBE\u7F6E",
|
|
157
|
+
description: "\u9879\u76EE\u611F\u77E5\u7684\u5B9E\u65F6\u7ED3\u6784\u5316\u4E0A\u4E0B\u6587\uFF1A\u7B26\u53F7\u641C\u7D22\u3001\u4ED3\u5E93\u5730\u56FE\u4E0E\u8C03\u7528\u56FE\u3002",
|
|
158
|
+
autoInject: "\u81EA\u52A8\u6CE8\u5165\u4ED3\u5E93\u5730\u56FE",
|
|
159
|
+
autoInjectHint: "\u628A\u5F53\u524D\u9879\u76EE\u7684\u7CBE\u7B80\u6392\u540D\u5730\u56FE\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3002",
|
|
160
|
+
codeHealth: "\u4EE3\u7801\u5065\u5EB7\u5DE5\u5177",
|
|
161
|
+
codeHealthHint: "\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",
|
|
162
|
+
mapFiles: "\u5730\u56FE\u6587\u4EF6\u6570",
|
|
163
|
+
mapFilesHint: "\u4ED3\u5E93\u5730\u56FE\u4E2D\u6700\u591A\u5C55\u793A\u7684\u6587\u4EF6\u6570\u3002",
|
|
164
|
+
discard: "\u653E\u5F03\u4FEE\u6539",
|
|
165
|
+
save: "\u4FDD\u5B58",
|
|
166
|
+
unavailable: "\u8BBE\u7F6E\u670D\u52A1\u4E0D\u53EF\u7528\u3002"
|
|
167
|
+
};
|
|
168
|
+
function CodeIndexSettingsCard(props) {
|
|
169
|
+
const scope = props.scope ?? null;
|
|
170
|
+
const t = props.t ?? ((key) => SETTINGS_EN[key] ?? key);
|
|
141
171
|
const [open, setOpen] = (0, import_react.useState)(false);
|
|
142
172
|
const [snapshot, setSnapshot] = (0, import_react.useState)(
|
|
143
173
|
scope ? scope.getSnapshot() : null
|
|
@@ -175,14 +205,14 @@ function CodeIndexSettingsCard() {
|
|
|
175
205
|
type: "button",
|
|
176
206
|
className: "dci-header",
|
|
177
207
|
"aria-expanded": open,
|
|
178
|
-
"aria-label": `${open ? "
|
|
208
|
+
"aria-label": `${t(open ? "collapse" : "expand")}: dsh-code-index`,
|
|
179
209
|
onClick: () => {
|
|
180
210
|
setOpen(!open);
|
|
181
211
|
},
|
|
182
212
|
children: [
|
|
183
213
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dci-headText", children: [
|
|
184
214
|
/* @__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: "
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dci-description", children: t("description") })
|
|
186
216
|
] }),
|
|
187
217
|
/* @__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
218
|
]
|
|
@@ -191,7 +221,7 @@ function CodeIndexSettingsCard() {
|
|
|
191
221
|
open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-body", children: [
|
|
192
222
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
|
|
193
223
|
/* @__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: "
|
|
224
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-auto-inject", children: t("autoInject") }),
|
|
195
225
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
196
226
|
"input",
|
|
197
227
|
{
|
|
@@ -206,11 +236,11 @@ function CodeIndexSettingsCard() {
|
|
|
206
236
|
}
|
|
207
237
|
)
|
|
208
238
|
] }),
|
|
209
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: "
|
|
239
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: t("autoInjectHint") })
|
|
210
240
|
] }),
|
|
211
241
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
|
|
212
242
|
/* @__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: "
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-code-health", children: t("codeHealth") }),
|
|
214
244
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
215
245
|
"input",
|
|
216
246
|
{
|
|
@@ -225,10 +255,10 @@ function CodeIndexSettingsCard() {
|
|
|
225
255
|
}
|
|
226
256
|
)
|
|
227
257
|
] }),
|
|
228
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: "
|
|
258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: t("codeHealthHint") })
|
|
229
259
|
] }),
|
|
230
260
|
/* @__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: "
|
|
261
|
+
/* @__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: t("mapFiles") }) }),
|
|
232
262
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
233
263
|
"input",
|
|
234
264
|
{
|
|
@@ -243,16 +273,16 @@ function CodeIndexSettingsCard() {
|
|
|
243
273
|
}
|
|
244
274
|
}
|
|
245
275
|
),
|
|
246
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: "
|
|
276
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: t("mapFilesHint") })
|
|
247
277
|
] }),
|
|
248
278
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-footer", children: [
|
|
249
279
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dci-discard", disabled: !dirty || !writable, onClick: () => {
|
|
250
280
|
setDraft(null);
|
|
251
|
-
}, children: "
|
|
252
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dci-save", disabled: !dirty || !writable, onClick: save, children: "
|
|
281
|
+
}, children: t("discard") }),
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dci-save", disabled: !dirty || !writable, onClick: save, children: t("save") })
|
|
253
283
|
] })
|
|
254
284
|
] }) : null,
|
|
255
|
-
scope ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", style: { padding: "0 16px 12px" }, children: "
|
|
285
|
+
scope ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", style: { padding: "0 16px 12px" }, children: t("unavailable") })
|
|
256
286
|
] });
|
|
257
287
|
}
|
|
258
288
|
var name = "dsh-code-index";
|
|
@@ -264,20 +294,46 @@ var TOOLVIEW_KEYS = [
|
|
|
264
294
|
"code_map",
|
|
265
295
|
"code_refs",
|
|
266
296
|
"code_change_context",
|
|
297
|
+
"code_context",
|
|
267
298
|
"code_health"
|
|
268
299
|
];
|
|
269
300
|
function apply(ctx) {
|
|
270
301
|
const card = CodeToolCard;
|
|
302
|
+
ctx.inject?.(["locale"], (localized) => {
|
|
303
|
+
if (localized.locale) localized.effect(() => localized.locale.register("settings.codeIndex", { zh: SETTINGS_ZH, en: SETTINGS_EN }));
|
|
304
|
+
});
|
|
271
305
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
272
306
|
for (const key of TOOLVIEW_KEYS) {
|
|
273
307
|
yield ctx.slots.register({ name: "tool.call.toolview", key }, card);
|
|
274
308
|
}
|
|
275
309
|
});
|
|
276
|
-
ctx.inject(["
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
310
|
+
ctx.inject?.(["configForms"], (configured) => {
|
|
311
|
+
const form = configured.configForms?.get("dsh-code-index") ?? configured.configForms?.get("code-index");
|
|
312
|
+
if (!form) return;
|
|
313
|
+
configured.slots.inject("settings.plugins.tab", function* () {
|
|
314
|
+
yield configured.slots.register(
|
|
315
|
+
{
|
|
316
|
+
name: "settings.plugins.tab",
|
|
317
|
+
id: "code-index",
|
|
318
|
+
order: 60,
|
|
319
|
+
label: "Code Index",
|
|
320
|
+
locale: "settings.codeIndex",
|
|
321
|
+
inject: () => ({ scope: form })
|
|
322
|
+
},
|
|
323
|
+
CodeIndexSettingsCard
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
ctx.inject?.(["settingsScope"], (legacy) => {
|
|
328
|
+
const settingsScope = legacy.settingsScope?.bind({ namespace: "code-index" });
|
|
329
|
+
legacy.slots.inject("settings.plugin.item", function* () {
|
|
330
|
+
yield legacy.slots.register(
|
|
331
|
+
{
|
|
332
|
+
name: "settings.plugin.item",
|
|
333
|
+
key: "code-index",
|
|
334
|
+
locale: "settings.codeIndex",
|
|
335
|
+
inject: () => ({ scope: settingsScope })
|
|
336
|
+
},
|
|
281
337
|
CodeIndexSettingsCard
|
|
282
338
|
);
|
|
283
339
|
});
|
package/dist/client.js.map
CHANGED
|
@@ -1 +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":[]}
|
|
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 effect(fn: () => (() => void) | void): void\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 configForms?: { get(id: string): SettingsScopeLike<CodeIndexSettings> | undefined }\n locale?: { register(namespace: string, dictionaries: { zh: Record<string, string>; en: Record<string, string> }): () => void }\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 toolSurface?: 'full' | 'compact'\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 ['task', 'query', 'symbol', 'action', 'direction', 'kind', 'budgetChars']) {\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/** 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. */\nexport const SETTINGS_EN = {\n collapse: 'Collapse settings', expand: 'Expand settings',\n description: 'Project-aware live structural context: symbol search, repo map, and call graph.',\n autoInject: 'Inject repo map', autoInjectHint: 'Add a compact ranked map of the current project to the system prompt.',\n codeHealth: 'Code health tool', codeHealthHint: 'Register code_health for dependency cycles and orphan modules (restart required).',\n mapFiles: 'Map files', mapFilesHint: 'Maximum number of files shown in each repo map.',\n discard: 'Discard changes', save: 'Save', unavailable: 'Settings service unavailable.',\n}\nexport const SETTINGS_ZH = {\n collapse: '收起设置', expand: '展开设置',\n description: '项目感知的实时结构化上下文:符号搜索、仓库地图与调用图。',\n autoInject: '自动注入仓库地图', autoInjectHint: '把当前项目的精简排名地图注入系统提示词。',\n codeHealth: '代码健康工具', codeHealthHint: '注册 code_health 工具,报告环依赖与孤儿模块(重启后生效)。',\n mapFiles: '地图文件数', mapFilesHint: '仓库地图中最多展示的文件数。',\n discard: '放弃修改', save: '保存', unavailable: '设置服务不可用。',\n}\n\nfunction CodeIndexSettingsCard(props: { scope?: SettingsScopeLike<CodeIndexSettings>; t?: (key: string) => string }): ReactNode {\n const scope = props.scope ?? null\n const t = props.t ?? ((key: string) => SETTINGS_EN[key as keyof typeof SETTINGS_EN] ?? key)\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={`${t(open ? 'collapse' : 'expand')}: 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\">{t('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\">{t('autoInject')}</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\">{t('autoInjectHint')}</p>\n </div>\n <div className=\"dci-field\">\n <div className=\"dci-head\">\n <label className=\"dci-label\" htmlFor=\"dci-code-health\">{t('codeHealth')}</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\">{t('codeHealthHint')}</p>\n </div>\n <div className=\"dci-field\">\n <div className=\"dci-head\">\n <label className=\"dci-label\" htmlFor=\"dci-map-files\">{t('mapFiles')}</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\">{t('mapFilesHint')}</p>\n </div>\n <div className=\"dci-footer\">\n <button type=\"button\" className=\"dci-discard\" disabled={!dirty || !writable} onClick={() => { setDraft(null) }}>\n {t('discard')}\n </button>\n <button type=\"button\" className=\"dci-save\" disabled={!dirty || !writable} onClick={save}>{t('save')}</button>\n </div>\n </div>\n ) : null}\n {scope ? null : <p className=\"dci-hint\" style={{ padding: '0 16px 12px' }}>{t('unavailable')}</p>}\n </li>\n )\n}\n\nexport const name = 'dsh-code-index'\n\n/** `slots` is required; config and locale services are injected optionally. */\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_context',\n 'code_health',\n]\n\nexport function apply(ctx: ClientContextLike): void {\n const card = CodeToolCard as unknown as (props: never) => ReactNode\n ctx.inject?.(['locale'], (localized) => {\n if (localized.locale) localized.effect(() => localized.locale!.register('settings.codeIndex', { zh: SETTINGS_ZH, en: SETTINGS_EN }))\n })\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 // New DSH exposes per-plugin volatile config forms and the plugin settings\n // tab. Keep the prior settingsScope slot for clients that still provide it.\n ctx.inject?.(['configForms'], (configured) => {\n const form = configured.configForms?.get('dsh-code-index') ?? configured.configForms?.get('code-index')\n if (!form) return\n configured.slots.inject('settings.plugins.tab', function* () {\n yield configured.slots.register(\n {\n name: 'settings.plugins.tab',\n id: 'code-index',\n order: 60,\n label: 'Code Index',\n locale: 'settings.codeIndex',\n inject: () => ({ scope: form }),\n },\n CodeIndexSettingsCard as unknown as (props: never) => ReactNode,\n )\n })\n })\n ctx.inject?.(['settingsScope'], (legacy) => {\n const settingsScope = legacy.settingsScope?.bind({ namespace: 'code-index' })\n legacy.slots.inject('settings.plugin.item', function* () {\n yield legacy.slots.register(\n {\n name: 'settings.plugin.item',\n key: 'code-index',\n locale: 'settings.codeIndex',\n inject: () => ({ scope: settingsScope }),\n },\n CodeIndexSettingsCard as unknown as (props: never) => ReactNode,\n )\n })\n })\n}\n"],"mappings":"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","names":[]}
|