dsh-code-index 0.7.0 → 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 +23 -0
- package/README.md +25 -5
- package/README.zh.md +25 -5
- package/dist/client.js +74 -19
- package/dist/client.js.map +1 -1
- package/dist/index.js +333 -103
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
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
|
+
|
|
5
28
|
## 0.7.0 — 2026-09-19
|
|
6
29
|
|
|
7
30
|
- **feat(context):** add deterministic task-aware `code_context` routing for change, symbol, architecture, test, exploration, and ambiguous tasks.
|
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
|
|
|
@@ -22,12 +22,25 @@ Fits a niche the ecosystem took a while to fill: alongside git/voice/browser/mem
|
|
|
22
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 |
|
|
23
23
|
| `code_health` | Opt-in (`codeHealth: true`): circular dependencies (import cycles) and orphan modules |
|
|
24
24
|
|
|
25
|
-
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.
|
|
26
37
|
|
|
27
38
|
## Install
|
|
28
39
|
|
|
29
40
|
Requires `dsh` (any install path — npx, npm, or source) and Node ≥ 22.
|
|
30
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
|
+
|
|
31
44
|
```sh
|
|
32
45
|
# from npm (prebuilt)
|
|
33
46
|
npx @deepseek-ai/dsh plugin --profile web add dsh-code-index
|
|
@@ -132,13 +145,15 @@ Options are passed as the plugin row's `config` in the profile patch (or default
|
|
|
132
145
|
|
|
133
146
|
| Key | Default | Meaning |
|
|
134
147
|
|---|---|---|
|
|
135
|
-
| `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. |
|
|
136
149
|
| `mapTopFiles` | `24` | Max files in a ranked map |
|
|
137
150
|
| `mapMaxChars` | `3200` | Hard cap on rendered map characters |
|
|
138
151
|
| `mapTtlMs` | `60000` | Refresh interval for the auto-injected map (ms, min 1000) |
|
|
139
152
|
| `autoInject` | `true` | Register the system prompt section |
|
|
140
153
|
| `codeHealth` | `false` | Register the `code_health` tool (cycles / orphan modules) |
|
|
141
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) |
|
|
142
157
|
|
|
143
158
|
## Supported languages
|
|
144
159
|
|
|
@@ -154,11 +169,16 @@ TypeScript, JavaScript, Python, Go, Rust, Java, C++ and C (`.ts .tsx .mts .cts .
|
|
|
154
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.
|
|
155
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).
|
|
156
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.
|
|
157
174
|
|
|
158
175
|
## Known limitations
|
|
159
176
|
|
|
160
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.
|
|
161
|
-
- 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.
|
|
162
182
|
- Local variables are indexed too — recall over precision; `code_search` ranking keeps them low.
|
|
163
183
|
- Developer-preview harness: expect breaking harness/plugin API changes upstream.
|
|
164
184
|
|
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
|
|
|
@@ -19,12 +19,25 @@
|
|
|
19
19
|
| `code_context` | 任务感知的统一入口:根据自然语言任务组合搜索、仓库地图、调用/import 图、变更上下文、测试和字符预算 |
|
|
20
20
|
| `code_health` | 可选开启(`codeHealth: true`):环依赖(import 环)与孤儿模块 |
|
|
21
21
|
|
|
22
|
-
外加一个可选的**自动注入系统提示词段**(`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 都有独立索引和变更状态。插件最多保留最近使用的四个项目上下文,只监听会话访问过的项目。文件事件会合并处理;工具调用也会扫描当前文件元数据,并在返回上下文前重解析已变更文件。
|
|
23
34
|
|
|
24
35
|
## 安装
|
|
25
36
|
|
|
26
37
|
需要 `dsh`(任意安装方式——npx、npm 或源码)与 Node ≥ 22。
|
|
27
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
|
+
|
|
28
41
|
```sh
|
|
29
42
|
# 从 npm(预编译)
|
|
30
43
|
npx @deepseek-ai/dsh plugin --profile web add dsh-code-index
|
|
@@ -109,13 +122,15 @@ export function extractSymbols(code, id) — src/extract.ts:121
|
|
|
109
122
|
|
|
110
123
|
| 键 | 默认 | 含义 |
|
|
111
124
|
|---|---|---|
|
|
112
|
-
| `excludeDirs` | `[]` |
|
|
125
|
+
| `excludeDirs` | `[]` | 追加到内置排除列表的目录名,按路径组件精确匹配;不支持 `secrets/**` 这类 glob |
|
|
113
126
|
| `mapTopFiles` | `24` | 排名地图中的最大文件数 |
|
|
114
127
|
| `mapMaxChars` | `3200` | 渲染地图的硬性字符上限 |
|
|
115
128
|
| `mapTtlMs` | `60000` | 自动注入地图的刷新间隔(毫秒,最小 1000) |
|
|
116
129
|
| `autoInject` | `true` | 是否注册系统提示词段 |
|
|
117
130
|
| `codeHealth` | `false` | 是否注册 `code_health` 工具(环/孤儿模块) |
|
|
118
131
|
| `toolSurface` | `full` | 实验性的 `compact` 模式只暴露 `code_index`、`code_context` 和已启用的 `code_health`; `full` 保持全部工具 |
|
|
132
|
+
| `externalWatch` | `true` | 监听当前活动项目的外部源码变更 |
|
|
133
|
+
| `watchDebounceMs` | `120` | 合并文件事件后再刷新受影响文件(最小 20 毫秒) |
|
|
119
134
|
|
|
120
135
|
## 支持的语言
|
|
121
136
|
|
|
@@ -131,11 +146,16 @@ TypeScript、JavaScript、Python、Go、Rust、Java、C++、C(`.ts .tsx .mts .ct
|
|
|
131
146
|
- **任务感知上下文**(`src/context.ts`):确定性地把任务路由到已有的搜索、地图、调用图、变更上下文和测试信号,再按统一优先级去重并限制字符预算。
|
|
132
147
|
- **健康检查**(`src/health.ts`):对 import 图跑 Tarjan SCC 得到环依赖;孤儿模块检测列出既不被 import、也不 import 任何文件的含符号文件(排除入口与测试)。
|
|
133
148
|
- **工作区解析**:每个工具解析会话 cwd(`agent.session.header.cwd`)并向上查找最近的 `.git`(有界——没有仓库标记的目录绝不会被索引)。
|
|
149
|
+
- **项目上下文**(`src/repo-context.ts`):用真实规范路径区分各 worktree,最多保留四个上下文。每次工具调用扫描文件元数据并只重解析变化文件;watcher 会在有限 debounce 后刷新脏文件。
|
|
150
|
+
- **忽略规则**:索引遵守仓库根目录及嵌套 `.gitignore`;`excludeDirs` 仍是精确目录名列表,不支持 glob。
|
|
134
151
|
|
|
135
152
|
## 已知限制
|
|
136
153
|
|
|
137
154
|
- **web-tree-sitter 固定为 `^0.25`(ESM)** —— 0.25 采用 ESM 具名导出(`Language`/`Query`);与 `tree-sitter-wasms` 静态构建的组合在 Node ≥ 22/24 下验证可用。
|
|
138
|
-
-
|
|
155
|
+
- 自动注入地图使用 DSH system prompt assembly 提供的当前会话上下文。没有 Agent 的 prompt assembly 会回退到 DSH 进程工作目录。新访问的项目第一次注入可能暂时为空,待索引完成后后续 assembly 会使用项目地图。
|
|
156
|
+
- Watcher 只为工具实际访问的项目启动,并随插件卸载清理。设置 `externalWatch: false` 后,每次工具调用仍会扫描元数据以发现常规 mtime 变化。
|
|
157
|
+
- 大型 monorepo 每次工具调用仍需要扫描目录元数据。文件解析是增量的,但扫描耗时取决于仓库规模和磁盘速度。
|
|
158
|
+
- 当前已通过预览版 DSH API 的类型检查、包级生命周期模拟;真实 DSH Web 安装/启停仍是发布前验证项。
|
|
139
159
|
- 局部变量也会被索引——召回优先于精确;`code_search` 的排名会压低它们。
|
|
140
160
|
- 开发者预览版 harness:上游 harness/插件 API 大概率有破坏性变更。
|
|
141
161
|
|
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
|
|
@@ -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";
|
|
@@ -269,16 +299,41 @@ var TOOLVIEW_KEYS = [
|
|
|
269
299
|
];
|
|
270
300
|
function apply(ctx) {
|
|
271
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
|
+
});
|
|
272
305
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
273
306
|
for (const key of TOOLVIEW_KEYS) {
|
|
274
307
|
yield ctx.slots.register({ name: "tool.call.toolview", key }, card);
|
|
275
308
|
}
|
|
276
309
|
});
|
|
277
|
-
ctx.inject(["
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
+
},
|
|
282
337
|
CodeIndexSettingsCard
|
|
283
338
|
);
|
|
284
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 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/** 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_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":[]}
|